Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pip install -e .

### Testing

Agents should not write tests or run tests unless explicitly asked to do so by the user.

To run the tests, use `pytest`:

```bash
Expand Down
2 changes: 1 addition & 1 deletion src/jrdev/agents/pipeline/plan_phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async def request_step_plan(self, files_to_send: List[str], user_task: str) -> s
self.agent.worker_id, update={"new_sub_task": sub_task_str, "description": "create plan"}
)

response = await generate_llm_response(self.app, model, messages, task_id=sub_task_str)
response = await generate_llm_response(self.app, model, messages, task_id=sub_task_str, json_output=True)

# mark sub_task complete
if self.agent.worker_id:
Expand Down
4 changes: 2 additions & 2 deletions src/jrdev/commands/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async def handle_help(app: Any, args: List[str], _worker_id: str):
)
app.ui.print_text(f" {cmd_format}/models{reset} - List all available models", print_type=None)
app.ui.print_text(
f" {cmd_format}{format_command_with_args('/modelprofile', '<list|get|set|default|showdefault>')}"
f" {cmd_format}{format_command_with_args('/modelprofile', '<list|get|set|setall|default|showdefault>')}"
f"{reset} - Manage model profiles for different task types",
print_type=None,
)
Expand Down Expand Up @@ -220,7 +220,7 @@ async def handle_help_plain(app: Any, _args: List[str]):
)
app.ui.print_text(" /models - List all available models", print_type=None)
app.ui.print_text(
" /modelprofile <list|get|set|default|showdefault> - Manage model profiles for different task types",
" /modelprofile <list|get|set|setall|default|showdefault> - Manage model profiles for different task types",
print_type=None,
)
app.ui.print_text(" /init - Index important project files and familiarize LLM with project", print_type=None)
Expand Down
25 changes: 24 additions & 1 deletion src/jrdev/commands/modelprofile.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ async def handle_modelprofile(app: Any, args: List[str], _worker_id: str) -> Non
/modelprofile list - Shows all profiles and their assigned models.
/modelprofile get <profile_name> - Shows the model assigned to a specific profile.
/modelprofile set <profile_name> <model_name> - Sets a profile to use a specific model.
/modelprofile setall <model_name> - Sets every profile to use a specific model.
/modelprofile default <profile_name> - Sets the default profile for general tasks.
/modelprofile showdefault - Shows the current default profile.
"""
Expand All @@ -47,6 +48,7 @@ async def handle_modelprofile(app: Any, args: List[str], _worker_id: str) -> Non
/modelprofile list - Show all profiles and their assigned models
/modelprofile get [profile] - Show the model assigned to a profile
/modelprofile set [profile] [model] - Set a profile to use a specific model
/modelprofile setall [model] - Set every profile to use a specific model
/modelprofile default [profile] - Set the default profile
/modelprofile showdefault - Show the current default profile
"""
Expand Down Expand Up @@ -102,6 +104,25 @@ def _handle_set(app: Any, args: List[str], manager: Any) -> None:
app.ui.model_list_updated()


def _handle_setall(app: Any, args: List[str], manager: Any) -> None:
# Set every profile to use the same model
if len(args) < 3:
app.ui.print_text(
"Missing arguments. Usage: /modelprofile setall [model]",
PrintType.ERROR,
)
return

model = args[2]
success = manager.update_all_profiles(model, app.state.model_list)

if not success:
app.ui.print_text("Failed to update all profiles", PrintType.ERROR)
else:
app.ui.print_text(f"Updated all profiles to use model: {model}")
app.ui.model_list_updated()


async def _handle_subcommand(app: Any, subcommand: str, args: List[str], manager: Any) -> None:
"""Returns true if handled"""
if subcommand == "list":
Expand All @@ -111,6 +132,8 @@ async def _handle_subcommand(app: Any, subcommand: str, args: List[str], manager
_handle_get(app, args, manager)
elif subcommand == "set":
_handle_set(app, args, manager)
elif subcommand == "setall":
_handle_setall(app, args, manager)
elif subcommand == "default":
# Set the default profile
if len(args) < 3:
Expand All @@ -137,7 +160,7 @@ async def _handle_subcommand(app: Any, subcommand: str, args: List[str], manager
else:
app.ui.print_text(f"Unknown subcommand: {subcommand}", PrintType.ERROR)
app.ui.print_text(
"Available subcommands: list, get, set, default, showdefault",
"Available subcommands: list, get, set, setall, default, showdefault",
PrintType.INFO,
)

Expand Down
42 changes: 42 additions & 0 deletions src/jrdev/models/model_profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,48 @@ def update_profile(self, profile_type: str, model_name: str, model_list: Optiona
logger.error(f"Error updating profile: {str(e)}")
return False

def update_all_profiles(self, model_name: str, model_list: Optional[ModelList] = None) -> bool:
"""
Update every profile to use the same model.

Args:
model_name: The model name to assign to every profile
model_list: Optional ModelList instance for validation

Returns:
True if update successful, False otherwise
"""
if model_list is None:
model_list = ModelList()

if not model_name:
logger.error("Invalid model name")
return False

if not model_list.validate_model_exists(model_name):
for profile_model in self.profiles["profiles"].values():
if model_name == profile_model:
logger.info(f"Accepting model '{model_name}' which exists in profiles")
break
else:
logger.error(f"Model '{model_name}' does not exist in available models. Options:")
for model in model_list.get_model_list():
logger.error(f"{model}")
return False

try:
for profile_type in self.profiles["profiles"]:
self.profiles["profiles"][profile_type] = model_name

if write_json_file(self.config_path, self.profiles):
logger.info(f"Updated all profiles to use model '{model_name}'")
return True
return False

except Exception as e:
logger.error(f"Error updating all profiles: {str(e)}")
return False

def list_available_profiles(self) -> Dict[str, str]:
"""
Return all available profile:model mappings.
Expand Down
50 changes: 27 additions & 23 deletions src/jrdev/prompts/code/create_steps.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
Instructions:
You are a professor of computer science, currently teaching a basic CS1000 course to some new students with
little experience programming. The requested task is one that will be given to the students.
CRITICAL: Do not provide any code for the students, only textual aide.
Generate a discrete implementation plan for the requested task. The plan is for a beginning programming student, so it must be textual guidance only and must not include source code.

**Instruction 1**:Generate a list of discrete steps. The plan must be formatted as a numbered list where each step corresponds to a single operation (DELETE or WRITE). Use only one step per file. There should only be one step for each file. Each step should be self-contained and include:
Return only one JSON object. Do not include markdown fences, prose, analysis, comments, or any text before or after the JSON object.

- The operation type.
- Filename
- The target location or reference (such as a function name, marker, or global scope).
- A description of the intended change. The description should include a brief explanation of the reason this change is being made.
The JSON object must have this shape:

Ensure that a student can follow each step independently. Provide only the plan in your response, with no
additional commentary or extraneous information. Some tasks for the students may be doable in a single step.
CRITICAL: Writing must be in a neutral, observer-style exposition that avoids any references to speakers or listeners.
{
"steps": [
{
"operation_type": "WRITE",
"filename": "path/to/file",
"target_location": "function name, marker, or global scope",
"description": "Neutral observer-style description of the intended change and why it is needed."
}
],
"use_context": ["path/to/file"]
}

**Instruction 2**:Generate a list of context files needed to complete the steps. The list should only include file paths that you currently have in your context.
Rules for `steps`:
- Use only one step per file.
- Use `WRITE` for any change to an existing file or for creating a file.
- Use `DELETE` only when removing a file or code element completely.
- Each step must be self-contained and independently actionable.
- The description must avoid references to speakers or listeners.
- Some tasks may only need a single step.

- Include a file that will be altered, deleted, or otherwise changed.
- Include a file that is related to any of the tasks and provides beneficial information about the task -- including information about modules, libraries, dependencies, templates, functions, globals, etc that will be used in the task.
- Include a file if the user specifically mentioned it.
- Include a file if seeing the patterns used in it could be generally helpful.
- Do not include a file if it is generally unrelated, not helpful, and may be an overall distraction to the student.

The response should be in json format example: {"steps": [{"operation_type": "WRITE", "filename": "src/test_file.py", "target_location": "after function X scope end", "description": "Adjust the code so that it prints hello world"}], "use_context": ["path/file.txt", "path2/file2.md"]}

Operation Type User Guide:
WRITE: Every change to existing code requires a full rewrite of the file.
DELETE: Use when removing code elements completely
Rules for `use_context`:
- Include every file that will be altered, deleted, or otherwise changed.
- Include files that provide useful patterns, related modules, dependencies, templates, functions, or globals for the task.
- Include files specifically mentioned by the user.
- Include only file paths currently available in the provided context.
- Exclude unrelated files that would distract from the task.
56 changes: 34 additions & 22 deletions src/jrdev/prompts/router/salvage_response.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
**INSTRUCTIONS**: Parse the included message. It is a malformed response with JSON that failed to parse. Salvage this response by formatting it in the following format:
**INSTRUCTIONS**: Parse the included message. It is a malformed response with JSON that failed to parse. Salvage this response by returning a valid JSON object with these fields:

- `decision`: one of `execute_action`, `clarify`, `chat`, or `summary`
- `reasoning`: always required
- `action`: required only for `execute_action`, with `type`, `name`, and `args`
- `final_command`: required only for `execute_action`; false for tools, true for commands
- `question`: required only for `clarify`
- `response`: required only for `chat` and `summary`

Example:
```json
{
decision: "execute_action" | "clarify" | "chat" | "summary",
reasoning: string, // Always required - explain your decision

// For execute_action only:
action?: {
type: "tool" | "command",
name: string,
args: string[]
"decision": "execute_action",
"reasoning": "The request requires a command.",
"action": {
"type": "command",
"name": "command_or_tool_name",
"args": []
},
final_action?: boolean, // false for tools, true for commands

// For clarify only:
question?: string,

// For chat/summary only:
response?: string
"final_command": true
}
```

**CRITICAL**
- Your response must begin with ```json and it must end with ```
- The must be no text or characters between ```json and the json object. Likewise, there must be no characters after the JSON object ends and the ```.
- There must be no comments included within the JSON object or anywhere else.
- You must not alter the text in anyway. You are only able to alter formatting.
- If a field in the object is missing, include the key and use a blank - parsable value. For example `"question": ""`.
**CRITICAL RULES**
- Your response must begin with ```json and end with ```
- There must be no text or characters between ```json and the JSON object, or after the JSON object ends and before ```
- There must be no comments included within the JSON object or anywhere else
- You must not alter the semantic meaning of the content - only fix formatting issues
- Common failure patterns to fix:
- Missing closing quotes on string fields, especially `response`
- Missing closing backticks in code blocks within strings
- Missing closing braces for the JSON object
- Unescaped backslashes in strings, such as `\n` when it should be `\\n`
- Missing commas between fields
- If a field is missing, include the key with a blank parsable value, such as `"question": ""`
- Preserve all original content exactly as written, only fixing structural JSON issues
- Special attention: When the `response` field contains markdown or code blocks, ensure:
- All backticks are properly closed
- The entire markdown content is properly quoted
- No unescaped special characters break the JSON string
45 changes: 21 additions & 24 deletions src/jrdev/prompts/router/select_command.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@ You must make decisions in this hierarchical order:
- If requires system interaction → continue to step 3

3. **Check Information Availability**
- If missing critical information → `execute_action` with tool (`final_action: false`)
- If missing critical information → `execute_action` with tool (`final_command: false`)
- If have all needed information → continue to step 4

4. **Execute Final Action**
- `execute_action` with command (`final_action: true`)
- `execute_action` with command (`final_command: true`)
- Follow up with `summary` to present results

## Available Actions

### Information Gathering Tools (`final_action: false`)
### Information Gathering Tools (`final_command: false`)
tools_list

### Execution Commands (`final_action: true`)
### Execution Commands (`final_command: true`)
commands_list

## Critical Rules

1. **NEVER guess file paths** - always verify with tools first
2. **NEVER use multiple commands in one response** - one decision per response
3. **ALWAYS set `final_action: false`** when gathering information
3. **ALWAYS set `final_command: false`** when gathering information
4. **ALWAYS provide reasoning** for your decision
5. **PREFER specific questions** in clarify responses
6. **IGNORE commands marked "Router:Ignore"** in the available commands list
Expand Down Expand Up @@ -63,25 +63,22 @@ When multiple decisions could apply, use this priority:

```json
{
decision: "execute_action" | "clarify" | "chat" | "summary",
reasoning: string, // Always required - explain your decision

// For execute_action only:
action?: {
type: "tool" | "command",
name: string,
args: string[]
"decision": "execute_action",
"reasoning": "Explain why this action is needed.",
"action": {
"type": "tool",
"name": "tool_name",
"args": []
},
final_action?: boolean, // false for tools, true for commands

// For clarify only:
question?: string,

// For chat/summary only:
response?: string
"final_command": false
}
```

Valid `decision` values are `execute_action`, `clarify`, `chat`, and `summary`.
For `execute_action`, include `action` and `final_command`.
For `clarify`, include `question`.
For `chat` or `summary`, include `response`.

## Example Workflows

### Scenario 1: "Add error handling to the main function"
Expand All @@ -95,7 +92,7 @@ When multiple decisions could apply, use this priority:
"name": "read_files",
"args": ["main.py"]
},
"final_action": false
"final_command": false
}
```

Expand All @@ -109,7 +106,7 @@ When multiple decisions could apply, use this priority:
"name": "/code",
"args": ["Add try-catch error handling to the main function in main.py"]
},
"final_action": true
"final_command": true
}
```

Expand All @@ -123,7 +120,7 @@ When multiple decisions could apply, use this priority:
"name": "/init",
"args": []
},
"final_action": true
"final_command": true
}
```

Expand Down Expand Up @@ -163,4 +160,4 @@ When multiple decisions could apply, use this priority:

---

Analyze the user's request based on the available tools and commands provided below. Be precise and follow the decision process outlined above.
Analyze the user's request based on the available tools and commands provided below. Be precise and follow the decision process outlined above.
Loading
Loading