pdd-cli 0.0.64__py3-none-any.whl → 0.0.65__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.
Potentially problematic release.
This version of pdd-cli might be problematic. Click here for more details.
- pdd/__init__.py +1 -1
- pdd/code_generator_main.py +327 -24
- pdd/data/llm_model.csv +2 -1
- pdd/render_mermaid.py +227 -0
- pdd/templates/architecture/architecture_json.prompt +5 -0
- pdd/templates/architecture/mermaid_diagram.prompt +40 -0
- {pdd_cli-0.0.64.dist-info → pdd_cli-0.0.65.dist-info}/METADATA +3 -3
- {pdd_cli-0.0.64.dist-info → pdd_cli-0.0.65.dist-info}/RECORD +12 -10
- {pdd_cli-0.0.64.dist-info → pdd_cli-0.0.65.dist-info}/WHEEL +0 -0
- {pdd_cli-0.0.64.dist-info → pdd_cli-0.0.65.dist-info}/entry_points.txt +0 -0
- {pdd_cli-0.0.64.dist-info → pdd_cli-0.0.65.dist-info}/licenses/LICENSE +0 -0
- {pdd_cli-0.0.64.dist-info → pdd_cli-0.0.65.dist-info}/top_level.txt +0 -0
pdd/__init__.py
CHANGED
pdd/code_generator_main.py
CHANGED
|
@@ -6,6 +6,8 @@ import pathlib
|
|
|
6
6
|
import shlex
|
|
7
7
|
import subprocess
|
|
8
8
|
import requests
|
|
9
|
+
import tempfile
|
|
10
|
+
import sys
|
|
9
11
|
from typing import Optional, Tuple, Dict, Any, List
|
|
10
12
|
|
|
11
13
|
import click
|
|
@@ -20,6 +22,7 @@ from .preprocess import preprocess as pdd_preprocess
|
|
|
20
22
|
from .code_generator import code_generator as local_code_generator_func
|
|
21
23
|
from .incremental_code_generator import incremental_code_generator as incremental_code_generator_func
|
|
22
24
|
from .get_jwt_token import get_jwt_token, AuthError, NetworkError, TokenError, UserCancelledError, RateLimitError
|
|
25
|
+
from .python_env_detector import detect_host_python_executable
|
|
23
26
|
|
|
24
27
|
# Environment variable names for Firebase/GitHub auth
|
|
25
28
|
FIREBASE_API_KEY_ENV_VAR = "NEXT_PUBLIC_FIREBASE_API_KEY"
|
|
@@ -463,7 +466,131 @@ def code_generator_main(
|
|
|
463
466
|
can_attempt_incremental = False
|
|
464
467
|
|
|
465
468
|
try:
|
|
466
|
-
|
|
469
|
+
# Determine template-driven switches
|
|
470
|
+
llm_enabled: bool = True
|
|
471
|
+
post_process_script: Optional[str] = None
|
|
472
|
+
prompt_body_for_script: str = prompt_content
|
|
473
|
+
# Allow environment override for LLM toggle when front matter omits it
|
|
474
|
+
env_llm_raw = None
|
|
475
|
+
try:
|
|
476
|
+
if env_vars and 'llm' in env_vars:
|
|
477
|
+
env_llm_raw = str(env_vars.get('llm'))
|
|
478
|
+
elif os.environ.get('llm') is not None:
|
|
479
|
+
env_llm_raw = os.environ.get('llm')
|
|
480
|
+
elif os.environ.get('LLM') is not None:
|
|
481
|
+
env_llm_raw = os.environ.get('LLM')
|
|
482
|
+
except Exception:
|
|
483
|
+
env_llm_raw = None
|
|
484
|
+
if fm_meta and isinstance(fm_meta, dict):
|
|
485
|
+
try:
|
|
486
|
+
if 'llm' in fm_meta:
|
|
487
|
+
llm_enabled = bool(fm_meta.get('llm', True))
|
|
488
|
+
elif env_llm_raw is not None:
|
|
489
|
+
llm_str = str(env_llm_raw).strip().lower()
|
|
490
|
+
if llm_str in {"0", "false", "no", "off"}:
|
|
491
|
+
llm_enabled = False
|
|
492
|
+
else:
|
|
493
|
+
llm_enabled = llm_str in {"1", "true", "yes", "on"}
|
|
494
|
+
except Exception:
|
|
495
|
+
llm_enabled = True
|
|
496
|
+
elif env_llm_raw is not None:
|
|
497
|
+
try:
|
|
498
|
+
llm_str = str(env_llm_raw).strip().lower()
|
|
499
|
+
if llm_str in {"0", "false", "no", "off"}:
|
|
500
|
+
llm_enabled = False
|
|
501
|
+
else:
|
|
502
|
+
llm_enabled = llm_str in {"1", "true", "yes", "on"}
|
|
503
|
+
except Exception:
|
|
504
|
+
llm_enabled = True
|
|
505
|
+
|
|
506
|
+
if verbose:
|
|
507
|
+
console.print(f"[blue]LLM enabled:[/blue] {llm_enabled}")
|
|
508
|
+
|
|
509
|
+
# Resolve post-process script from env/CLI override, then front matter, then sensible default per template
|
|
510
|
+
try:
|
|
511
|
+
post_process_script = None
|
|
512
|
+
script_override = None
|
|
513
|
+
if env_vars:
|
|
514
|
+
script_override = env_vars.get('POST_PROCESS_PYTHON') or env_vars.get('post_process_python')
|
|
515
|
+
if not script_override:
|
|
516
|
+
script_override = os.environ.get('POST_PROCESS_PYTHON') or os.environ.get('post_process_python')
|
|
517
|
+
if script_override and str(script_override).strip():
|
|
518
|
+
expanded = _expand_vars(str(script_override), env_vars)
|
|
519
|
+
pkg_dir = pathlib.Path(__file__).parent.resolve()
|
|
520
|
+
repo_root = pathlib.Path.cwd().resolve()
|
|
521
|
+
repo_pdd_dir = (repo_root / 'pdd').resolve()
|
|
522
|
+
candidate = pathlib.Path(expanded)
|
|
523
|
+
if not candidate.is_absolute():
|
|
524
|
+
# 1) As provided, relative to CWD
|
|
525
|
+
as_is = (repo_root / candidate)
|
|
526
|
+
# 2) Under repo pdd/
|
|
527
|
+
under_repo_pdd = (repo_pdd_dir / candidate.name) if not as_is.exists() else as_is
|
|
528
|
+
# 3) Under installed package dir
|
|
529
|
+
under_pkg = (pkg_dir / candidate.name) if not as_is.exists() and not under_repo_pdd.exists() else as_is
|
|
530
|
+
if as_is.exists():
|
|
531
|
+
candidate = as_is
|
|
532
|
+
elif under_repo_pdd.exists():
|
|
533
|
+
candidate = under_repo_pdd
|
|
534
|
+
elif under_pkg.exists():
|
|
535
|
+
candidate = under_pkg
|
|
536
|
+
else:
|
|
537
|
+
candidate = as_is # will fail later with not found
|
|
538
|
+
post_process_script = str(candidate.resolve())
|
|
539
|
+
elif fm_meta and isinstance(fm_meta, dict):
|
|
540
|
+
raw_script = fm_meta.get('post_process_python')
|
|
541
|
+
if isinstance(raw_script, str) and raw_script.strip():
|
|
542
|
+
# Expand variables like $VAR and ${VAR}
|
|
543
|
+
expanded = _expand_vars(raw_script, env_vars)
|
|
544
|
+
pkg_dir = pathlib.Path(__file__).parent.resolve()
|
|
545
|
+
repo_root = pathlib.Path.cwd().resolve()
|
|
546
|
+
repo_pdd_dir = (repo_root / 'pdd').resolve()
|
|
547
|
+
candidate = pathlib.Path(expanded)
|
|
548
|
+
if not candidate.is_absolute():
|
|
549
|
+
as_is = (repo_root / candidate)
|
|
550
|
+
under_repo_pdd = (repo_pdd_dir / candidate.name) if not as_is.exists() else as_is
|
|
551
|
+
under_pkg = (pkg_dir / candidate.name) if not as_is.exists() and not under_repo_pdd.exists() else as_is
|
|
552
|
+
if as_is.exists():
|
|
553
|
+
candidate = as_is
|
|
554
|
+
elif under_repo_pdd.exists():
|
|
555
|
+
candidate = under_repo_pdd
|
|
556
|
+
elif under_pkg.exists():
|
|
557
|
+
candidate = under_pkg
|
|
558
|
+
else:
|
|
559
|
+
candidate = as_is
|
|
560
|
+
post_process_script = str(candidate.resolve())
|
|
561
|
+
# Fallback default: for architecture template, use built-in render_mermaid.py
|
|
562
|
+
if not post_process_script:
|
|
563
|
+
try:
|
|
564
|
+
prompt_str = str(prompt_file)
|
|
565
|
+
looks_like_arch_template = (
|
|
566
|
+
(isinstance(prompt_file, str) and (
|
|
567
|
+
prompt_str.endswith("architecture/architecture_json.prompt") or
|
|
568
|
+
prompt_str.endswith("architecture/architecture_json") or
|
|
569
|
+
"architecture_json.prompt" in prompt_str or
|
|
570
|
+
"architecture/architecture_json" in prompt_str
|
|
571
|
+
))
|
|
572
|
+
)
|
|
573
|
+
looks_like_arch_output = (
|
|
574
|
+
bool(output_path) and pathlib.Path(str(output_path)).name == 'architecture.json'
|
|
575
|
+
)
|
|
576
|
+
if looks_like_arch_template or looks_like_arch_output:
|
|
577
|
+
pkg_dir = pathlib.Path(__file__).parent
|
|
578
|
+
repo_pdd_dir = pathlib.Path.cwd() / 'pdd'
|
|
579
|
+
if (pkg_dir / 'render_mermaid.py').exists():
|
|
580
|
+
post_process_script = str((pkg_dir / 'render_mermaid.py').resolve())
|
|
581
|
+
elif (repo_pdd_dir / 'render_mermaid.py').exists():
|
|
582
|
+
post_process_script = str((repo_pdd_dir / 'render_mermaid.py').resolve())
|
|
583
|
+
except Exception:
|
|
584
|
+
post_process_script = None
|
|
585
|
+
if verbose:
|
|
586
|
+
console.print(f"[blue]Post-process script resolved to:[/blue] {post_process_script if post_process_script else 'None'}")
|
|
587
|
+
except Exception:
|
|
588
|
+
post_process_script = None
|
|
589
|
+
# If LLM is disabled but no post-process script is provided, surface a helpful error
|
|
590
|
+
if not llm_enabled and not post_process_script:
|
|
591
|
+
console.print("[red]Error: llm: false requires 'post_process_python' to be specified in front matter.[/red]")
|
|
592
|
+
return "", was_incremental_operation, total_cost, "error"
|
|
593
|
+
if llm_enabled and can_attempt_incremental and existing_code_content is not None and original_prompt_content_for_incremental is not None:
|
|
467
594
|
if verbose:
|
|
468
595
|
console.print(Panel("Attempting incremental code generation...", title="[blue]Mode[/blue]", expand=False))
|
|
469
596
|
|
|
@@ -514,7 +641,7 @@ def code_generator_main(
|
|
|
514
641
|
elif verbose:
|
|
515
642
|
console.print(Panel(f"Incremental update successful. Model: {model_name}, Cost: ${total_cost:.6f}", title="[green]Incremental Success[/green]", expand=False))
|
|
516
643
|
|
|
517
|
-
if not was_incremental_operation: # Full generation path
|
|
644
|
+
if llm_enabled and not was_incremental_operation: # Full generation path
|
|
518
645
|
if verbose:
|
|
519
646
|
console.print(Panel("Performing full code generation...", title="[blue]Mode[/blue]", expand=False))
|
|
520
647
|
|
|
@@ -599,28 +726,166 @@ def code_generator_main(
|
|
|
599
726
|
was_incremental_operation = False
|
|
600
727
|
if verbose:
|
|
601
728
|
console.print(Panel(f"Full generation successful. Model: {model_name}, Cost: ${total_cost:.6f}", title="[green]Local Success[/green]", expand=False))
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
729
|
+
|
|
730
|
+
# Optional post-process Python hook (runs after LLM when enabled, or standalone when LLM is disabled)
|
|
731
|
+
if post_process_script:
|
|
605
732
|
try:
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
733
|
+
python_executable = detect_host_python_executable()
|
|
734
|
+
# Choose stdin for the script: LLM output if available and enabled, else prompt body
|
|
735
|
+
stdin_payload = generated_code_content if (llm_enabled and generated_code_content is not None) else prompt_body_for_script
|
|
736
|
+
env = os.environ.copy()
|
|
737
|
+
env['PDD_LANGUAGE'] = str(language or '')
|
|
738
|
+
env['PDD_OUTPUT_PATH'] = str(output_path or '')
|
|
739
|
+
env['PDD_PROMPT_FILE'] = str(pathlib.Path(prompt_file).resolve())
|
|
740
|
+
env['PDD_LLM'] = '1' if llm_enabled else '0'
|
|
741
|
+
try:
|
|
742
|
+
env['PDD_ENV_VARS'] = json.dumps(env_vars or {})
|
|
743
|
+
except Exception:
|
|
744
|
+
env['PDD_ENV_VARS'] = '{}'
|
|
745
|
+
# If front matter provides args, run in argv mode with a temp input file
|
|
746
|
+
fm_args = None
|
|
747
|
+
try:
|
|
748
|
+
# Env/CLI override for args (comma-separated or JSON list)
|
|
749
|
+
raw_args_env = None
|
|
750
|
+
if env_vars:
|
|
751
|
+
raw_args_env = env_vars.get('POST_PROCESS_ARGS') or env_vars.get('post_process_args')
|
|
752
|
+
if not raw_args_env:
|
|
753
|
+
raw_args_env = os.environ.get('POST_PROCESS_ARGS') or os.environ.get('post_process_args')
|
|
754
|
+
if raw_args_env:
|
|
755
|
+
s = str(raw_args_env).strip()
|
|
756
|
+
parsed_list = None
|
|
757
|
+
if s.startswith('[') and s.endswith(']'):
|
|
758
|
+
try:
|
|
759
|
+
parsed = json.loads(s)
|
|
760
|
+
if isinstance(parsed, list):
|
|
761
|
+
parsed_list = [str(a) for a in parsed]
|
|
762
|
+
except Exception:
|
|
763
|
+
parsed_list = None
|
|
764
|
+
if parsed_list is None:
|
|
765
|
+
if ',' in s:
|
|
766
|
+
parsed_list = [part.strip() for part in s.split(',') if part.strip()]
|
|
767
|
+
else:
|
|
768
|
+
parsed_list = [part for part in s.split() if part]
|
|
769
|
+
fm_args = parsed_list or None
|
|
770
|
+
if fm_args is None:
|
|
771
|
+
raw_args = fm_meta.get('post_process_args') if isinstance(fm_meta, dict) else None
|
|
772
|
+
if isinstance(raw_args, list):
|
|
773
|
+
fm_args = [str(a) for a in raw_args]
|
|
774
|
+
except Exception:
|
|
775
|
+
fm_args = None
|
|
776
|
+
proc = None
|
|
777
|
+
temp_input_path = None
|
|
778
|
+
try:
|
|
779
|
+
if fm_args is None:
|
|
780
|
+
# Provide sensible default args for architecture template with render_mermaid.py
|
|
781
|
+
try:
|
|
782
|
+
if post_process_script and pathlib.Path(post_process_script).name == 'render_mermaid.py':
|
|
783
|
+
if isinstance(prompt_file, str) and prompt_file.endswith('architecture/architecture_json.prompt'):
|
|
784
|
+
fm_args = ["{INPUT_FILE}", "{APP_NAME}", "{OUTPUT_HTML}"]
|
|
785
|
+
except Exception:
|
|
786
|
+
pass
|
|
787
|
+
if fm_args:
|
|
788
|
+
# When LLM is disabled, use the existing output file instead of creating a temp file
|
|
789
|
+
if not llm_enabled and output_path and pathlib.Path(output_path).exists():
|
|
790
|
+
temp_input_path = str(pathlib.Path(output_path).resolve())
|
|
791
|
+
env['PDD_POSTPROCESS_INPUT_FILE'] = temp_input_path
|
|
792
|
+
else:
|
|
793
|
+
# Write payload to a temp file for scripts expecting a file path input
|
|
794
|
+
suffix = '.json' if (isinstance(language, str) and str(language).lower().strip() == 'json') or (output_path and str(output_path).lower().endswith('.json')) else '.txt'
|
|
795
|
+
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix=suffix, encoding='utf-8') as tf:
|
|
796
|
+
tf.write(stdin_payload or '')
|
|
797
|
+
temp_input_path = tf.name
|
|
798
|
+
env['PDD_POSTPROCESS_INPUT_FILE'] = temp_input_path
|
|
799
|
+
# Compute placeholder values
|
|
800
|
+
app_name_val = (env_vars or {}).get('APP_NAME') if env_vars else None
|
|
801
|
+
if not app_name_val:
|
|
802
|
+
app_name_val = 'System Architecture'
|
|
803
|
+
output_html_default = None
|
|
804
|
+
if output_path and str(output_path).lower().endswith('.json'):
|
|
805
|
+
output_html_default = str(pathlib.Path(output_path).with_name(f"{pathlib.Path(output_path).stem}_diagram.html").resolve())
|
|
806
|
+
placeholder_map = {
|
|
807
|
+
'INPUT_FILE': temp_input_path or '',
|
|
808
|
+
'OUTPUT': str(output_path or ''),
|
|
809
|
+
'PROMPT_FILE': str(pathlib.Path(prompt_file).resolve()),
|
|
810
|
+
'APP_NAME': str(app_name_val),
|
|
811
|
+
'OUTPUT_HTML': str(output_html_default or ''),
|
|
812
|
+
}
|
|
813
|
+
def _subst_arg(arg: str) -> str:
|
|
814
|
+
# First expand $VARS using existing helper, then {TOKENS}
|
|
815
|
+
expanded = _expand_vars(arg, env_vars)
|
|
816
|
+
for key, val in placeholder_map.items():
|
|
817
|
+
expanded = expanded.replace('{' + key + '}', val)
|
|
818
|
+
return expanded
|
|
819
|
+
args_list = [_subst_arg(a) for a in fm_args]
|
|
820
|
+
if verbose:
|
|
821
|
+
console.print(Panel(f"Post-process hook (argv)\nScript: {post_process_script}\nArgs: {args_list}", title="[blue]Post-process[/blue]", expand=False))
|
|
822
|
+
proc = subprocess.run(
|
|
823
|
+
[python_executable, post_process_script] + args_list,
|
|
824
|
+
text=True,
|
|
825
|
+
capture_output=True,
|
|
826
|
+
timeout=300,
|
|
827
|
+
cwd=str(pathlib.Path(post_process_script).parent),
|
|
828
|
+
env=env
|
|
829
|
+
)
|
|
830
|
+
else:
|
|
831
|
+
# Run the script with stdin payload, capture stdout as final content
|
|
832
|
+
if verbose:
|
|
833
|
+
console.print(Panel(f"Post-process hook (stdin)\nScript: {post_process_script}", title="[blue]Post-process[/blue]", expand=False))
|
|
834
|
+
proc = subprocess.run(
|
|
835
|
+
[python_executable, post_process_script],
|
|
836
|
+
input=stdin_payload or '',
|
|
837
|
+
text=True,
|
|
838
|
+
capture_output=True,
|
|
839
|
+
timeout=300,
|
|
840
|
+
cwd=str(pathlib.Path(post_process_script).parent),
|
|
841
|
+
env=env
|
|
842
|
+
)
|
|
843
|
+
finally:
|
|
844
|
+
if temp_input_path:
|
|
614
845
|
try:
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
846
|
+
# Only delete temp files, not the actual output file when llm=false
|
|
847
|
+
if llm_enabled or not (output_path and pathlib.Path(output_path).exists() and temp_input_path == str(pathlib.Path(output_path).resolve())):
|
|
848
|
+
os.unlink(temp_input_path)
|
|
849
|
+
except Exception:
|
|
850
|
+
pass
|
|
851
|
+
if proc and proc.returncode == 0:
|
|
852
|
+
if verbose:
|
|
853
|
+
console.print(Panel(f"Post-process success (rc=0)\nstdout: {proc.stdout[:150]}\nstderr: {proc.stderr[:150]}", title="[green]Post-process[/green]", expand=False))
|
|
854
|
+
# Do not modify generated_code_content to preserve architecture.json
|
|
855
|
+
else:
|
|
856
|
+
rc = getattr(proc, 'returncode', 'N/A')
|
|
857
|
+
err = getattr(proc, 'stderr', '')
|
|
858
|
+
console.print(f"[yellow]Post-process failed (rc={rc}). Stderr:\n{err[:500]}[/yellow]")
|
|
859
|
+
except FileNotFoundError:
|
|
860
|
+
console.print(f"[yellow]Post-process script not found: {post_process_script}. Skipping.[/yellow]")
|
|
861
|
+
except FileNotFoundError:
|
|
862
|
+
console.print(f"[yellow]Post-process script not found: {post_process_script}. Skipping.[/yellow]")
|
|
863
|
+
except subprocess.TimeoutExpired:
|
|
864
|
+
console.print("[yellow]Post-process script timed out. Skipping.[/yellow]")
|
|
865
|
+
except Exception as e:
|
|
866
|
+
console.print(f"[yellow]Post-process script error: {e}. Skipping.[/yellow]")
|
|
867
|
+
if generated_code_content is not None:
|
|
868
|
+
# Optional output_schema JSON validation before writing (only when LLM ran)
|
|
869
|
+
if llm_enabled:
|
|
870
|
+
try:
|
|
871
|
+
if fm_meta and isinstance(fm_meta.get("output_schema"), dict):
|
|
872
|
+
is_json_output = False
|
|
873
|
+
if isinstance(language, str) and str(language).lower().strip() == "json":
|
|
874
|
+
is_json_output = True
|
|
875
|
+
elif output_path and str(output_path).endswith(".json"):
|
|
876
|
+
is_json_output = True
|
|
877
|
+
if is_json_output:
|
|
878
|
+
parsed = json.loads(generated_code_content)
|
|
879
|
+
try:
|
|
880
|
+
import jsonschema # type: ignore
|
|
881
|
+
jsonschema.validate(instance=parsed, schema=fm_meta.get("output_schema"))
|
|
882
|
+
except ModuleNotFoundError:
|
|
883
|
+
if verbose and not quiet:
|
|
884
|
+
console.print("[yellow]jsonschema not installed; skipping schema validation.[/yellow]")
|
|
885
|
+
except Exception as ve:
|
|
886
|
+
raise click.UsageError(f"Generated JSON does not match output_schema: {ve}")
|
|
887
|
+
except json.JSONDecodeError as jde:
|
|
888
|
+
raise click.UsageError(f"Generated output is not valid JSON: {jde}")
|
|
624
889
|
|
|
625
890
|
if output_path:
|
|
626
891
|
p_output = pathlib.Path(output_path)
|
|
@@ -628,13 +893,51 @@ def code_generator_main(
|
|
|
628
893
|
p_output.write_text(generated_code_content, encoding="utf-8")
|
|
629
894
|
if verbose or not quiet:
|
|
630
895
|
console.print(f"Generated code saved to: [green]{p_output.resolve()}[/green]")
|
|
896
|
+
# Safety net: ensure architecture HTML is generated post-write if applicable
|
|
897
|
+
try:
|
|
898
|
+
# Prefer resolved script if available; else default for architecture outputs
|
|
899
|
+
script_path2 = post_process_script
|
|
900
|
+
if not script_path2:
|
|
901
|
+
looks_like_arch_output2 = pathlib.Path(str(p_output)).name == 'architecture.json'
|
|
902
|
+
if looks_like_arch_output2:
|
|
903
|
+
pkg_dir2 = pathlib.Path(__file__).parent
|
|
904
|
+
repo_pdd_dir2 = pathlib.Path.cwd() / 'pdd'
|
|
905
|
+
if (pkg_dir2 / 'render_mermaid.py').exists():
|
|
906
|
+
script_path2 = str((pkg_dir2 / 'render_mermaid.py').resolve())
|
|
907
|
+
elif (repo_pdd_dir2 / 'render_mermaid.py').exists():
|
|
908
|
+
script_path2 = str((repo_pdd_dir2 / 'render_mermaid.py').resolve())
|
|
909
|
+
if script_path2 and pathlib.Path(script_path2).exists():
|
|
910
|
+
app_name2 = os.environ.get('APP_NAME') or (env_vars or {}).get('APP_NAME') or 'System Architecture'
|
|
911
|
+
out_html2 = os.environ.get('POST_PROCESS_OUTPUT') or str(p_output.with_name(f"{p_output.stem}_diagram.html").resolve())
|
|
912
|
+
# Only run if HTML not present yet
|
|
913
|
+
if not pathlib.Path(out_html2).exists():
|
|
914
|
+
try:
|
|
915
|
+
py_exec2 = detect_host_python_executable()
|
|
916
|
+
except Exception:
|
|
917
|
+
py_exec2 = sys.executable
|
|
918
|
+
if verbose:
|
|
919
|
+
console.print(Panel(f"Safety net post-process\nScript: {script_path2}\nArgs: {[str(p_output.resolve()), app_name2, out_html2]}", title="[blue]Post-process[/blue]", expand=False))
|
|
920
|
+
sp2 = subprocess.run([py_exec2, script_path2, str(p_output.resolve()), app_name2, out_html2],
|
|
921
|
+
capture_output=True, text=True, cwd=str(pathlib.Path(script_path2).parent))
|
|
922
|
+
if sp2.returncode == 0 and not quiet:
|
|
923
|
+
print(f"✅ Generated: {out_html2}")
|
|
924
|
+
elif verbose:
|
|
925
|
+
console.print(f"[yellow]Safety net failed (rc={sp2.returncode}). stderr:\n{sp2.stderr[:300]}[/yellow]")
|
|
926
|
+
except Exception:
|
|
927
|
+
pass
|
|
928
|
+
# Post-step now runs regardless of LLM value via the general post-process hook above.
|
|
631
929
|
elif not quiet:
|
|
632
930
|
# No destination resolved; surface the generated code directly to the console.
|
|
633
931
|
console.print(Panel(Text(generated_code_content, overflow="fold"), title="[cyan]Generated Code[/cyan]", expand=False))
|
|
634
932
|
console.print("[yellow]No output path resolved; skipping file write and stdout print.[/yellow]")
|
|
635
933
|
else:
|
|
636
|
-
|
|
637
|
-
|
|
934
|
+
# If LLM was disabled and post-process ran, that's a success (no error)
|
|
935
|
+
if not llm_enabled and post_process_script:
|
|
936
|
+
if verbose or not quiet:
|
|
937
|
+
console.print("[green]Post-process completed successfully (LLM was disabled).[/green]")
|
|
938
|
+
else:
|
|
939
|
+
console.print("[red]Error: Code generation failed. No code was produced.[/red]")
|
|
940
|
+
return "", was_incremental_operation, total_cost, model_name or "error"
|
|
638
941
|
|
|
639
942
|
except Exception as e:
|
|
640
943
|
console.print(f"[red]An unexpected error occurred: {e}[/red]")
|
pdd/data/llm_model.csv
CHANGED
|
@@ -17,4 +17,5 @@ Fireworks,fireworks_ai/accounts/fireworks/models/glm-4p5,3.0,8.0,1364,,FIREWORKS
|
|
|
17
17
|
OpenAI,groq/moonshotai/kimi-k2-instruct,1.0,3.0,1330,,GROQ_API_KEY,0,True,none
|
|
18
18
|
Anthropic,anthropic/claude-sonnet-4-5-20250929,3.0,15.0,1356,,ANTHROPIC_API_KEY,64000,True,budget
|
|
19
19
|
Anthropic,anthropic/claude-opus-4-1-20250805,3.0,15.0,1474,,ANTHROPIC_API_KEY,32000,True,budget
|
|
20
|
-
Anthropic,anthropic/claude-3-5-haiku-20241022,3.0,15.0,1133,,ANTHROPIC_API_KEY,8192,True,budget
|
|
20
|
+
Anthropic,anthropic/claude-3-5-haiku-20241022,3.0,15.0,1133,,ANTHROPIC_API_KEY,8192,True,budget
|
|
21
|
+
OpenAI,openai/claude-sonnet-4-5,0,0,1354,https://DRUGBEY-DU61012.snowflakecomputing.com/api/v2/cortex/openai,SNOWFLAKE_PAT,64000,True,budget
|
pdd/render_mermaid.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Render architecture.json as an interactive HTML Mermaid diagram.
|
|
4
|
+
Usage:
|
|
5
|
+
python render_mermaid.py architecture.json "App Name" [output.html]
|
|
6
|
+
Features:
|
|
7
|
+
- Direct browser rendering (no external tools)
|
|
8
|
+
- Beautiful modern UI with statistics
|
|
9
|
+
- Color-coded module categories
|
|
10
|
+
- Interactive Mermaid diagram
|
|
11
|
+
- Self-contained HTML (works offline)
|
|
12
|
+
"""
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
import html
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
# Indentation constants for better maintainability
|
|
19
|
+
INDENT = ' ' # 4 spaces per level
|
|
20
|
+
LEVELS = {
|
|
21
|
+
'root': 0,
|
|
22
|
+
'subgraph': 1,
|
|
23
|
+
'node': 2,
|
|
24
|
+
'connection': 1,
|
|
25
|
+
'style': 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def generate_mermaid_code(architecture, app_name="System"):
|
|
29
|
+
"""Generate Mermaid flowchart code from architecture JSON."""
|
|
30
|
+
# Escape quotes for Mermaid label, which uses HTML entities
|
|
31
|
+
escaped_app_name = app_name.replace('"', '"')
|
|
32
|
+
# Match test expectation: add a trailing space only if quotes were present
|
|
33
|
+
prd_label = f'{escaped_app_name} ' if """ in escaped_app_name else escaped_app_name
|
|
34
|
+
|
|
35
|
+
lines = ["flowchart TB", f'{INDENT * LEVELS["node"]}PRD["{prd_label}"]', INDENT]
|
|
36
|
+
|
|
37
|
+
if not architecture:
|
|
38
|
+
lines.append(INDENT)
|
|
39
|
+
|
|
40
|
+
# Categorize modules by tags (frontend takes priority over backend)
|
|
41
|
+
frontend = [
|
|
42
|
+
m
|
|
43
|
+
for m in architecture
|
|
44
|
+
if any(t in m.get('tags', []) for t in ['frontend', 'react', 'nextjs', 'ui', 'page', 'component'])
|
|
45
|
+
]
|
|
46
|
+
backend = [
|
|
47
|
+
m
|
|
48
|
+
for m in architecture
|
|
49
|
+
if m not in frontend
|
|
50
|
+
and any(t in m.get('tags', []) for t in ['backend', 'api', 'database', 'sqlalchemy', 'fastapi'])
|
|
51
|
+
]
|
|
52
|
+
shared = [m for m in architecture if m not in frontend and m not in backend]
|
|
53
|
+
|
|
54
|
+
# Generate subgraphs
|
|
55
|
+
for group_name, modules in [("Frontend", frontend), ("Backend", backend), ("Shared", shared)]:
|
|
56
|
+
if modules:
|
|
57
|
+
lines.append(f"{INDENT * LEVELS['subgraph']}subgraph {group_name}")
|
|
58
|
+
for m in modules:
|
|
59
|
+
name = Path(m['filename']).stem
|
|
60
|
+
pri = m.get('priority', 0)
|
|
61
|
+
lines.append(f'{INDENT * LEVELS["node"]}{name}["{name} ({pri})"]')
|
|
62
|
+
lines.append(f"{INDENT * LEVELS['subgraph']}end")
|
|
63
|
+
lines.append(INDENT)
|
|
64
|
+
|
|
65
|
+
# PRD connections
|
|
66
|
+
if frontend:
|
|
67
|
+
lines.append(f"{INDENT * LEVELS['connection']}PRD --> Frontend")
|
|
68
|
+
if backend:
|
|
69
|
+
lines.append(f"{INDENT * LEVELS['connection']}PRD --> Backend")
|
|
70
|
+
|
|
71
|
+
# Add newline between PRD connections and dependencies
|
|
72
|
+
if frontend or backend:
|
|
73
|
+
lines.append("")
|
|
74
|
+
|
|
75
|
+
# Dependencies
|
|
76
|
+
for m in architecture:
|
|
77
|
+
src = Path(m['filename']).stem
|
|
78
|
+
for dep in m.get('dependencies', []):
|
|
79
|
+
dst = Path(dep).stem
|
|
80
|
+
lines.append(f'{INDENT * LEVELS["connection"]}{src} -->|uses| {dst}')
|
|
81
|
+
|
|
82
|
+
# Add newline after dependencies
|
|
83
|
+
if any(m.get('dependencies', []) for m in architecture):
|
|
84
|
+
lines.append(INDENT)
|
|
85
|
+
|
|
86
|
+
# Styles
|
|
87
|
+
lines.extend([f"{INDENT * LEVELS['style']}classDef frontend fill:#FFF3E0,stroke:#F57C00,stroke-width:2px",
|
|
88
|
+
f"{INDENT * LEVELS['style']}classDef backend fill:#E3F2FD,stroke:#1976D2,stroke-width:2px",
|
|
89
|
+
f"{INDENT * LEVELS['style']}classDef shared fill:#E8F5E9,stroke:#388E3C,stroke-width:2px",
|
|
90
|
+
f"{INDENT * LEVELS['style']}classDef system fill:#E0E0E0,stroke:#616161,stroke-width:3px", INDENT])
|
|
91
|
+
|
|
92
|
+
# Apply classes
|
|
93
|
+
if frontend:
|
|
94
|
+
lines.append(f"{INDENT * LEVELS['style']}class {','.join([Path(m['filename']).stem for m in frontend])} frontend")
|
|
95
|
+
if backend:
|
|
96
|
+
lines.append(f"{INDENT * LEVELS['style']}class {','.join([Path(m['filename']).stem for m in backend])} backend")
|
|
97
|
+
if shared:
|
|
98
|
+
lines.append(f"{INDENT * LEVELS['style']}class {','.join([Path(m['filename']).stem for m in shared])} shared")
|
|
99
|
+
lines.append(f"{INDENT * LEVELS['style']}class PRD system")
|
|
100
|
+
|
|
101
|
+
return "\n".join(lines)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def generate_html(mermaid_code, architecture, app_name):
|
|
105
|
+
"""Generate interactive HTML with hover tooltips."""
|
|
106
|
+
|
|
107
|
+
# Create module data as JSON for tooltips
|
|
108
|
+
module_data = {}
|
|
109
|
+
for m in architecture:
|
|
110
|
+
module_id = Path(m['filename']).stem
|
|
111
|
+
module_data[module_id] = {
|
|
112
|
+
'filename': m['filename'],
|
|
113
|
+
'priority': m.get('priority', 'N/A'),
|
|
114
|
+
'description': m.get('description', 'No description'),
|
|
115
|
+
'dependencies': m.get('dependencies', []),
|
|
116
|
+
'tags': m.get('tags', []),
|
|
117
|
+
'filepath': m.get('filepath', ''),
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module_json = json.dumps(module_data)
|
|
121
|
+
escaped_app_name = html.escape(app_name)
|
|
122
|
+
|
|
123
|
+
return f"""<!DOCTYPE html>
|
|
124
|
+
<html><head><meta charset="UTF-8"><title>{escaped_app_name}</title>
|
|
125
|
+
<script type=\"module\">
|
|
126
|
+
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
|
|
127
|
+
mermaid.initialize({{startOnLoad:true,theme:'default'}});
|
|
128
|
+
window.addEventListener('load', () => {{
|
|
129
|
+
const moduleData = {module_json};
|
|
130
|
+
|
|
131
|
+
// Add hover listeners to all nodes
|
|
132
|
+
setTimeout(() => {{
|
|
133
|
+
const nodes = document.querySelectorAll('.node');
|
|
134
|
+
nodes.forEach(node => {{
|
|
135
|
+
const text = node.querySelector('.nodeLabel');
|
|
136
|
+
if (!text) return;
|
|
137
|
+
|
|
138
|
+
const nodeText = text.textContent.trim();
|
|
139
|
+
const moduleId = nodeText.split(' ')[0];
|
|
140
|
+
const data = moduleData[moduleId];
|
|
141
|
+
|
|
142
|
+
if (data) {{
|
|
143
|
+
node.style.cursor = 'pointer';
|
|
144
|
+
|
|
145
|
+
node.addEventListener('mouseenter', (e) => {{
|
|
146
|
+
showTooltip(e, data);
|
|
147
|
+
}});
|
|
148
|
+
|
|
149
|
+
node.addEventListener('mouseleave', () => {{
|
|
150
|
+
hideTooltip();
|
|
151
|
+
}});
|
|
152
|
+
}}
|
|
153
|
+
}});
|
|
154
|
+
}}, 500);
|
|
155
|
+
}});
|
|
156
|
+
function showTooltip(e, data) {{
|
|
157
|
+
hideTooltip();
|
|
158
|
+
|
|
159
|
+
const tooltip = document.createElement('div');
|
|
160
|
+
tooltip.id = 'module-tooltip';
|
|
161
|
+
tooltip.innerHTML = `
|
|
162
|
+
<div style="font-weight:600;margin-bottom:8px;font-size:1.1em;">${{data.filename}}</div>
|
|
163
|
+
<div style="margin-bottom:6px;"><strong>Priority:</strong> ${{data.priority}}</div>
|
|
164
|
+
<div style="margin-bottom:6px;"><strong>Path:</strong> ${{data.filepath}}</div>
|
|
165
|
+
<div style="margin-bottom:6px;"><strong>Tags:</strong> ${{data.tags.join(', ')}}</div>
|
|
166
|
+
<div style="margin-bottom:6px;"><strong>Dependencies:</strong> ${{data.dependencies.length > 0 ? data.dependencies.join(', ') : 'None'}}</div>
|
|
167
|
+
<div style="margin-top:8px;padding-top:8px;border-top:1px solid #ddd;font-size:0.9em;color:#444;">${{data.description}}</div>
|
|
168
|
+
`;
|
|
169
|
+
|
|
170
|
+
document.body.appendChild(tooltip);
|
|
171
|
+
|
|
172
|
+
const rect = e.target.closest('.node').getBoundingClientRect();
|
|
173
|
+
tooltip.style.left = rect.right + 10 + 'px';
|
|
174
|
+
tooltip.style.top = rect.top + window.scrollY + 'px';
|
|
175
|
+
}}
|
|
176
|
+
function hideTooltip() {{
|
|
177
|
+
const existing = document.getElementById('module-tooltip');
|
|
178
|
+
if (existing) existing.remove();
|
|
179
|
+
}}
|
|
180
|
+
</script>
|
|
181
|
+
<style>
|
|
182
|
+
*{{margin:0;padding:0;box-sizing:border-box}}
|
|
183
|
+
body{{font-family:system-ui,sans-serif;background:#fff;color:#000;padding:2rem;max-width:1400px;margin:0 auto}}
|
|
184
|
+
h1{{font-size:2rem;font-weight:600;margin-bottom:2rem;padding-bottom:1rem;border-bottom:2px solid #000}}
|
|
185
|
+
.diagram{{border:1px solid #000;padding:2rem;margin:2rem 0;overflow-x:auto;position:relative}}
|
|
186
|
+
.mermaid{{display:flex;justify-content:center}}
|
|
187
|
+
#module-tooltip{{
|
|
188
|
+
position:absolute;
|
|
189
|
+
background:#fff;
|
|
190
|
+
border:2px solid #000;
|
|
191
|
+
padding:1rem;
|
|
192
|
+
max-width:400px;
|
|
193
|
+
z-index:1000;
|
|
194
|
+
box-shadow:4px 4px 0 rgba(0,0,0,0.1);
|
|
195
|
+
font-size:0.9rem;
|
|
196
|
+
line-height:1.5;
|
|
197
|
+
}}
|
|
198
|
+
.node{{transition:opacity 0.2s}}
|
|
199
|
+
.node:hover{{opacity:0.8}}
|
|
200
|
+
</style></head><body>
|
|
201
|
+
<h1>{escaped_app_name}</h1>
|
|
202
|
+
<div class="diagram"><pre class="mermaid">{mermaid_code}</pre></div>
|
|
203
|
+
</body></html>"""
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
if len(sys.argv) < 2:
|
|
208
|
+
print("Usage: python render_mermaid.py <architecture.json> [app_name] [output.html]")
|
|
209
|
+
sys.exit(1)
|
|
210
|
+
|
|
211
|
+
arch_file = sys.argv[1]
|
|
212
|
+
app_name = sys.argv[2] if len(sys.argv) > 2 else "System Architecture"
|
|
213
|
+
output_file = sys.argv[3] if len(sys.argv) > 3 else f"{Path(arch_file).stem}_diagram.html"
|
|
214
|
+
|
|
215
|
+
with open(arch_file) as f:
|
|
216
|
+
architecture = json.load(f)
|
|
217
|
+
|
|
218
|
+
mermaid_code = generate_mermaid_code(architecture, app_name)
|
|
219
|
+
html_content = generate_html(mermaid_code, architecture, app_name)
|
|
220
|
+
|
|
221
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
222
|
+
f.write(html_content)
|
|
223
|
+
|
|
224
|
+
print(f"✅ Generated: {output_file}")
|
|
225
|
+
print(f"📊 Modules: {len(architecture)}")
|
|
226
|
+
print(f"🌐 Open {output_file} in your browser!")
|
|
227
|
+
|
|
@@ -5,6 +5,11 @@ version: 1.0.0
|
|
|
5
5
|
tags: [architecture, template, json]
|
|
6
6
|
language: json
|
|
7
7
|
output: architecture.json
|
|
8
|
+
post_process_python: ./pdd/render_mermaid.py
|
|
9
|
+
post_process_args:
|
|
10
|
+
- "{INPUT_FILE}"
|
|
11
|
+
- "{APP_NAME}"
|
|
12
|
+
- "{OUTPUT_HTML}"
|
|
8
13
|
variables:
|
|
9
14
|
APP_NAME:
|
|
10
15
|
required: false
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architecture/mermaid_diagram
|
|
3
|
+
description: Convert architecture.json to a Mermaid UML component diagram
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags: [architecture, visualization, mermaid, diagram]
|
|
6
|
+
language: markdown
|
|
7
|
+
output: architecture_diagram.md
|
|
8
|
+
variables:
|
|
9
|
+
ARCHITECTURE_JSON_FILE:
|
|
10
|
+
required: true
|
|
11
|
+
type: path
|
|
12
|
+
description: Path to the architecture.json file to visualize.
|
|
13
|
+
example_paths: [architecture.json, pdd/architecture.json]
|
|
14
|
+
APP_NAME:
|
|
15
|
+
required: false
|
|
16
|
+
type: string
|
|
17
|
+
description: Application name to display at the top of the diagram.
|
|
18
|
+
default: "System Architecture"
|
|
19
|
+
LAYOUT:
|
|
20
|
+
required: false
|
|
21
|
+
type: string
|
|
22
|
+
description: Mermaid diagram layout direction (TB=top-bottom, LR=left-right).
|
|
23
|
+
default: "TB"
|
|
24
|
+
usage:
|
|
25
|
+
generate:
|
|
26
|
+
- name: Basic
|
|
27
|
+
command: pdd generate -e ARCHITECTURE_JSON_FILE=architecture.json --output diagram.md pdd/templates/architecture/mermaid_diagram.prompt
|
|
28
|
+
---
|
|
29
|
+
You are a software architecture visualizer. Generate a Mermaid UML component diagram from the provided architecture.json.
|
|
30
|
+
<architecture_json>
|
|
31
|
+
<include>{ARCHITECTURE_JSON_FILE}</include>
|
|
32
|
+
</architecture_json>
|
|
33
|
+
Create a flowchart {LAYOUT} diagram with:
|
|
34
|
+
1. Top node: "{APP_NAME}"
|
|
35
|
+
2. Subgraphs: Frontend (tags: frontend, react, nextjs, ui), Backend (tags: backend, api, database), Shared (tags: utils, shared)
|
|
36
|
+
3. Module labels: filename (priority)
|
|
37
|
+
4. Dependencies: arrows labeled "uses"
|
|
38
|
+
5. Colors: Frontend=#FFF3E0, Backend=#E3F2FD, Shared=#E8F5E9, System=#E0E0E0
|
|
39
|
+
Output complete Mermaid markdown with diagram and module details table.
|
|
40
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pdd-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.65
|
|
4
4
|
Summary: PDD (Prompt-Driven Development) Command Line Interface
|
|
5
5
|
Author: Greg Tanaka
|
|
6
6
|
Author-email: glt@alumni.caltech.edu
|
|
@@ -53,7 +53,7 @@ Requires-Dist: build; extra == "dev"
|
|
|
53
53
|
Requires-Dist: twine; extra == "dev"
|
|
54
54
|
Dynamic: license-file
|
|
55
55
|
|
|
56
|
-
.. image:: https://img.shields.io/badge/pdd--cli-v0.0.
|
|
56
|
+
.. image:: https://img.shields.io/badge/pdd--cli-v0.0.65-blue
|
|
57
57
|
:alt: PDD-CLI Version
|
|
58
58
|
|
|
59
59
|
.. image:: https://img.shields.io/badge/Discord-join%20chat-7289DA.svg?logo=discord&logoColor=white&link=https://discord.gg/Yp4RTh8bG7
|
|
@@ -130,7 +130,7 @@ After installation, verify:
|
|
|
130
130
|
|
|
131
131
|
pdd --version
|
|
132
132
|
|
|
133
|
-
You'll see the current PDD version (e.g., 0.0.
|
|
133
|
+
You'll see the current PDD version (e.g., 0.0.65).
|
|
134
134
|
|
|
135
135
|
Getting Started with Examples
|
|
136
136
|
-----------------------------
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pdd/__init__.py,sha256=
|
|
1
|
+
pdd/__init__.py,sha256=mjlahZ8psYqW2LsFyznBE1QNqQyKJ99QuhlQodla-mY,633
|
|
2
2
|
pdd/auto_deps_main.py,sha256=cpP3bbzVL3jomrGinpzTxzIDIC8tmDDYOwUAC1TKRaw,3970
|
|
3
3
|
pdd/auto_include.py,sha256=OJcdcwTwJNqHPHKG9P4m9Ij-PiLex0EbuwJP0uiQi_Y,7484
|
|
4
4
|
pdd/auto_update.py,sha256=w6jzTnMiYRNpwQHQxWNiIAwQ0d6xh1iOB3xgDsabWtc,5236
|
|
@@ -9,7 +9,7 @@ pdd/change_main.py,sha256=04VHiO_D-jlfeRn6rrVH7ZTA5agXPoJGm1StGI8--XY,27804
|
|
|
9
9
|
pdd/cli.py,sha256=qjDBwwwE-sTWFqKTJOIiYh2nuimlTTgXtMDE0RUuVaU,60805
|
|
10
10
|
pdd/cmd_test_main.py,sha256=M-i5x26ORXurt_pu8x1sgLAyVIItbuRThiux4wBg3Ls,7768
|
|
11
11
|
pdd/code_generator.py,sha256=AxMRZKGIlLh9xWdn2FA6b3zSoZ-5TIZNIAzqjFboAQs,4718
|
|
12
|
-
pdd/code_generator_main.py,sha256=
|
|
12
|
+
pdd/code_generator_main.py,sha256=ysNetig_JV3XoBRy7bNh8eH-jQP5feB8P7qr1xUOSfs,54100
|
|
13
13
|
pdd/comment_line.py,sha256=sX2hf4bG1fILi_rvI9MkkwCZ2IitgKkW7nOiw8aQKPY,1845
|
|
14
14
|
pdd/conflicts_in_prompts.py,sha256=9N3rZWdJUGayOTOgnHW9G_Jm1C9G4Y8hSLhnURc1BkY,4890
|
|
15
15
|
pdd/conflicts_main.py,sha256=SK8eljbAY_wWT4dieRSsQwBrU1Dm90MImry3AGL-Dj4,3704
|
|
@@ -55,6 +55,7 @@ pdd/preprocess_main.py,sha256=WGhOB9qEu7MmFoyXNml_AmqGii73LJWngx4kTlZ526k,3262
|
|
|
55
55
|
pdd/process_csv_change.py,sha256=ckNqVPRooWVyIvmqjdEgo2PDLnpoQ6Taa2dUaWGRlzU,27926
|
|
56
56
|
pdd/pytest_output.py,sha256=IrRKYneW_F6zv9WaJwKFGnOBLFBFjk1CnhO_EVAjb9E,6612
|
|
57
57
|
pdd/python_env_detector.py,sha256=y-QESoPNiKaD821uz8okX-9qA-oqvH9cQHY2_MwFHzU,5194
|
|
58
|
+
pdd/render_mermaid.py,sha256=_uKvqVD1ImSs4BvohqWDhbrAdj_DDTWZ1JCEDE7AeBo,8653
|
|
58
59
|
pdd/setup_tool.py,sha256=h3MW7yr5tSlreq3u26aUfQqFTneQ-wNCoq0vwIYxqMY,24087
|
|
59
60
|
pdd/split.py,sha256=9lWrh-JOjOpxRp4-s1VL7bqJMVWlsmY5LxONT7sYM8A,5288
|
|
60
61
|
pdd/split_main.py,sha256=52rcZoeS_wpYRiqbqMUgr_hUY7GS62otwzDfuAGi6YA,4845
|
|
@@ -73,7 +74,7 @@ pdd/update_model_costs.py,sha256=RfeOlAHtc1FCx47A7CjrH2t5WXQclQ_9uYtNjtQh75I,229
|
|
|
73
74
|
pdd/update_prompt.py,sha256=zc-HiI1cwGBkJHVmNDyoSZa13lZH90VdB9l8ajdj6Kk,4543
|
|
74
75
|
pdd/xml_tagger.py,sha256=5Bc3HRm7iz_XjBdzQIcMb8KocUQ8PELI2NN5Gw4amd4,4825
|
|
75
76
|
pdd/data/language_format.csv,sha256=7vrzwf84jHhEPSq_YNjHNtVBURBginiuN22ab3nGQl8,1038
|
|
76
|
-
pdd/data/llm_model.csv,sha256=
|
|
77
|
+
pdd/data/llm_model.csv,sha256=6QjpGHq5M-JyIHAK6YzpXZh-EtQwGOO1sWDKDEWhep8,1843
|
|
77
78
|
pdd/prompts/auto_include_LLM.prompt,sha256=sNF2rdJu9wJ8c0lwjCfZ9ZReX8zGXRUNehRs1ZiyDoc,12108
|
|
78
79
|
pdd/prompts/bug_to_unit_test_LLM.prompt,sha256=KdMkvRVnjVSf0NTYIaDXIMT93xPttXEwkMpjWx5leLs,1588
|
|
79
80
|
pdd/prompts/change_LLM.prompt,sha256=5rgWIL16p3VRURd2_lNtcbu_MVRqPhI8gFIBt1gkzDQ,2164
|
|
@@ -110,11 +111,12 @@ pdd/prompts/trim_results_start_LLM.prompt,sha256=OKz8fAf1cYWKWgslFOHEkUpfaUDARh3
|
|
|
110
111
|
pdd/prompts/unfinished_prompt_LLM.prompt,sha256=vud_G9PlVv9Ig64uBC-hPEVFRk5lwpc8pW6tOIxJM4I,5082
|
|
111
112
|
pdd/prompts/update_prompt_LLM.prompt,sha256=prIc8uLp2jqnLTHt6JvWDZGanPZipivhhYeXe0lVaYw,1328
|
|
112
113
|
pdd/prompts/xml_convertor_LLM.prompt,sha256=YGRGXJeg6EhM9690f-SKqQrKqSJjLFD51UrPOlO0Frg,2786
|
|
113
|
-
pdd/templates/architecture/architecture_json.prompt,sha256=
|
|
114
|
+
pdd/templates/architecture/architecture_json.prompt,sha256=Qein8clkG9Z9cF2vNeBi7g7THhuTA_zxRXZOeihq-sQ,10865
|
|
115
|
+
pdd/templates/architecture/mermaid_diagram.prompt,sha256=81lv1XJqdp7KfMGGt_EeRoUnHvbMg3SNhDxlQU4p2Z8,1549
|
|
114
116
|
pdd/templates/generic/generate_prompt.prompt,sha256=4PhcNczpYpwSiaGt0r2f-vhSO3JFqeU1fTEy6YpPudQ,10758
|
|
115
|
-
pdd_cli-0.0.
|
|
116
|
-
pdd_cli-0.0.
|
|
117
|
-
pdd_cli-0.0.
|
|
118
|
-
pdd_cli-0.0.
|
|
119
|
-
pdd_cli-0.0.
|
|
120
|
-
pdd_cli-0.0.
|
|
117
|
+
pdd_cli-0.0.65.dist-info/licenses/LICENSE,sha256=kvTJnnxPVTYlGKSY4ZN1kzdmJ0lxRdNWxgupaB27zsU,1066
|
|
118
|
+
pdd_cli-0.0.65.dist-info/METADATA,sha256=bVkGdVNityCJ8HO38uLp8WYMDjbGjkS-rO8ksUtFDME,12687
|
|
119
|
+
pdd_cli-0.0.65.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
120
|
+
pdd_cli-0.0.65.dist-info/entry_points.txt,sha256=Kr8HtNVb8uHZtQJNH4DnF8j7WNgWQbb7_Pw5hECSR-I,36
|
|
121
|
+
pdd_cli-0.0.65.dist-info/top_level.txt,sha256=xjnhIACeMcMeDfVNREgQZl4EbTni2T11QkL5r7E-sbE,4
|
|
122
|
+
pdd_cli-0.0.65.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|