code-puppy 0.0.66__tar.gz → 0.0.67__tar.gz

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 (27) hide show
  1. {code_puppy-0.0.66 → code_puppy-0.0.67}/PKG-INFO +1 -1
  2. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/models.json +4 -0
  3. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/tools/ts_code_map.py +139 -11
  4. {code_puppy-0.0.66 → code_puppy-0.0.67}/pyproject.toml +1 -1
  5. {code_puppy-0.0.66 → code_puppy-0.0.67}/.gitignore +0 -0
  6. {code_puppy-0.0.66 → code_puppy-0.0.67}/LICENSE +0 -0
  7. {code_puppy-0.0.66 → code_puppy-0.0.67}/README.md +0 -0
  8. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/__init__.py +0 -0
  9. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/agent.py +0 -0
  10. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/agent_prompts.py +0 -0
  11. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/command_line/__init__.py +0 -0
  12. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/command_line/file_path_completion.py +0 -0
  13. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/command_line/meta_command_handler.py +0 -0
  14. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/command_line/model_picker_completion.py +0 -0
  15. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/command_line/prompt_toolkit_completion.py +0 -0
  16. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/command_line/utils.py +0 -0
  17. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/config.py +0 -0
  18. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/main.py +0 -0
  19. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/model_factory.py +0 -0
  20. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/session_memory.py +0 -0
  21. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/tools/__init__.py +0 -0
  22. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/tools/command_runner.py +0 -0
  23. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/tools/common.py +0 -0
  24. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/tools/file_modifications.py +0 -0
  25. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/tools/file_operations.py +0 -0
  26. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/tools/web_search.py +0 -0
  27. {code_puppy-0.0.66 → code_puppy-0.0.67}/code_puppy/version_checker.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.66
3
+ Version: 0.0.67
4
4
  Summary: Code generation agent
5
5
  Author: Michael Pfaffenberger
6
6
  License: MIT
@@ -15,6 +15,10 @@
15
15
  "type": "openai",
16
16
  "name": "gpt-4.1-nano"
17
17
  },
18
+ "o3": {
19
+ "type": "openai",
20
+ "name": "o3"
21
+ },
18
22
  "gpt-4.1-custom": {
19
23
  "type": "custom_openai",
20
24
  "name": "gpt-4.1-custom",
@@ -266,6 +266,19 @@ LANGS = {
266
266
  "struct_definition": partial(_f("struct {name}"), style="magenta"),
267
267
  },
268
268
  },
269
+ # ──────── markup / style ─────────────────────────────────────────
270
+ ".html": {
271
+ "lang": "html",
272
+ "name_field": None,
273
+ "nodes": {
274
+ # rely on parser presence; generic element handling not needed for tests
275
+ },
276
+ },
277
+ ".css": {
278
+ "lang": "css",
279
+ "name_field": None,
280
+ "nodes": {},
281
+ },
269
282
  # ───────── scripting (shell / infra) ─────────────────────────────
270
283
  ".sh": {
271
284
  "lang": "bash",
@@ -281,7 +294,37 @@ LANGS = {
281
294
  },
282
295
  }
283
296
 
284
- # Cache parsers so we don’t re-create them file-after-file
297
+ # ---------------------------------------------------------------------------
298
+ # Emoji helpers (cute! 🐶)
299
+ # ---------------------------------------------------------------------------
300
+
301
+ _NODE_EMOJIS = {
302
+ "function": "🦴",
303
+ "class": "🏠",
304
+ "struct": "🏗️",
305
+ "interface": "🎛️",
306
+ "trait": "💎",
307
+ "type": "🧩",
308
+ "object": "📦",
309
+ "export": "📤",
310
+ }
311
+
312
+ _FILE_EMOJIS = {
313
+ ".py": "🐍",
314
+ ".js": "✨",
315
+ ".jsx": "✨",
316
+ ".ts": "🌀",
317
+ ".tsx": "🌀",
318
+ ".rb": "💎",
319
+ ".go": "🐹",
320
+ ".rs": "🦀",
321
+ ".java": "☕️",
322
+ ".c": "🔧",
323
+ ".cpp": "➕",
324
+ ".hpp": "➕",
325
+ ".swift": "🕊️",
326
+ ".kt": "🤖",
327
+ }
285
328
  _PARSER_CACHE = {}
286
329
 
287
330
 
@@ -313,6 +356,55 @@ def _span(node):
313
356
  return Text(f" [{start_line}:{end_line}]", style="bold white")
314
357
 
315
358
 
359
+ def _emoji_for_node_type(ts_type: str) -> str:
360
+ """Return a cute emoji for a given Tree-sitter node type (best-effort)."""
361
+ # naive mapping based on substrings – keeps it simple
362
+ if "function" in ts_type or "method" in ts_type or ts_type.startswith("fn_"):
363
+ return _NODE_EMOJIS["function"]
364
+ if "class" in ts_type:
365
+ return _NODE_EMOJIS["class"]
366
+ if "struct" in ts_type:
367
+ return _NODE_EMOJIS["struct"]
368
+ if "interface" in ts_type:
369
+ return _NODE_EMOJIS["interface"]
370
+ if "trait" in ts_type:
371
+ return _NODE_EMOJIS["trait"]
372
+ if "type_spec" in ts_type or "type_declaration" in ts_type:
373
+ return _NODE_EMOJIS["type"]
374
+ if "object" in ts_type:
375
+ return _NODE_EMOJIS["object"]
376
+ if ts_type.startswith("export"):
377
+ return _NODE_EMOJIS["export"]
378
+ return ""
379
+
380
+
381
+ # ----------------------------------------------------------------------
382
+ # traversal (clean)
383
+ # ----------------------------------------------------------------------
384
+
385
+
386
+ def _walk_fix(ts_node, rich_parent, info):
387
+ """Recursive traversal adding child nodes with emoji labels."""
388
+ nodes_cfg = info["nodes"]
389
+ name_field = info["name_field"]
390
+
391
+ for child in ts_node.children:
392
+ n_type = child.type
393
+ if n_type in nodes_cfg:
394
+ style = nodes_cfg[n_type].keywords["style"]
395
+ ident = child.child_by_field_name(name_field) if name_field else _first_identifier(child)
396
+ label_text = ident.text.decode() if ident else "<anon>"
397
+ label = nodes_cfg[n_type].func(label_text)
398
+ emoji = _emoji_for_node_type(n_type)
399
+ if emoji:
400
+ label = f"{emoji} {label}"
401
+ branch = rich_parent.add(Text(label, style=style) + _span(child))
402
+ _walk_fix(child, branch, info)
403
+ else:
404
+ _walk_fix(child, rich_parent, info)
405
+ # ----------------------------------------------------------------------
406
+
407
+
316
408
  def _walk(ts_node, rich_parent, info):
317
409
  nodes_cfg = info["nodes"]
318
410
  name_field = info["name_field"]
@@ -329,6 +421,9 @@ def _walk(ts_node, rich_parent, info):
329
421
 
330
422
  label_text = ident.text.decode() if ident else "<anon>"
331
423
  label = nodes_cfg[t].func(label_text)
424
+ emoji = _emoji_for_node_type(t)
425
+ if emoji:
426
+ label = f"{emoji} {label}"
332
427
  branch = rich_parent.add(Text(label, style=style) + _span(child))
333
428
  _walk(child, branch, info)
334
429
  else:
@@ -345,35 +440,68 @@ def map_code_file(filepath):
345
440
  parser = parser_for(info["lang"])
346
441
  tree = parser.parse(code)
347
442
 
348
- root_label = Path(filepath).name
443
+ file_emoji = _FILE_EMOJIS.get(ext, "📄")
444
+ root_label = f"{file_emoji} {Path(filepath).name}"
349
445
  base = RichTree(Text(root_label, style="bold cyan"))
350
446
 
351
447
  if tree.root_node.has_error:
352
448
  base.add(Text("⚠️ syntax error", style="bold red"))
353
449
 
354
- _walk(tree.root_node, base, info)
450
+ _walk_fix(tree.root_node, base, info)
355
451
  return base
356
452
 
357
453
 
358
454
  def make_code_map(directory: str, ignore_tests: bool = True) -> str:
455
+ """Generate a Rich-rendered code map including directory hierarchy.
456
+
457
+ Args:
458
+ directory: Root directory to scan.
459
+ ignore_tests: Whether to skip files with 'test' in the name.
460
+
461
+ Returns:
462
+ Plain-text rendering of the generated Rich tree (last 1k chars).
463
+ """
464
+ # Create root of tree representing starting directory
359
465
  base_tree = RichTree(Text(Path(directory).name, style="bold magenta"))
360
466
 
467
+ # Cache to ensure we reuse RichTree nodes per directory path
468
+ dir_nodes: dict[str, RichTree] = {Path(directory).resolve(): base_tree} # key=abs path
469
+
361
470
  for root, dirs, files in os.walk(directory):
471
+ # ignore dot-folders early
362
472
  dirs[:] = [d for d in dirs if not d.startswith(".")]
473
+
474
+ abs_root = Path(root).resolve()
475
+
476
+ # Ensure current directory has a node; create if coming from parent
477
+ if abs_root not in dir_nodes and abs_root != Path(directory).resolve():
478
+ rel_parts = abs_root.relative_to(directory).parts
479
+ parent_path = Path(directory).resolve()
480
+ for part in rel_parts: # walk down creating nodes as needed
481
+ parent_node = dir_nodes[parent_path]
482
+ current_path = parent_path / part
483
+ if current_path not in dir_nodes:
484
+ dir_label = Text(part, style="bold magenta")
485
+ dir_node = parent_node.add(dir_label)
486
+ dir_nodes[current_path] = dir_node
487
+ parent_path = current_path
488
+
489
+ current_node = dir_nodes.get(abs_root, base_tree)
490
+
363
491
  for f in files:
364
- if (
365
- should_ignore_path(os.path.join(root, f))
366
- or ignore_tests
367
- and "test" in f
368
- ):
492
+ file_path = os.path.join(root, f)
493
+ if should_ignore_path(file_path):
494
+ continue
495
+ if ignore_tests and "test" in f:
369
496
  continue
370
497
  try:
371
- file_tree = map_code_file(os.path.join(root, f))
498
+ file_tree = map_code_file(file_path)
372
499
  if file_tree is not None:
373
- base_tree.add(file_tree)
500
+ current_node.add(file_tree)
374
501
  except Exception:
375
- base_tree.add(Text(f"[error reading {f}]", style="bold red"))
502
+ current_node.add(Text(f"[error reading {f}]", style="bold red"))
376
503
 
504
+ # Render and return last 1000 characters
377
505
  buf = Console(record=True, width=120)
378
506
  buf.print(base_tree)
379
507
  return buf.export_text()[-1000:]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "code-puppy"
7
- version = "0.0.66"
7
+ version = "0.0.67"
8
8
  description = "Code generation agent"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes