code-puppy 0.0.50__tar.gz → 0.0.52__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.50 → code_puppy-0.0.52}/PKG-INFO +1 -1
  2. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/command_line/meta_command_handler.py +39 -2
  3. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/command_line/prompt_toolkit_completion.py +39 -1
  4. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/config.py +39 -9
  5. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/main.py +4 -0
  6. {code_puppy-0.0.50 → code_puppy-0.0.52}/pyproject.toml +1 -1
  7. {code_puppy-0.0.50 → code_puppy-0.0.52}/.gitignore +0 -0
  8. {code_puppy-0.0.50 → code_puppy-0.0.52}/LICENSE +0 -0
  9. {code_puppy-0.0.50 → code_puppy-0.0.52}/README.md +0 -0
  10. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/__init__.py +0 -0
  11. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/agent.py +0 -0
  12. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/agent_prompts.py +0 -0
  13. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/command_line/__init__.py +0 -0
  14. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/command_line/file_path_completion.py +0 -0
  15. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/command_line/model_picker_completion.py +0 -0
  16. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/command_line/utils.py +0 -0
  17. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/model_factory.py +0 -0
  18. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/models.json +0 -0
  19. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/session_memory.py +0 -0
  20. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/tools/__init__.py +0 -0
  21. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/tools/code_map.py +0 -0
  22. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/tools/command_runner.py +0 -0
  23. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/tools/common.py +0 -0
  24. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/tools/file_modifications.py +0 -0
  25. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/tools/file_operations.py +0 -0
  26. {code_puppy-0.0.50 → code_puppy-0.0.52}/code_puppy/tools/web_search.py +0 -0
  27. {code_puppy-0.0.50 → code_puppy-0.0.52}/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.50
3
+ Version: 0.0.52
4
4
  Summary: Code generation agent
5
5
  Author: Michael Pfaffenberger
6
6
  License: MIT
@@ -1,3 +1,13 @@
1
+ META_COMMANDS_HELP = '''
2
+ [bold magenta]Meta Commands Help[/bold magenta]
3
+ ~help, ~h Show this help message
4
+ ~cd [dir] Change directory or show directories
5
+ ~codemap [dir] Show code structure for [dir]
6
+ ~m <model> Set active model
7
+ ~show Show puppy status info
8
+ ~<unknown> Show unknown meta command warning
9
+ '''
10
+
1
11
  from code_puppy.command_line.model_picker_completion import update_model_in_input, load_model_names, get_active_model
2
12
  from rich.console import Console
3
13
  import os
@@ -47,7 +57,6 @@ def handle_meta_command(command: str, console: Console) -> bool:
47
57
  if command.strip().startswith("~show"):
48
58
  from code_puppy.config import get_puppy_name, get_owner_name
49
59
  from code_puppy.command_line.model_picker_completion import get_active_model
50
- import os
51
60
  puppy_name = get_puppy_name()
52
61
  owner_name = get_owner_name()
53
62
  model = get_active_model()
@@ -61,6 +70,34 @@ def handle_meta_command(command: str, console: Console) -> bool:
61
70
  ''')
62
71
  return True
63
72
 
73
+ if command.startswith("~set"):
74
+ # Syntax: ~set KEY=VALUE or ~set KEY VALUE
75
+ from code_puppy.config import set_config_value, get_config_keys
76
+ tokens = command.split(None, 2)
77
+ argstr = command[len('~set'):].strip()
78
+ key = None
79
+ value = None
80
+ if '=' in argstr:
81
+ key, value = argstr.split('=', 1)
82
+ key = key.strip()
83
+ value = value.strip()
84
+ elif len(tokens) >= 3:
85
+ key = tokens[1]
86
+ value = tokens[2]
87
+ elif len(tokens) == 2:
88
+ key = tokens[1]
89
+ value = ''
90
+ else:
91
+ console.print('[yellow]Usage:[/yellow] ~set KEY=VALUE or ~set KEY VALUE')
92
+ console.print('Config keys: ' + ', '.join(get_config_keys()))
93
+ return True
94
+ if key:
95
+ set_config_value(key, value)
96
+ console.print(f'[green]🌶 Set[/green] [cyan]{key}[/cyan] = "{value}" in puppy.cfg!')
97
+ else:
98
+ console.print('[red]You must supply a key.[/red]')
99
+ return True
100
+
64
101
  if command.startswith("~m"):
65
102
  # Try setting model and show confirmation
66
103
  new_input = update_model_in_input(command)
@@ -76,7 +113,7 @@ def handle_meta_command(command: str, console: Console) -> bool:
76
113
  console.print(f"[yellow]Usage:[/yellow] ~m <model_name>")
77
114
  return True
78
115
  if command in ("~help", "~h"):
79
- 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!)")
116
+ console.print(META_COMMANDS_HELP)
80
117
  return True
81
118
  if command.startswith("~"):
82
119
  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()
@@ -50,6 +50,31 @@ def get_puppy_name():
50
50
  def get_owner_name():
51
51
  return get_value("owner_name") or "Master"
52
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
+
53
78
  # --- MODEL STICKY EXTENSION STARTS HERE ---
54
79
  def get_model_name():
55
80
  """Returns the last used model name stored in config, or None if unset."""
@@ -66,20 +91,25 @@ def set_model_name(model: str):
66
91
  config.write(f)
67
92
 
68
93
  def get_yolo_mode():
69
- """Checks env var CODE_PUPPY_YOLO or puppy.cfg for 'yolo_mode'.
70
- Returns True if either is explicitly truthy, else False by default.
71
- Env var wins if both are set.
72
- Allowed env/cfg values: 1, '1', 'true', 'yes', 'on' (case-insensitive).
73
94
  """
74
- env_val = os.getenv('YOLO_MODE')
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
+ """
75
102
  true_vals = {'1', 'true', 'yes', 'on'}
76
- if env_val is not None:
77
- if str(env_val).strip().lower() in true_vals:
78
- return True
79
- return False
80
103
  cfg_val = get_value('yolo_mode')
81
104
  if cfg_val is not None:
82
105
  if str(cfg_val).strip().lower() in true_vals:
83
106
  return True
84
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
85
115
  return False
@@ -102,6 +102,10 @@ async def interactive_mode(history_file_path: str) -> None:
102
102
  console.print("Type 'clear' to reset the conversation history.")
103
103
  console.print("Type [bold blue]@[/bold blue] for path completion, or [bold blue]~m[/bold blue] to pick a model.")
104
104
 
105
+ # Show meta commands right at startup - DRY!
106
+ from code_puppy.command_line.meta_command_handler import META_COMMANDS_HELP
107
+ console.print(META_COMMANDS_HELP)
108
+
105
109
  # Check if prompt_toolkit is installed
106
110
  try:
107
111
  import prompt_toolkit
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "code-puppy"
7
- version = "0.0.50"
7
+ version = "0.0.52"
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