Skip to content

Commit 2afd32c

Browse files
committed
Bump version
1 parent 3ce2b51 commit 2afd32c

15 files changed

+390
-45
lines changed

CHANGELOG.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1+
## v0.0.97 (2025-12-30)
2+
3+
### Feat
4+
5+
- Enhance CLI Python prompt to display examples used for grounding
6+
- Add diagnostic logging for Issue #186 (C, E, T files)
7+
- Enhance cloud execution and syntax validation
8+
9+
### Refactor
10+
11+
- Update prompts to support existing test files as lists
12+
113
## v0.0.96 (2025-12-29)
214

315
### Feat
416

5-
- Enhance update command and improve path validation
17+
- **Cloud Example Generation:** Added cloud execution support to `context_generator_main` via new async `_run_cloud_generation()` function. Uses `CloudConfig.get_jwt_token()` for authentication and automatically falls back to local execution on cloud failure. Supports `--local` flag to bypass cloud.
18+
19+
- **Improved Syntax Repair:** Rewrote `_validate_and_fix_python_syntax()` with a binary search algorithm to find the longest valid Python prefix when LLM output contains trailing JSON garbage (e.g., `"explanation":`, `"filename":` metadata). Provides user feedback on repair success/failure.
20+
21+
### Refactor
22+
23+
- **Prompt Include Tag Updates:** Standardized `<include>` tags in `context_generator_main_python.prompt` and `preprocess_python.prompt` to use consistent naming conventions (e.g., `<context.module_name>` wrappers).
24+
25+
### Tests
26+
27+
- Updated `tests/test_context_generator_main.py` with coverage for cloud execution, local fallback, and syntax repair scenarios.
628

729
## v0.0.95 (2025-12-28)
830

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# PDD (Prompt-Driven Development) Command Line Interface
22

3-
![PDD-CLI Version](https://img.shields.io/badge/pdd--cli-v0.0.96-blue) [![Discord](https://img.shields.io/badge/Discord-join%20chat-7289DA.svg?logo=discord&logoColor=white)](https://discord.gg/Yp4RTh8bG7)
3+
![PDD-CLI Version](https://img.shields.io/badge/pdd--cli-v0.0.97-blue) [![Discord](https://img.shields.io/badge/Discord-join%20chat-7289DA.svg?logo=discord&logoColor=white)](https://discord.gg/Yp4RTh8bG7)
44

55
## Introduction
66

@@ -285,7 +285,7 @@ export PDD_TEST_OUTPUT_PATH=/path/to/tests/
285285

286286
## Version
287287

288-
Current version: 0.0.96
288+
Current version: 0.0.97
289289

290290
To check your installed version, run:
291291
```

examples/hello/gc.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def hello() -> None:
2+
"""
3+
Prints the greeting message "hello".
4+
5+
This function outputs the string "hello" to the console and returns nothing.
6+
7+
Example:
8+
>>> hello()
9+
hello
10+
"""
11+
print("hello")

examples/hello/repo_root

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Subproject commit c8b42bb616421377b3eb193a4601329e1266d9a1
1+
Subproject commit 5547071b64416d2f003789cd163fc105105e5141

pdd/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""PDD - Prompt Driven Development"""
22

3-
__version__ = "0.0.96"
3+
__version__ = "0.0.97"
44

55
# Strength parameter used for LLM extraction across the codebase
66
# Used in postprocessing, XML tagging, code generation, and other extraction

pdd/agentic_common.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,65 @@ def log_debug(message: str, *, verbose: bool, quiet: bool, label: str = "") -> N
9595
console.log(f"{prefix}{message}")
9696

9797

98+
def _detect_suspicious_files(cwd: Path, context: str, *, verbose: bool, quiet: bool, label: str = "") -> None:
99+
"""
100+
Detect suspicious single-character files (like C, E, T) in a directory.
101+
102+
Issue #186: Empty files named C, E, T (first letters of Code, Example, Test)
103+
have been appearing during agentic operations. This logs them for diagnosis.
104+
"""
105+
import datetime
106+
import traceback
107+
108+
suspicious: List[Path] = []
109+
try:
110+
for f in cwd.iterdir():
111+
if f.is_file() and len(f.name) <= 2 and not f.name.startswith('.'):
112+
suspicious.append(f)
113+
except Exception:
114+
return
115+
116+
if not suspicious:
117+
return
118+
119+
timestamp = datetime.datetime.now().isoformat()
120+
prefix = _format_label(label)
121+
console.print(f"[bold red]{prefix}⚠️ SUSPICIOUS FILES DETECTED (Issue #186)[/bold red]")
122+
console.print(f"[red]{prefix}Timestamp: {timestamp}[/red]")
123+
console.print(f"[red]{prefix}Context: {context}[/red]")
124+
console.print(f"[red]{prefix}Directory: {cwd}[/red]")
125+
for sf in suspicious:
126+
try:
127+
size = sf.stat().st_size
128+
console.print(f"[red]{prefix} - {sf.name} (size: {size} bytes)[/red]")
129+
except Exception:
130+
console.print(f"[red]{prefix} - {sf.name} (could not stat)[/red]")
131+
132+
# Also log to a file for persistence
133+
log_file = Path.home() / ".pdd" / "suspicious_files.log"
134+
try:
135+
log_file.parent.mkdir(parents=True, exist_ok=True)
136+
with open(log_file, "a") as lf:
137+
lf.write(f"\n{'='*60}\n")
138+
lf.write(f"Timestamp: {timestamp}\n")
139+
lf.write(f"Context: {context}\n")
140+
lf.write(f"Directory: {cwd}\n")
141+
lf.write(f"CWD at detection: {Path.cwd()}\n")
142+
lf.write(f"Label: {label}\n")
143+
for sf in suspicious:
144+
try:
145+
size = sf.stat().st_size
146+
lf.write(f" - {sf.name} (size: {size} bytes)\n")
147+
except Exception as e:
148+
lf.write(f" - {sf.name} (error: {e})\n")
149+
lf.write("Stack trace:\n")
150+
for line in traceback.format_stack()[-10:]:
151+
lf.write(line)
152+
lf.write("\n")
153+
except Exception:
154+
pass # Best-effort logging
155+
156+
98157
def log_error(message: str, *, verbose: bool, quiet: bool, label: str = "") -> None:
99158
"""
100159
Log an error message.
@@ -861,4 +920,23 @@ def run_agentic_task(
861920
prompt_file.unlink()
862921
except OSError:
863922
# Best-effort cleanup; ignore errors.
864-
pass
923+
pass
924+
925+
# Issue #186: Scan for suspicious files after agentic task
926+
_detect_suspicious_files(
927+
cwd,
928+
f"After run_agentic_task",
929+
verbose=verbose,
930+
quiet=quiet,
931+
label=label,
932+
)
933+
# Also scan project root if different from cwd
934+
project_root = Path.cwd()
935+
if project_root != cwd:
936+
_detect_suspicious_files(
937+
project_root,
938+
f"After run_agentic_task - project root",
939+
verbose=verbose,
940+
quiet=quiet,
941+
label=label,
942+
)

pdd/agentic_fix.py

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import re
55
import shutil
66
import subprocess
7+
import sys
78
import difflib
89
import tempfile
910
from pathlib import Path
@@ -56,6 +57,68 @@ def _verbose(msg: str) -> None:
5657
if _IS_VERBOSE:
5758
console.print(msg)
5859

60+
61+
def _detect_suspicious_files(cwd: Path, context: str = "") -> List[Path]:
62+
"""
63+
Detect suspicious single-character files (like C, E, T) in a directory.
64+
65+
This is a diagnostic function to help identify when/where these files are created.
66+
Issue #186: Empty files named C, E, T (first letters of Code, Example, Test)
67+
have been appearing during agentic operations.
68+
69+
Args:
70+
cwd: Directory to scan
71+
context: Description of what operation just ran (for logging)
72+
73+
Returns:
74+
List of suspicious file paths found
75+
"""
76+
suspicious: List[Path] = []
77+
try:
78+
for f in cwd.iterdir():
79+
if f.is_file() and len(f.name) <= 2 and not f.name.startswith('.'):
80+
suspicious.append(f)
81+
82+
if suspicious:
83+
import datetime
84+
timestamp = datetime.datetime.now().isoformat()
85+
_always(f"[bold red]⚠️ SUSPICIOUS FILES DETECTED (Issue #186)[/bold red]")
86+
_always(f"[red]Timestamp: {timestamp}[/red]")
87+
_always(f"[red]Context: {context}[/red]")
88+
_always(f"[red]Directory: {cwd}[/red]")
89+
for sf in suspicious:
90+
try:
91+
size = sf.stat().st_size
92+
_always(f"[red] - {sf.name} (size: {size} bytes)[/red]")
93+
except Exception:
94+
_always(f"[red] - {sf.name} (could not stat)[/red]")
95+
96+
# Also log to a file for persistence
97+
log_file = Path.home() / ".pdd" / "suspicious_files.log"
98+
log_file.parent.mkdir(parents=True, exist_ok=True)
99+
with open(log_file, "a") as lf:
100+
lf.write(f"\n{'='*60}\n")
101+
lf.write(f"Timestamp: {timestamp}\n")
102+
lf.write(f"Context: {context}\n")
103+
lf.write(f"Directory: {cwd}\n")
104+
lf.write(f"CWD at detection: {Path.cwd()}\n")
105+
for sf in suspicious:
106+
try:
107+
size = sf.stat().st_size
108+
lf.write(f" - {sf.name} (size: {size} bytes)\n")
109+
except Exception as e:
110+
lf.write(f" - {sf.name} (error: {e})\n")
111+
# Log stack trace to help identify caller
112+
import traceback
113+
lf.write("Stack trace:\n")
114+
lf.write(traceback.format_stack()[-10:][0] if traceback.format_stack() else "N/A")
115+
lf.write("\n")
116+
except Exception as e:
117+
_verbose(f"[yellow]Could not scan for suspicious files: {e}[/yellow]")
118+
119+
return suspicious
120+
121+
59122
def _begin_marker(path: Path) -> str:
60123
"""Marker that must wrap the BEGIN of a corrected file block emitted by the agent."""
61124
return f"<<<BEGIN_FILE:{path}>>>"
@@ -435,6 +498,12 @@ def _run_anthropic_variants(prompt_text: str, cwd: Path, total_timeout: int, lab
435498
return last
436499
finally:
437500
prompt_file.unlink(missing_ok=True)
501+
# Issue #186: Scan for suspicious files after Anthropic agent runs
502+
_detect_suspicious_files(cwd, f"After _run_anthropic_variants ({label})")
503+
# Also scan project root in case agent created files there
504+
project_root = Path.cwd()
505+
if project_root != cwd:
506+
_detect_suspicious_files(project_root, f"After _run_anthropic_variants ({label}) - project root")
438507

439508
def _run_cli_args_google(args: List[str], cwd: Path, timeout: int) -> subprocess.CompletedProcess:
440509
"""Subprocess runner for Google commands with common sanitized env."""
@@ -494,6 +563,12 @@ def _run_google_variants(prompt_text: str, cwd: Path, total_timeout: int, label:
494563
return last
495564
finally:
496565
prompt_file.unlink(missing_ok=True)
566+
# Issue #186: Scan for suspicious files after Google agent runs
567+
_detect_suspicious_files(cwd, f"After _run_google_variants ({label})")
568+
# Also scan project root in case agent created files there
569+
project_root = Path.cwd()
570+
if project_root != cwd:
571+
_detect_suspicious_files(project_root, f"After _run_google_variants ({label}) - project root")
497572

498573
def _run_testcmd(cmd: str, cwd: Path) -> bool:
499574
"""
@@ -532,7 +607,7 @@ def _verify_and_log(unit_test_file: str, cwd: Path, *, verify_cmd: Optional[str]
532607
return _run_testcmd(run_cmd, cwd)
533608
# Fallback: try running with Python if no run command found
534609
verify = subprocess.run(
535-
[os.sys.executable, str(Path(unit_test_file).resolve())],
610+
[sys.executable, str(Path(unit_test_file).resolve())],
536611
capture_output=True,
537612
text=True,
538613
check=False,
@@ -800,7 +875,7 @@ def _try_harvest_then_verify(
800875
newest = code_path.read_text(encoding="utf-8")
801876
_print_diff(code_snapshot, newest, code_path)
802877
ok = _post_apply_verify_or_testcmd(
803-
provider, unit_test_file, working_dir,
878+
provider, unit_test_file, cwd,
804879
verify_cmd=verify_cmd, verify_enabled=verify_enabled,
805880
stdout=res.stdout or "", stderr=res.stderr or ""
806881
)
@@ -992,7 +1067,7 @@ def _is_useless_error_content(content: str) -> bool:
9921067
else:
9931068
# Fallback: run directly with Python interpreter
9941069
pre = subprocess.run(
995-
[os.sys.executable, str(Path(unit_test_file).resolve())],
1070+
[sys.executable, str(Path(unit_test_file).resolve())],
9961071
capture_output=True,
9971072
text=True,
9981073
check=False,

pdd/cmd_test_main.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ def cmd_test_main(
157157
source_file_path=source_file_path_for_prompt,
158158
test_file_path=test_file_path_for_prompt,
159159
module_name=module_name_for_prompt,
160+
existing_tests=input_strings.get("existing_tests"),
160161
)
161162
except Exception as exception:
162163
# A general exception is caught to handle various errors that can occur
@@ -216,10 +217,16 @@ def cmd_test_main(
216217
# Ensure parent directory exists
217218
output_path = Path(output_file)
218219
output_path.parent.mkdir(parents=True, exist_ok=True)
219-
220-
with open(output_file, "w", encoding="utf-8") as file_handle:
221-
file_handle.write(unit_test)
222-
print(f"[bold green]Unit tests saved to:[/bold green] {output_file}")
220+
221+
# Use append mode when merging with existing tests
222+
if merge and existing_tests:
223+
with open(output_file, "a", encoding="utf-8") as file_handle:
224+
file_handle.write("\n\n" + unit_test)
225+
print(f"[bold green]Unit tests appended to:[/bold green] {output_file}")
226+
else:
227+
with open(output_file, "w", encoding="utf-8") as file_handle:
228+
file_handle.write(unit_test)
229+
print(f"[bold green]Unit tests saved to:[/bold green] {output_file}")
223230
except Exception as exception:
224231
# A broad exception is caught here to handle potential file system errors
225232
# (e.g., permissions, disk space) that can occur when writing the

0 commit comments

Comments
 (0)