pdd-cli 0.0.90__py3-none-any.whl → 0.0.121__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.
Files changed (151) hide show
  1. pdd/__init__.py +38 -6
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +506 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +537 -0
  6. pdd/agentic_common.py +533 -770
  7. pdd/agentic_crash.py +2 -1
  8. pdd/agentic_e2e_fix.py +319 -0
  9. pdd/agentic_e2e_fix_orchestrator.py +582 -0
  10. pdd/agentic_fix.py +118 -3
  11. pdd/agentic_update.py +27 -9
  12. pdd/agentic_verify.py +3 -2
  13. pdd/architecture_sync.py +565 -0
  14. pdd/auth_service.py +210 -0
  15. pdd/auto_deps_main.py +63 -53
  16. pdd/auto_include.py +236 -3
  17. pdd/auto_update.py +125 -47
  18. pdd/bug_main.py +195 -23
  19. pdd/cmd_test_main.py +345 -197
  20. pdd/code_generator.py +4 -2
  21. pdd/code_generator_main.py +118 -32
  22. pdd/commands/__init__.py +6 -0
  23. pdd/commands/analysis.py +113 -48
  24. pdd/commands/auth.py +309 -0
  25. pdd/commands/connect.py +358 -0
  26. pdd/commands/fix.py +155 -114
  27. pdd/commands/generate.py +5 -0
  28. pdd/commands/maintenance.py +3 -2
  29. pdd/commands/misc.py +8 -0
  30. pdd/commands/modify.py +225 -163
  31. pdd/commands/sessions.py +284 -0
  32. pdd/commands/utility.py +12 -7
  33. pdd/construct_paths.py +334 -32
  34. pdd/context_generator_main.py +167 -170
  35. pdd/continue_generation.py +6 -3
  36. pdd/core/__init__.py +33 -0
  37. pdd/core/cli.py +44 -7
  38. pdd/core/cloud.py +237 -0
  39. pdd/core/dump.py +68 -20
  40. pdd/core/errors.py +4 -0
  41. pdd/core/remote_session.py +61 -0
  42. pdd/crash_main.py +219 -23
  43. pdd/data/llm_model.csv +4 -4
  44. pdd/docs/prompting_guide.md +864 -0
  45. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  46. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  47. pdd/fix_code_loop.py +208 -34
  48. pdd/fix_code_module_errors.py +6 -2
  49. pdd/fix_error_loop.py +291 -38
  50. pdd/fix_main.py +208 -6
  51. pdd/fix_verification_errors_loop.py +235 -26
  52. pdd/fix_verification_main.py +269 -83
  53. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  54. pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
  55. pdd/frontend/dist/index.html +376 -0
  56. pdd/frontend/dist/logo.svg +33 -0
  57. pdd/generate_output_paths.py +46 -5
  58. pdd/generate_test.py +212 -151
  59. pdd/get_comment.py +19 -44
  60. pdd/get_extension.py +8 -9
  61. pdd/get_jwt_token.py +309 -20
  62. pdd/get_language.py +8 -7
  63. pdd/get_run_command.py +7 -5
  64. pdd/insert_includes.py +2 -1
  65. pdd/llm_invoke.py +531 -97
  66. pdd/load_prompt_template.py +15 -34
  67. pdd/operation_log.py +342 -0
  68. pdd/path_resolution.py +140 -0
  69. pdd/postprocess.py +122 -97
  70. pdd/preprocess.py +68 -12
  71. pdd/preprocess_main.py +33 -1
  72. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  73. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  74. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  75. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  76. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  77. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  78. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  79. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  80. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  81. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  82. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  83. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  84. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
  85. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  86. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  87. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  88. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  89. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  90. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  91. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  92. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  93. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  94. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  95. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  96. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  97. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  98. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  99. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  100. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  101. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  102. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  103. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  104. pdd/prompts/agentic_update_LLM.prompt +192 -338
  105. pdd/prompts/auto_include_LLM.prompt +22 -0
  106. pdd/prompts/change_LLM.prompt +3093 -1
  107. pdd/prompts/detect_change_LLM.prompt +571 -14
  108. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  109. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  110. pdd/prompts/generate_test_LLM.prompt +19 -1
  111. pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
  112. pdd/prompts/insert_includes_LLM.prompt +262 -252
  113. pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
  114. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  115. pdd/remote_session.py +876 -0
  116. pdd/server/__init__.py +52 -0
  117. pdd/server/app.py +335 -0
  118. pdd/server/click_executor.py +587 -0
  119. pdd/server/executor.py +338 -0
  120. pdd/server/jobs.py +661 -0
  121. pdd/server/models.py +241 -0
  122. pdd/server/routes/__init__.py +31 -0
  123. pdd/server/routes/architecture.py +451 -0
  124. pdd/server/routes/auth.py +364 -0
  125. pdd/server/routes/commands.py +929 -0
  126. pdd/server/routes/config.py +42 -0
  127. pdd/server/routes/files.py +603 -0
  128. pdd/server/routes/prompts.py +1347 -0
  129. pdd/server/routes/websocket.py +473 -0
  130. pdd/server/security.py +243 -0
  131. pdd/server/terminal_spawner.py +217 -0
  132. pdd/server/token_counter.py +222 -0
  133. pdd/summarize_directory.py +236 -237
  134. pdd/sync_animation.py +8 -4
  135. pdd/sync_determine_operation.py +329 -47
  136. pdd/sync_main.py +272 -28
  137. pdd/sync_orchestration.py +289 -211
  138. pdd/sync_order.py +304 -0
  139. pdd/template_expander.py +161 -0
  140. pdd/templates/architecture/architecture_json.prompt +41 -46
  141. pdd/trace.py +1 -1
  142. pdd/track_cost.py +0 -13
  143. pdd/unfinished_prompt.py +2 -1
  144. pdd/update_main.py +68 -26
  145. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
  146. pdd_cli-0.0.121.dist-info/RECORD +229 -0
  147. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  148. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
  149. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
  150. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
  151. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,376 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/logo.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Prompt Driven Development</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script>
10
+ tailwind.config = {
11
+ theme: {
12
+ screens: {
13
+ 'xs': '475px',
14
+ 'sm': '640px',
15
+ 'md': '768px',
16
+ 'lg': '1024px',
17
+ 'xl': '1280px',
18
+ '2xl': '1536px',
19
+ },
20
+ extend: {
21
+ colors: {
22
+ surface: {
23
+ 50: '#f8fafc',
24
+ 100: '#f1f5f9',
25
+ 200: '#e2e8f0',
26
+ 300: '#cbd5e1',
27
+ 400: '#94a3b8',
28
+ 500: '#64748b',
29
+ 600: '#475569',
30
+ 700: '#334155',
31
+ 800: '#1e293b',
32
+ 900: '#0f172a',
33
+ 950: '#020617',
34
+ },
35
+ accent: {
36
+ 50: '#eff6ff',
37
+ 100: '#dbeafe',
38
+ 200: '#bfdbfe',
39
+ 300: '#93c5fd',
40
+ 400: '#60a5fa',
41
+ 500: '#3b82f6',
42
+ 600: '#2563eb',
43
+ 700: '#1d4ed8',
44
+ 800: '#1e40af',
45
+ 900: '#1e3a8a',
46
+ },
47
+ },
48
+ fontFamily: {
49
+ sans: ['Inter', 'system-ui', '-apple-system', 'sans-serif'],
50
+ mono: ['JetBrains Mono', 'Fira Code', 'ui-monospace', 'monospace'],
51
+ },
52
+ boxShadow: {
53
+ 'glow': '0 0 20px rgba(59, 130, 246, 0.3)',
54
+ 'glow-lg': '0 0 40px rgba(59, 130, 246, 0.4)',
55
+ 'inner-glow': 'inset 0 1px 0 rgba(255, 255, 255, 0.05)',
56
+ },
57
+ backdropBlur: {
58
+ xs: '2px',
59
+ },
60
+ animation: {
61
+ 'fade-in': 'fadeIn 0.3s ease-out',
62
+ 'slide-up': 'slideUp 0.3s ease-out',
63
+ 'slide-down': 'slideDown 0.2s ease-out',
64
+ 'scale-in': 'scaleIn 0.2s ease-out',
65
+ 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
66
+ 'slide-in-right': 'slideInRight 0.25s ease-out',
67
+ },
68
+ textColor: {
69
+ text: {
70
+ primary: '#f1f5f9',
71
+ secondary: '#94a3b8',
72
+ muted: '#64748b',
73
+ },
74
+ },
75
+ },
76
+ },
77
+ }
78
+ </script>
79
+ <link rel="preconnect" href="https://fonts.googleapis.com">
80
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
81
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
82
+ <style>
83
+ * {
84
+ -webkit-font-smoothing: antialiased;
85
+ -moz-osx-font-smoothing: grayscale;
86
+ }
87
+
88
+ body {
89
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
90
+ }
91
+
92
+ /* Custom scrollbar - Webkit (Chrome, Safari, Edge) */
93
+ ::-webkit-scrollbar {
94
+ width: 10px;
95
+ height: 10px;
96
+ }
97
+ ::-webkit-scrollbar-track {
98
+ background: #1e293b;
99
+ border-radius: 5px;
100
+ }
101
+ ::-webkit-scrollbar-thumb {
102
+ background: #475569;
103
+ border-radius: 5px;
104
+ border: 2px solid #1e293b;
105
+ }
106
+ ::-webkit-scrollbar-thumb:hover {
107
+ background: #64748b;
108
+ }
109
+ ::-webkit-scrollbar-corner {
110
+ background: #1e293b;
111
+ }
112
+
113
+ /* Firefox scrollbar */
114
+ * {
115
+ scrollbar-width: thin;
116
+ scrollbar-color: #475569 #1e293b;
117
+ }
118
+
119
+ /* Animations */
120
+ @keyframes fadeIn {
121
+ from { opacity: 0; transform: translateY(-10px); }
122
+ to { opacity: 1; transform: translateY(0); }
123
+ }
124
+ @keyframes slideUp {
125
+ from { opacity: 0; transform: translateY(20px); }
126
+ to { opacity: 1; transform: translateY(0); }
127
+ }
128
+ @keyframes slideDown {
129
+ from { opacity: 0; transform: translateY(-10px); }
130
+ to { opacity: 1; transform: translateY(0); }
131
+ }
132
+ @keyframes scaleIn {
133
+ from { opacity: 0; transform: scale(0.95); }
134
+ to { opacity: 1; transform: scale(1); }
135
+ }
136
+ @keyframes slideInRight {
137
+ from { opacity: 0; transform: translateX(20px); }
138
+ to { opacity: 1; transform: translateX(0); }
139
+ }
140
+ @keyframes indeterminate {
141
+ 0% { transform: translateX(-100%); }
142
+ 100% { transform: translateX(300%); }
143
+ }
144
+
145
+ .animate-fade-in { animation: fadeIn 0.3s ease-out; }
146
+ .animate-indeterminate { animation: indeterminate 1.5s ease-in-out infinite; }
147
+ .animate-slide-up { animation: slideUp 0.3s ease-out; }
148
+ .animate-slide-down { animation: slideDown 0.2s ease-out; }
149
+ .animate-scale-in { animation: scaleIn 0.2s ease-out; }
150
+ .animate-slide-in-right { animation: slideInRight 0.25s ease-out; }
151
+
152
+ /* Glass morphism utility */
153
+ .glass {
154
+ background: rgba(30, 41, 59, 0.7);
155
+ backdrop-filter: blur(12px);
156
+ -webkit-backdrop-filter: blur(12px);
157
+ }
158
+ .glass-light {
159
+ background: rgba(51, 65, 85, 0.5);
160
+ backdrop-filter: blur(8px);
161
+ -webkit-backdrop-filter: blur(8px);
162
+ }
163
+
164
+ /* Gradient text utility */
165
+ .text-gradient {
166
+ background: linear-gradient(135deg, #60a5fa, #a78bfa);
167
+ -webkit-background-clip: text;
168
+ -webkit-text-fill-color: transparent;
169
+ background-clip: text;
170
+ }
171
+
172
+ /* Focus ring */
173
+ .focus-ring:focus {
174
+ outline: none;
175
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
176
+ }
177
+
178
+ /* Card hover effect */
179
+ .card-hover {
180
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
181
+ }
182
+ .card-hover:hover {
183
+ transform: translateY(-2px);
184
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
185
+ }
186
+
187
+ /* Markdown rendering styles */
188
+ .markdown-body {
189
+ color: #e2e8f0;
190
+ line-height: 1.7;
191
+ font-size: 15px;
192
+ }
193
+ .markdown-body h1, .markdown-body h2, .markdown-body h3,
194
+ .markdown-body h4, .markdown-body h5, .markdown-body h6 {
195
+ color: #f1f5f9;
196
+ font-weight: 600;
197
+ margin-top: 1.5em;
198
+ margin-bottom: 0.75em;
199
+ line-height: 1.3;
200
+ }
201
+ .markdown-body h1 { font-size: 2em; border-bottom: 1px solid #334155; padding-bottom: 0.3em; }
202
+ .markdown-body h2 { font-size: 1.5em; border-bottom: 1px solid #334155; padding-bottom: 0.3em; }
203
+ .markdown-body h3 { font-size: 1.25em; }
204
+ .markdown-body h4 { font-size: 1.1em; }
205
+ .markdown-body p { margin-bottom: 1em; }
206
+ .markdown-body ul, .markdown-body ol {
207
+ padding-left: 1.5em;
208
+ margin-bottom: 1em;
209
+ }
210
+ .markdown-body li { margin-bottom: 0.35em; }
211
+ .markdown-body ul { list-style-type: disc; }
212
+ .markdown-body ol { list-style-type: decimal; }
213
+ .markdown-body li > ul, .markdown-body li > ol { margin-top: 0.35em; margin-bottom: 0; }
214
+ .markdown-body code {
215
+ background: #1e293b;
216
+ padding: 0.2em 0.4em;
217
+ border-radius: 6px;
218
+ font-family: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
219
+ font-size: 0.9em;
220
+ color: #93c5fd;
221
+ }
222
+ .markdown-body pre {
223
+ background: #0f172a;
224
+ border: 1px solid #334155;
225
+ border-radius: 8px;
226
+ padding: 1em;
227
+ overflow-x: auto;
228
+ margin-bottom: 1em;
229
+ }
230
+ .markdown-body pre code {
231
+ background: transparent;
232
+ padding: 0;
233
+ border-radius: 0;
234
+ color: #e2e8f0;
235
+ font-size: 0.875em;
236
+ }
237
+ .markdown-body blockquote {
238
+ border-left: 4px solid #3b82f6;
239
+ padding-left: 1em;
240
+ margin-left: 0;
241
+ margin-bottom: 1em;
242
+ color: #94a3b8;
243
+ font-style: italic;
244
+ }
245
+ .markdown-body a {
246
+ color: #60a5fa;
247
+ text-decoration: none;
248
+ }
249
+ .markdown-body a:hover { text-decoration: underline; }
250
+ .markdown-body hr {
251
+ border: none;
252
+ border-top: 1px solid #334155;
253
+ margin: 2em 0;
254
+ }
255
+ .markdown-body table {
256
+ width: 100%;
257
+ border-collapse: collapse;
258
+ margin-bottom: 1em;
259
+ }
260
+ .markdown-body th, .markdown-body td {
261
+ border: 1px solid #334155;
262
+ padding: 0.5em 0.75em;
263
+ text-align: left;
264
+ }
265
+ .markdown-body th {
266
+ background: #1e293b;
267
+ font-weight: 600;
268
+ }
269
+ .markdown-body img {
270
+ max-width: 100%;
271
+ border-radius: 8px;
272
+ }
273
+ .markdown-body strong { color: #f1f5f9; font-weight: 600; }
274
+ .markdown-body em { color: #cbd5e1; }
275
+
276
+ /* ReactFlow edge selection styles */
277
+ .react-flow__edge.selected .react-flow__edge-path {
278
+ stroke: #f97316 !important;
279
+ stroke-width: 3px !important;
280
+ }
281
+ .react-flow__edge:hover .react-flow__edge-path {
282
+ stroke: #60a5fa !important;
283
+ stroke-width: 2.5px !important;
284
+ }
285
+ .react-flow__edge.selected:hover .react-flow__edge-path {
286
+ stroke: #fb923c !important;
287
+ stroke-width: 3px !important;
288
+ }
289
+
290
+ /* CodeMirror Autocomplete styles */
291
+ .cm-tooltip {
292
+ z-index: 1000 !important;
293
+ }
294
+ .cm-tooltip.cm-tooltip-autocomplete {
295
+ background: #1e293b !important;
296
+ border: 1px solid #334155 !important;
297
+ border-radius: 8px !important;
298
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4) !important;
299
+ overflow: hidden !important;
300
+ }
301
+ .cm-tooltip.cm-tooltip-autocomplete > ul {
302
+ font-family: inherit !important;
303
+ max-height: 280px !important;
304
+ padding: 4px 0 !important;
305
+ }
306
+ .cm-tooltip.cm-tooltip-autocomplete > ul > li {
307
+ padding: 8px 12px !important;
308
+ display: flex !important;
309
+ align-items: center !important;
310
+ gap: 8px !important;
311
+ cursor: pointer !important;
312
+ }
313
+ .cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] {
314
+ background: #3b82f6 !important;
315
+ color: white !important;
316
+ }
317
+ .cm-completionLabel {
318
+ color: #60a5fa !important;
319
+ font-weight: 500 !important;
320
+ }
321
+ .cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] .cm-completionLabel {
322
+ color: white !important;
323
+ }
324
+ .cm-completionDetail {
325
+ color: #94a3b8 !important;
326
+ font-size: 11px !important;
327
+ margin-left: auto !important;
328
+ font-style: normal !important;
329
+ }
330
+ .cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] .cm-completionDetail {
331
+ color: rgba(255, 255, 255, 0.8) !important;
332
+ }
333
+
334
+ /* Mobile responsive utilities */
335
+ /* Hide scrollbars utility */
336
+ .scrollbar-hide {
337
+ -ms-overflow-style: none;
338
+ scrollbar-width: none;
339
+ }
340
+ .scrollbar-hide::-webkit-scrollbar {
341
+ display: none;
342
+ }
343
+
344
+ /* Safe area insets for notched devices */
345
+ .safe-top {
346
+ padding-top: env(safe-area-inset-top);
347
+ }
348
+ .safe-bottom {
349
+ padding-bottom: env(safe-area-inset-bottom);
350
+ }
351
+
352
+ /* Prevent iOS zoom on input focus */
353
+ @media screen and (max-width: 640px) {
354
+ input, textarea, select {
355
+ font-size: 16px !important;
356
+ }
357
+ }
358
+ </style>
359
+
360
+ <script type="importmap">
361
+ {
362
+ "imports": {
363
+ "react-dom/": "https://aistudiocdn.com/react-dom@^19.2.0/",
364
+ "react/": "https://aistudiocdn.com/react@^19.2.0/",
365
+ "react": "https://aistudiocdn.com/react@^19.2.0",
366
+ "@google/genai": "https://aistudiocdn.com/@google/genai@^0.15.0"
367
+ }
368
+ }
369
+ </script>
370
+ <script type="module" crossorigin src="/assets/index-CUWd8al1.js"></script>
371
+ <link rel="stylesheet" crossorigin href="/assets/index-B5DZHykP.css">
372
+ </head>
373
+ <body class="bg-surface-950 text-gray-200">
374
+ <div id="root"></div>
375
+ </body>
376
+ </html>
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
3
+ <defs>
4
+ <filter id="glow" x="-60%" y="-60%" width="220%" height="220%">
5
+ <feGaussianBlur in="SourceGraphic" stdDeviation="40" result="blur"/>
6
+ <feMerge>
7
+ <feMergeNode in="blur"/>
8
+ <feMergeNode in="SourceGraphic"/>
9
+ </feMerge>
10
+ </filter>
11
+ </defs>
12
+
13
+ <g stroke="#00e3ff"
14
+ stroke-width="60"
15
+ stroke-linecap="round"
16
+ stroke-linejoin="round"
17
+ fill="none"
18
+ filter="url(#glow)">
19
+
20
+ <!-- speech-bubble "P" outline -->
21
+ <path d="
22
+ M 260 180
23
+ H 600
24
+ A 230 230 0 0 1 600 660
25
+ H 480
26
+ L 260 880
27
+ V 180
28
+ Z"/>
29
+
30
+ <!-- chevron shifted +15 px -->
31
+ <polyline points="505 340 585 420 505 500"/>
32
+ </g>
33
+ </svg>
@@ -156,17 +156,38 @@ CONTEXT_CONFIG_MAP: Dict[str, Dict[str, str]] = {
156
156
  # --- Helper Function ---
157
157
 
158
158
  def _get_default_filename(command: str, output_key: str, basename: str, language: str, file_extension: str) -> str:
159
- """Generates the default filename based on the command and output key."""
159
+ """Generates the default filename based on the command and output key.
160
+
161
+ Supports subdirectory basenames like 'core/cloud'. When the basename contains
162
+ a forward slash, the directory structure is preserved in the output:
163
+ - Directory part (e.g., 'core/') is prepended to the final filename
164
+ - Pattern is applied only to the name part (e.g., 'cloud')
165
+
166
+ Example: basename='core/cloud', pattern='test_{basename}{ext}'
167
+ Result: 'core/test_cloud.py' (NOT 'test_core/cloud.py')
168
+ """
160
169
  try:
170
+ # Split basename into directory and name components for subdirectory support
171
+ if '/' in basename:
172
+ dir_part, name_part = basename.rsplit('/', 1)
173
+ dir_prefix = dir_part + '/'
174
+ else:
175
+ dir_prefix = ''
176
+ name_part = basename
177
+
161
178
  pattern = DEFAULT_FILENAMES[command][output_key]
179
+
162
180
  # Use specific extension if in pattern, otherwise use language extension
163
181
  if '{ext}' in pattern:
164
- # Ensure file_extension starts with '.' if not empty
182
+ # Ensure file_extension starts with '.' if not empty
165
183
  effective_extension = file_extension if file_extension.startswith('.') or not file_extension else '.' + file_extension
166
- return pattern.format(basename=basename, language=language, ext=effective_extension)
184
+ filename = pattern.format(basename=name_part, language=language, ext=effective_extension)
167
185
  else:
168
186
  # Pattern already contains the full extension (e.g., .prompt, .log, .csv)
169
- return pattern.format(basename=basename, language=language) # ext might not be needed
187
+ filename = pattern.format(basename=name_part, language=language)
188
+
189
+ # Prepend directory part to preserve subdirectory structure
190
+ return dir_prefix + filename
170
191
  except KeyError:
171
192
  logger.error(f"Default filename pattern not found for command '{command}', output key '{output_key}'.")
172
193
  # Fallback or raise error - returning a basic fallback for now
@@ -311,6 +332,11 @@ def generate_output_paths(
311
332
  elif context_path:
312
333
  source = "context"
313
334
 
335
+ # Check if the ORIGINAL context path ends with / (explicit complete directory)
336
+ # When user configures "context/" or "backend/functions/utils/", they mean
337
+ # "put files directly here" - don't add dir_prefix from basename
338
+ original_context_path_ends_with_slash = context_path.endswith('/')
339
+
314
340
  # Resolve relative `.pddrc` paths based on path_resolution_mode.
315
341
  # "cwd" mode: resolve relative to current working directory (for sync)
316
342
  # "config_base" mode: resolve relative to config_base_dir (for fix, etc.)
@@ -335,7 +361,22 @@ def generate_output_paths(
335
361
 
336
362
  if is_dir:
337
363
  logger.debug(f"Context path '{context_path}' identified as a directory.")
338
- final_path = os.path.join(context_path, default_filename)
364
+ # When the config path explicitly ends with /, it's a complete directory
365
+ # Don't add dir_prefix - generate filename with just the name part
366
+ if original_context_path_ends_with_slash:
367
+ # Extract just the name part without dir_prefix
368
+ if '/' in basename:
369
+ _, name_part = basename.rsplit('/', 1)
370
+ else:
371
+ name_part = basename
372
+ # Generate filename without dir_prefix
373
+ filename_without_prefix = _get_default_filename(
374
+ command, output_key, name_part, language, file_extension
375
+ )
376
+ final_path = os.path.join(context_path, filename_without_prefix)
377
+ logger.debug(f"Using explicit directory without dir_prefix: {final_path}")
378
+ else:
379
+ final_path = os.path.join(context_path, default_filename)
339
380
  else:
340
381
  logger.debug(f"Context path '{context_path}' identified as a specific file path.")
341
382
  final_path = context_path