code-puppy 0.0.49__tar.gz → 0.0.51__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.49 → code_puppy-0.0.51}/PKG-INFO +1 -1
  2. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/command_line/meta_command_handler.py +45 -1
  3. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/command_line/prompt_toolkit_completion.py +39 -1
  4. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/config.py +49 -2
  5. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/tools/command_runner.py +2 -1
  6. {code_puppy-0.0.49 → code_puppy-0.0.51}/pyproject.toml +1 -1
  7. {code_puppy-0.0.49 → code_puppy-0.0.51}/.gitignore +0 -0
  8. {code_puppy-0.0.49 → code_puppy-0.0.51}/LICENSE +0 -0
  9. {code_puppy-0.0.49 → code_puppy-0.0.51}/README.md +0 -0
  10. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/__init__.py +0 -0
  11. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/agent.py +0 -0
  12. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/agent_prompts.py +0 -0
  13. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/command_line/__init__.py +0 -0
  14. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/command_line/file_path_completion.py +0 -0
  15. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/command_line/model_picker_completion.py +0 -0
  16. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/command_line/utils.py +0 -0
  17. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/main.py +0 -0
  18. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/model_factory.py +0 -0
  19. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/models.json +0 -0
  20. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/session_memory.py +0 -0
  21. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/tools/__init__.py +0 -0
  22. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/tools/code_map.py +0 -0
  23. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/tools/common.py +0 -0
  24. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/tools/file_modifications.py +0 -0
  25. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/tools/file_operations.py +0 -0
  26. {code_puppy-0.0.49 → code_puppy-0.0.51}/code_puppy/tools/web_search.py +0 -0
  27. {code_puppy-0.0.49 → code_puppy-0.0.51}/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.49
3
+ Version: 0.0.51
4
4
  Summary: Code generation agent
5
5
  Author: Michael Pfaffenberger
6
6
  License: MIT
@@ -44,6 +44,50 @@ def handle_meta_command(command: str, console: Console) -> bool:
44
44
  console.print(f'[red]Not a directory:[/red] [bold]{dirname}[/bold]')
45
45
  return True
46
46
 
47
+ if command.strip().startswith("~show"):
48
+ from code_puppy.config import get_puppy_name, get_owner_name
49
+ from code_puppy.command_line.model_picker_completion import get_active_model
50
+ puppy_name = get_puppy_name()
51
+ owner_name = get_owner_name()
52
+ model = get_active_model()
53
+ from code_puppy.config import get_yolo_mode
54
+ yolo_mode = get_yolo_mode()
55
+ console.print(f'''[bold magenta]🐶 Puppy Status[/bold magenta]
56
+ \n[bold]puppy_name:[/bold] [cyan]{puppy_name}[/cyan]
57
+ [bold]owner_name:[/bold] [cyan]{owner_name}[/cyan]
58
+ [bold]model:[/bold] [green]{model}[/green]
59
+ [bold]YOLO_MODE:[/bold] {'[red]ON[/red]' if yolo_mode else '[yellow]off[/yellow]'}
60
+ ''')
61
+ return True
62
+
63
+ if command.startswith("~set"):
64
+ # Syntax: ~set KEY=VALUE or ~set KEY VALUE
65
+ from code_puppy.config import set_config_value, get_config_keys
66
+ tokens = command.split(None, 2)
67
+ argstr = command[len('~set'):].strip()
68
+ key = None
69
+ value = None
70
+ if '=' in argstr:
71
+ key, value = argstr.split('=', 1)
72
+ key = key.strip()
73
+ value = value.strip()
74
+ elif len(tokens) >= 3:
75
+ key = tokens[1]
76
+ value = tokens[2]
77
+ elif len(tokens) == 2:
78
+ key = tokens[1]
79
+ value = ''
80
+ else:
81
+ console.print('[yellow]Usage:[/yellow] ~set KEY=VALUE or ~set KEY VALUE')
82
+ console.print('Config keys: ' + ', '.join(get_config_keys()))
83
+ return True
84
+ if key:
85
+ set_config_value(key, value)
86
+ console.print(f'[green]🌶 Set[/green] [cyan]{key}[/cyan] = "{value}" in puppy.cfg!')
87
+ else:
88
+ console.print('[red]You must supply a key.[/red]')
89
+ return True
90
+
47
91
  if command.startswith("~m"):
48
92
  # Try setting model and show confirmation
49
93
  new_input = update_model_in_input(command)
@@ -59,7 +103,7 @@ def handle_meta_command(command: str, console: Console) -> bool:
59
103
  console.print(f"[yellow]Usage:[/yellow] ~m <model_name>")
60
104
  return True
61
105
  if command in ("~help", "~h"):
62
- console.print("[bold magenta]Meta commands available:[/bold magenta]\n ~m <model>: Pick a model from your list!\n ~cd [dir]: Change directories\n ~codemap [dir]: Visualize project code structure\n ~help: Show this help\n (More soon. Woof!)")
106
+ console.print("[bold magenta]Meta commands available:[/bold magenta]\n ~m <model>: Pick a model from your list!\n ~cd [dir]: Change directories\n ~codemap [dir]: Visualize project code structure\n ~set KEY=VALUE: Set a puppy.cfg setting!\n ~help: Show this help\n (More soon. Woof!)")
63
107
  return True
64
108
  if command.startswith("~"):
65
109
  name = command[1:].split()[0] if len(command)>1 else ""
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  from code_puppy.command_line.utils import list_directory
3
- from code_puppy.config import get_puppy_name, get_owner_name
3
+ from code_puppy.config import get_puppy_name, get_owner_name, get_config_keys, get_value
4
4
  # ANSI color codes are no longer necessary because prompt_toolkit handles
5
5
  # styling via the `Style` class. We keep them here commented-out in case
6
6
  # someone needs raw ANSI later, but they are unused in the current code.
@@ -27,6 +27,43 @@ from code_puppy.command_line.file_path_completion import FilePathCompleter
27
27
 
28
28
  from prompt_toolkit.completion import Completer, Completion
29
29
 
30
+ class SetCompleter(Completer):
31
+ def __init__(self, trigger: str = '~set'):
32
+ self.trigger = trigger
33
+ def get_completions(self, document, complete_event):
34
+ text = document.text_before_cursor
35
+ if not text.strip().startswith(self.trigger):
36
+ return
37
+ # If the only thing typed is exactly '~set', suggest space
38
+ if text.strip() == self.trigger:
39
+ yield Completion(self.trigger + ' ', start_position=-len(self.trigger), display=f'{self.trigger} ', display_meta='set config')
40
+ tokens = text.strip().split()
41
+ # completion for the first arg after ~set
42
+ if len(tokens) == 1:
43
+ # user just typed ~set <-- suggest config keys
44
+ base = ''
45
+ else:
46
+ base = tokens[1]
47
+ # --- SPECIAL HANDLING FOR 'model' KEY ---
48
+ if base == 'model':
49
+ # Don't return any completions -- let ModelNameCompleter handle it
50
+ return
51
+ for key in get_config_keys():
52
+ if key == 'model':
53
+ continue # exclude 'model' from regular ~set completions
54
+ if key.startswith(base):
55
+ prev_value = get_value(key)
56
+ # Ensure there's a space after '~set' if it's the only thing typed
57
+ if text.strip() == self.trigger or text.strip() == self.trigger + '':
58
+ prefix = self.trigger + ' ' # Always enforce a space
59
+ insert_text = f'{prefix}{key} = {prev_value}' if prev_value is not None else f'{prefix}{key} = '
60
+ sp = -len(text)
61
+ else:
62
+ insert_text = f'{key} = {prev_value}' if prev_value is not None else f'{key} = '
63
+ sp = -len(base)
64
+ # Make it obvious the value part is from before
65
+ yield Completion(insert_text, start_position=sp, display_meta=f'puppy.cfg key (was: {prev_value})' if prev_value is not None else 'puppy.cfg key')
66
+
30
67
  class CDCompleter(Completer):
31
68
  def __init__(self, trigger: str = '~cd'):
32
69
  self.trigger = trigger
@@ -84,6 +121,7 @@ async def get_input_with_combined_completion(prompt_str = '>>> ', history_file:
84
121
  FilePathCompleter(symbol='@'),
85
122
  ModelNameCompleter(trigger='~m'),
86
123
  CDCompleter(trigger='~cd'),
124
+ SetCompleter(trigger='~set'),
87
125
  ])
88
126
  # Add custom key bindings for Alt+M to insert a new line without submitting
89
127
  bindings = KeyBindings()
@@ -7,7 +7,6 @@ CONFIG_FILE = os.path.join(CONFIG_DIR, "puppy.cfg")
7
7
  DEFAULT_SECTION = "puppy"
8
8
  REQUIRED_KEYS = ["puppy_name", "owner_name"]
9
9
 
10
-
11
10
  def ensure_config_exists():
12
11
  """
13
12
  Ensure that the .code_puppy dir and puppy.cfg exist, prompting if needed.
@@ -45,13 +44,37 @@ def get_value(key: str):
45
44
  val = config.get(DEFAULT_SECTION, key, fallback=None)
46
45
  return val
47
46
 
48
-
49
47
  def get_puppy_name():
50
48
  return get_value("puppy_name") or "Puppy"
51
49
 
52
50
  def get_owner_name():
53
51
  return get_value("owner_name") or "Master"
54
52
 
53
+ # --- CONFIG SETTER STARTS HERE ---
54
+ def get_config_keys():
55
+ '''
56
+ Returns the list of all config keys currently in puppy.cfg,
57
+ plus certain preset expected keys (e.g. "yolo_mode", "model").
58
+ '''
59
+ default_keys = ['yolo_mode', 'model']
60
+ config = configparser.ConfigParser()
61
+ config.read(CONFIG_FILE)
62
+ keys = set(config[DEFAULT_SECTION].keys()) if DEFAULT_SECTION in config else set()
63
+ keys.update(default_keys)
64
+ return sorted(keys)
65
+
66
+ def set_config_value(key: str, value: str):
67
+ '''
68
+ Sets a config value in the persistent config file.
69
+ '''
70
+ config = configparser.ConfigParser()
71
+ config.read(CONFIG_FILE)
72
+ if DEFAULT_SECTION not in config:
73
+ config[DEFAULT_SECTION] = {}
74
+ config[DEFAULT_SECTION][key] = value
75
+ with open(CONFIG_FILE, 'w') as f:
76
+ config.write(f)
77
+
55
78
  # --- MODEL STICKY EXTENSION STARTS HERE ---
56
79
  def get_model_name():
57
80
  """Returns the last used model name stored in config, or None if unset."""
@@ -66,3 +89,27 @@ def set_model_name(model: str):
66
89
  config[DEFAULT_SECTION]["model"] = model or ""
67
90
  with open(CONFIG_FILE, "w") as f:
68
91
  config.write(f)
92
+
93
+ def get_yolo_mode():
94
+ """
95
+ Checks puppy.cfg for 'yolo_mode' (case-insensitive in value only).
96
+ If not set, checks YOLO_MODE env var:
97
+ - If found in env, saves that value to puppy.cfg for future use.
98
+ - If neither present, defaults to False.
99
+ Allowed values for ON: 1, '1', 'true', 'yes', 'on' (all case-insensitive for value).
100
+ Always prioritizes the config once set!
101
+ """
102
+ true_vals = {'1', 'true', 'yes', 'on'}
103
+ cfg_val = get_value('yolo_mode')
104
+ if cfg_val is not None:
105
+ if str(cfg_val).strip().lower() in true_vals:
106
+ return True
107
+ return False
108
+ env_val = os.getenv('YOLO_MODE')
109
+ if env_val is not None:
110
+ # Persist the env value now
111
+ set_config_value('yolo_mode', env_val)
112
+ if str(env_val).strip().lower() in true_vals:
113
+ return True
114
+ return False
115
+ return False
@@ -19,7 +19,8 @@ def register_command_runner_tools(agent):
19
19
  if cwd:
20
20
  console.print(f"[dim]Working directory: {cwd}[/dim]")
21
21
  console.print("[dim]" + "-" * 60 + "[/dim]")
22
- yolo_mode = os.getenv("YOLO_MODE", "false").lower() == "true"
22
+ from code_puppy.config import get_yolo_mode
23
+ yolo_mode = get_yolo_mode()
23
24
  if not yolo_mode:
24
25
  user_input = input("Are you sure you want to run this command? (yes/no): ")
25
26
  if user_input.strip().lower() not in {"yes", "y"}:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "code-puppy"
7
- version = "0.0.49"
7
+ version = "0.0.51"
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