Skip to content

feat: seed default templates from detected tools#24

Open
jellydn wants to merge 12 commits into
mainfrom
feat/default-template
Open

feat: seed default templates from detected tools#24
jellydn wants to merge 12 commits into
mainfrom
feat/default-template

Conversation

@jellydn

@jellydn jellydn commented Feb 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • Generate default templates based on tools detected on the user’s machine
  • Wire detection into config initialization so first-run config isn’t empty
  • Keep tool-specific prompt commands consistent across providers

What changed

  • Build default templates from category prompts + preferred tool order
  • Use detectInstalledTools before loadConfig to seed templates on first run
  • Keep templates aligned with each tool’s prompt flags/commands

Why

New users currently start with an empty config, which adds setup friction. Seeding templates from detected tools gives immediate, useful workflows without manual configuration.

How

  1. Detect installed tools at startup
  2. Pass detected tools into loadConfig
  3. Create one template per category using the best available tool

Test plan

  • Run with no existing ~/.config/ai-switcher/config.json
  • Verify templates are generated for available tools
  • Confirm existing config still overrides defaults

Important

This PR adds dynamic template generation based on detected tools, enhancing initial configuration with pre-configured workflows.

  • Behavior:
    • Default templates are generated based on detected tools using buildDefaultTemplates() in config.ts.
    • loadConfig() in config.ts now seeds templates on first run by detecting installed tools.
    • Templates align with each tool's prompt flags/commands.
  • Functions:
    • Adds buildDefaultTemplates(), pickTool(), and buildCommandForTool() in config.ts to support dynamic template generation.
    • Modifies loadConfig() in config.ts to accept detected tools and create default config with templates.
  • Misc:
    • Updates main() in index.ts to pass detected tools to loadConfig() and handle tool selection and execution.

This description was created by Ellipsis for a73cf1e. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • Default templates are now generated dynamically from detected tools.
    • App will create and populate configuration automatically when none exists.
  • Improvements

    • Centralized, safer command parsing when launching tools.
    • Enhanced command validation with placeholder checks and escaping.
    • Clearer, earlier error reporting for invalid templates and missing descriptions.

Copilot AI and others added 8 commits February 1, 2026 01:31
Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>
Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>
Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>
Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>
Change the codex prompt command from "codex" to "codex exec" to align with the correct CLI invocation for the OpenAI Codex tool.
- Add 8 pre-configured templates for common AI coding tasks
- Include templates for code review, commit generation, architecture explanation, PR creation, type safety, testing, documentation, and code explanation
- Templates support multiple aliases for easier access
- Improve out-of-box experience with ready-to-use AI workflows
- Replace hardcoded templates with dynamic generation from detected tools
- Add category-based prompt definitions with tool preferences
- Implement intelligent tool selection with fallback logic
- Support multiple AI CLI tools (opencode, claude, amp, codex, ccs profiles)
- Update loadConfig to accept detected tools for template generation
- Maintain backward compatibility with existing config structure

This change allows the tool to automatically generate appropriate templates
based on what AI tools are actually installed on the user's system,
providing a better out-of-box experience without manual configuration.
@changeset-bot

changeset-bot Bot commented Feb 1, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 2e74fbd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Feb 1, 2026

Copy link
Copy Markdown

Walkthrough

Replaces static default templates with generated templates built from detected tools; loadConfig now accepts optional detectedTools and generates/validates templates when no config exists. Index uses centralized parseTemplateCommand for extracting command and args before launching tools.

Changes

Cohort / File(s) Summary
Config Module
src/config.ts
Replaces DEFAULT_TEMPLATES with CATEGORY_PROMPTS and functions to normalize tool names, pick preferred tools, escape quotes, build per-tool commands, and produce buildDefaultTemplates. Adds SAFE_COMMAND_PATTERN, countPlaceholders(), refactors validateTemplate, and changes loadConfig(detectedTools?: Tool[]) to generate templates when config missing.
Index / Runtime Integration
src/index.ts
Replaces ad-hoc command parsing with parseTemplateCommand(finalCommand) (imported from template utilities), and calls loadConfig(detectedTools) so generated templates are available before merging with detected tools and launching processes.

Sequence Diagram

sequenceDiagram
    participant Index as Index Module
    participant Config as Config Module
    participant Builder as Template Builder
    participant Validator as Validator
    participant Runtime as Runtime / Launcher

    Index->>Index: detect available tools
    Index->>Config: loadConfig(detectedTools)
    Config->>Builder: buildDefaultTemplates(detectedTools)
    Builder->>Builder: normalizeName / pickTool / buildCommandForTool
    Builder->>Validator: validateTemplate(s)
    Validator-->>Builder: validation results
    Builder-->>Config: return generated templates
    Config->>Config: merge or persist templates into config
    Config-->>Index: return merged config
    Index->>Index: parseTemplateCommand(finalCommand) => cmd + args
    Index->>Runtime: launchTool(cmd, args)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I hop through configs, templates in sight,
I craft commands by moon and by light,
I count each placeholder, tidy and quick,
I whisper to tools — ready, click-click! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: seed default templates from detected tools' clearly and concisely describes the main change: generating default templates dynamically from tools detected on the user's machine to populate config on first run.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/default-template
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/default-template

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @jellydn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience by introducing support for the OpenAI Codex CLI and implementing a robust dynamic default template generation system. Instead of starting with an empty configuration, users will now automatically receive a set of intelligent, tool-aware templates tailored to their installed AI CLIs. This change aims to reduce friction for new users and leverage the specific strengths of various AI tools for common development tasks, providing instant value and progressive enhancement.

Highlights

  • OpenAI Codex CLI Support: The OpenAI Codex CLI has been integrated, allowing the system to auto-detect it and use codex exec as its prompt command. This expands the range of AI tools supported.
  • Dynamic Default Template Generation: A new feature dynamically generates default templates based on the AI CLI tools detected on the user's system. This provides immediate value to new users by offering tailored templates for common coding workflows like code review, commit message generation, documentation, and testing.
  • Enhanced Tool Selection Logic: The system now includes CATEGORY_PROMPTS which define 8 common coding workflows, each with a ranked list of preferred AI tools. This allows for intelligent selection of the best available tool to generate a template for a specific task, including special handling for ccs:* wildcard profiles.
  • Tool-Specific Command Formatting: Commands generated for templates are now properly formatted with tool-specific flags and arguments, ensuring correct invocation for opencode, claude, amp, codex, and ccs tools.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@jellydn jellydn changed the title feat: add Codex CLI support and dynamic default templates feat: add dynamic default templates Feb 1, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for the Codex CLI and dynamically generates default templates, significantly improving the onboarding experience. However, a critical command injection vulnerability exists in the template construction, allowing user-supplied input to escape single-quoted prompt strings and lead to arbitrary code execution due to insufficient validation and shell execution. Robust shell argument escaping or avoiding shell execution is strongly recommended. Additionally, there are suggestions for src/config.ts to improve code clarity and consistency.

Comment thread src/config.ts
Comment on lines +121 to +133
preferredTools: ["ccs:mm", "claude", "opencode", "amp", "codex", "ccs:*"],
},
{
name: "test",
description: "Generate tests",
aliases: ["spec", "tests"],
prompt:
"Write tests using Arrange-Act-Assert pattern. Focus on behavior, not implementation details for: $@",
requiresInput: true,
preferredTools: ["ccs:mm", "claude", "opencode", "amp", "codex", "ccs:*"],
},
{
name: "docs",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

Critical command injection vulnerability in buildCommandForTool (src/config.ts). The function constructs shell commands by wrapping the prompt in single quotes but does not escape single quotes within the prompt. User-supplied input can escape the quoted string and execute arbitrary shell commands, as launchTool uses a shell and isSafeCommand (src/template.ts) does not block shell metacharacters. This must be addressed immediately, for example, by robust shell argument escaping or avoiding shell execution. For future maintainability, consider refactoring the if statements to a switch statement after resolving this critical security flaw.

Comment thread src/config.ts

function normalizeName(name: string): string {
return name.trim().toLowerCase();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic here to strip : $@ for prompts that don't require input is a bit confusing because the prompts in CATEGORY_PROMPTS that have requiresInput: false don't contain this placeholder to begin with. This makes the .replace() call a no-op and potentially confusing.

To simplify and improve clarity, you could remove the .replace() call and rely on the convention that prompts for requiresInput: false categories don't contain the placeholder.

Suggested change
}
const prompt = category.prompt;

@jellydn jellydn changed the title feat: add dynamic default templates feat: seed default templates from detected tools Feb 1, 2026
@jellydn jellydn requested a review from Copilot February 1, 2026 02:23
@jellydn jellydn marked this pull request as ready for review February 1, 2026 02:23

@ellipsis-dev ellipsis-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Looks good to me! 👍

Reviewed everything up to fcac10a in 7 seconds. Click for details.
  • Reviewed 214 lines of code in 2 files
  • Skipped 0 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_n3tkVNhNNY3dixkf

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@greptile-apps

greptile-apps Bot commented Feb 1, 2026

Copy link
Copy Markdown

Greptile Overview

Greptile Summary

Introduced automatic template generation based on detected tools, improving first-run user experience by seeding useful defaults instead of starting with an empty config.

Key Changes:

  • Added CATEGORY_PROMPTS array defining 8 template categories (review, commit-zen, architecture-explanation, etc.) with preferred tool ordering
  • Implemented buildDefaultTemplates() that matches detected tools against preferred tools and generates template commands
  • Modified loadConfig() to accept detectedTools parameter and create default templates when config doesn't exist
  • Reordered startup in index.ts to call detectInstalledTools() before loadConfig() so detected tools are available for template generation
  • Template generation respects tool-specific command syntax (opencode, claude, amp, codex, ccs profiles)

Issue Found:

  • Line 210 in config.ts: regex pattern replace(": $@", "") only matches literal string, not regex - won't trim prompts ending with space before $@

Confidence Score: 4/5

  • Safe to merge after fixing the regex issue on line 210
  • The implementation is well-structured and aligns with existing patterns. The logic bug in prompt trimming needs fixing but won't cause runtime errors - it will just leave $@ in prompts where it shouldn't be
  • Fix src/config.ts:210 before merging

Important Files Changed

Filename Overview
src/config.ts Added template generation logic with category prompts and tool detection - found regex issue in prompt trimming logic
src/index.ts Reordered tool detection to run before config loading - clean and correct implementation

Sequence Diagram

sequenceDiagram
    participant User
    participant main
    participant detectInstalledTools
    participant loadConfig
    participant buildDefaultTemplates
    participant pickTool
    participant buildCommandForTool
    participant createDefaultConfig
    participant FS as File System

    User->>main: Run ai-switcher
    main->>detectInstalledTools: Detect installed tools
    detectInstalledTools-->>main: Return Tool[]
    main->>loadConfig: loadConfig(detectedTools)
    
    alt Config file does not exist
        loadConfig->>buildDefaultTemplates: buildDefaultTemplates(detectedTools)
        loop For each CATEGORY_PROMPTS
            buildDefaultTemplates->>pickTool: pickTool(detectedTools, preferredTools)
            pickTool-->>buildDefaultTemplates: Return matching Tool or undefined
            alt Tool found
                buildDefaultTemplates->>buildCommandForTool: buildCommandForTool(tool, prompt)
                buildCommandForTool-->>buildDefaultTemplates: Return command string or null
                alt Command built successfully
                    buildDefaultTemplates->>buildDefaultTemplates: Add template to list
                end
            end
        end
        buildDefaultTemplates-->>loadConfig: Return Template[]
        loadConfig->>createDefaultConfig: createDefaultConfig(templates)
        createDefaultConfig->>FS: Write config.json with generated templates
        loadConfig-->>main: Return Config with generated templates
    else Config file exists
        loadConfig->>FS: Read config.json
        FS-->>loadConfig: Return config content
        loadConfig-->>main: Return existing Config
    end
    
    main->>main: Continue with tool execution
Loading

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread src/config.ts Outdated
continue;
}

const prompt = category.requiresInput ? category.prompt : category.prompt.replace(": $@", "");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 210's regex will not match prompts that end with : $@, causing them to remain unchanged

Suggested change
const prompt = category.requiresInput ? category.prompt : category.prompt.replace(": $@", "");
const prompt = category.requiresInput ? category.prompt : category.prompt.replace(/ $@$/, "");
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config.ts
Line: 210:210

Comment:
Line 210's regex will not match prompts that end with `: $@`, causing them to remain unchanged

```suggestion
    const prompt = category.requiresInput ? category.prompt : category.prompt.replace(/ $@$/, "");
```

How can I resolve this? If you propose a fix, please make it concise.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/config.ts`:
- Around line 176-195: The buildCommandForTool function interpolates raw prompt
text into single-quoted shell commands (variable prompt), which breaks when the
prompt contains single quotes; sanitize the prompt before interpolation by
replacing each single quote with the standard shell-safe sequence that closes
the single-quoted string, inserts an escaped single quote, and reopens it (i.e.,
perform the shell-safe single-quote escaping), then use that sanitizedPrompt in
every returned command branch (including the tool.promptCommand branch) so
generated commands remain valid.

Comment thread src/config.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements automatic template generation based on detected tools to improve the first-run experience. Instead of starting with empty configuration, new users get pre-configured templates tailored to the tools installed on their system.

Changes:

  • Modified config loading to accept detected tools and generate default templates based on tool availability
  • Added CATEGORY_PROMPTS data structure defining template categories with tool preferences
  • Reordered initialization in main() to detect tools before loading config

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/index.ts Reordered tool detection to occur before config loading, enabling template seeding
src/config.ts Added template generation logic including CATEGORY_PROMPTS definitions, tool selection, and command building

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/config.ts Outdated
Comment on lines +190 to +191
if (name.startsWith("ccs:") && tool.promptCommand) {
return `${tool.promptCommand} '${prompt}'`;

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new template generation always uses "--permission-mode plan" for ccs: profiles, but the old DEFAULT_TEMPLATES used "--permission-mode acceptEdits" for templates like "types", "test", and "docs" (see old config.ts lines 40-57). This is a breaking behavioral change that will affect users upgrading from the old defaults. Templates that previously made automatic edits will now only suggest them. Consider either preserving the acceptEdits mode for appropriate templates or documenting this as a breaking change.

Copilot uses AI. Check for mistakes.
Comment thread src/config.ts
Comment on lines +150 to +225
function normalizeName(name: string): string {
return name.trim().toLowerCase();
}

function pickTool(detectedTools: Tool[], preferredTools: string[]): Tool | undefined {
const toolsByName = new Map(detectedTools.map((tool) => [normalizeName(tool.name), tool]));

for (const preferred of preferredTools) {
const normalized = normalizeName(preferred);
if (normalized === "ccs:*") {
const ccsProfile = detectedTools.find((tool) => normalizeName(tool.name).startsWith("ccs:"));
if (ccsProfile) {
return ccsProfile;
}
continue;
}

const directMatch = toolsByName.get(normalized);
if (directMatch) {
return directMatch;
}
}

return undefined;
}

function buildCommandForTool(tool: Tool, prompt: string): string | null {
const name = normalizeName(tool.name);
if (name === "opencode") {
return `opencode run --model opencode/big-pickle --agent plan '${prompt}'`;
}
if (name === "claude") {
return `claude --permission-mode plan -p '${prompt}'`;
}
if (name === "amp") {
return `amp -x '${prompt}'`;
}
if (name === "codex") {
return `codex exec '${prompt}'`;
}
if (name.startsWith("ccs:") && tool.promptCommand) {
return `${tool.promptCommand} '${prompt}'`;
}

return null;
}

function buildDefaultTemplates(detectedTools: Tool[]): Template[] {
if (detectedTools.length === 0) {
return [];
}

const templates: Template[] = [];

for (const category of CATEGORY_PROMPTS) {
const tool = pickTool(detectedTools, category.preferredTools);
if (!tool) {
continue;
}

const prompt = category.requiresInput ? category.prompt : category.prompt.replace(": $@", "");
const command = buildCommandForTool(tool, prompt);
if (!command) {
continue;
}

templates.push({
name: category.name,
command,
description: `${category.description} with ${tool.name}`,
aliases: category.aliases,
});
}

return templates;
}

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new functions buildDefaultTemplates, pickTool, buildCommandForTool, and normalizeName are not covered by tests. Given that other functions in this file have comprehensive test coverage (see config.test.ts), these new functions should also have tests to ensure they correctly generate templates from detected tools, handle edge cases like missing tools, and properly format commands.

Copilot uses AI. Check for mistakes.
Comment thread src/config.ts Outdated
continue;
}

const prompt = category.requiresInput ? category.prompt : category.prompt.replace(": $@", "");

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The replace(": $@", "") logic is inconsistent with the actual prompt patterns. Some prompts that have requiresInput: false don't have ": $@" suffix to remove (e.g., "Explain this codebase architecture" on line 102, "Create draft pr with what why how by gh cli" on line 110). Consider using a more robust pattern like prompt.replace(/[:\s]*\$@\s*$/, "") or checking if the prompt ends with "$@" before attempting replacement.

Suggested change
const prompt = category.requiresInput ? category.prompt : category.prompt.replace(": $@", "");
const prompt = category.requiresInput
? category.prompt
: category.prompt.replace(/[:\s]*\$@\s*$/, "");

Copilot uses AI. Check for mistakes.
Comment thread src/config.ts Outdated
description: "Generate commit message",
aliases: ["zen", "logical-commit"],
prompt:
"Review the following changes and generate a concise git commit message, group by logical changes with commitizen convention, do atomic commit message: $@",

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit message prompt differs from the previous DEFAULT_TEMPLATES. The old version included "on git" ("Review the following changes on git and generate...") while the new version omits it ("Review the following changes and generate..."). This may affect the quality of generated commit messages as the tool may not understand the context is specifically about git changes. Consider adding "on git" back or making it more explicit that this is for git changes.

Suggested change
"Review the following changes and generate a concise git commit message, group by logical changes with commitizen convention, do atomic commit message: $@",
"Review the following changes on git and generate a concise git commit message, group by logical changes with commitizen convention, do atomic commit message: $@",

Copilot uses AI. Check for mistakes.
Comment thread src/config.ts Outdated
Comment on lines +176 to +189
function buildCommandForTool(tool: Tool, prompt: string): string | null {
const name = normalizeName(tool.name);
if (name === "opencode") {
return `opencode run --model opencode/big-pickle --agent plan '${prompt}'`;
}
if (name === "claude") {
return `claude --permission-mode plan -p '${prompt}'`;
}
if (name === "amp") {
return `amp -x '${prompt}'`;
}
if (name === "codex") {
return `codex exec '${prompt}'`;
}

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded commands in buildCommandForTool don't match the tool definitions from detectInstalledTools. For example, opencode is detected with promptCommand "opencode run --model opencode/big-pickle" (see detect.ts:21), but buildCommandForTool adds "--agent plan" which isn't in the detected tool's promptCommand. This creates inconsistency between template-based usage and direct tool usage. Consider either using tool.promptCommand when available, or documenting why templates need different flags than regular tool invocation.

Copilot uses AI. Check for mistakes.
- Add escapeSingleQuotes function to safely handle single quotes in commands
- Extract SAFE_COMMAND_PATTERN constant for consistent validation
- Refactor validateTool function to use early returns for better readability
- Extract countPlaceholders helper function for template validation
- Add parseTemplateCommand function to centralize command parsing logic
- Remove hardcoded DEFAULT_TEMPLATES and use empty array instead
- Improve error handling in validateTemplate with early returns

@ellipsis-dev ellipsis-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Looks good to me! 👍

Reviewed a73cf1e in 7 seconds. Click for details.
  • Reviewed 255 lines of code in 2 files
  • Skipped 0 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_LyDKgkh4WVtjfRld

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/config.ts (1)

337-338: ⚠️ Potential issue | 🟡 Minor

Add try-catch for JSON parsing to provide helpful error messages.

If the config file contains malformed JSON, JSON.parse will throw a SyntaxError with a generic message like "Unexpected token". Wrapping this in a try-catch would provide a more helpful error that includes the config file path.

🛡️ Proposed fix
   const rawContent = readFileSync(CONFIG_PATH, "utf-8");
-  const parsed = JSON.parse(rawContent);
+  let parsed: unknown;
+  try {
+    parsed = JSON.parse(rawContent);
+  } catch (error) {
+    const message = error instanceof SyntaxError ? error.message : "Unknown parsing error";
+    throw new Error(`Invalid JSON in config file ${CONFIG_PATH}: ${message}`);
+  }
🧹 Nitpick comments (1)
src/config.ts (1)

102-122: Consider handling duplicate tool names explicitly.

If detectedTools contains tools with the same normalized name, the Map constructor will silently keep only the last one. While this is unlikely with detectInstalledTools, it could cause unexpected behavior with user-provided tools.

♻️ Optional: Log warning on duplicates
 function pickTool(detectedTools: Tool[], preferredTools: string[]): Tool | undefined {
+  const seenNames = new Set<string>();
+  for (const tool of detectedTools) {
+    const normalized = normalizeName(tool.name);
+    if (seenNames.has(normalized)) {
+      console.warn(`Warning: Duplicate tool name detected: ${tool.name}`);
+    }
+    seenNames.add(normalized);
+  }
   const toolsByName = new Map(detectedTools.map((tool) => [normalizeName(tool.name), tool]));

@jellydn

jellydn commented Feb 9, 2026

Copy link
Copy Markdown
Owner Author

Code Review: Issues Found

🔴 Blocking Issues

1. Bug: validateTool() no longer accumulates all validation errors
Location: src/config.ts:195-222

The refactored function returns early on each validation failure instead of accumulating errors. Previously, users would see all issues at once; now they must run the tool multiple times to discover each error.

2. Potential double-escaping in template commands
Location: src/config.ts (buildCommandForTool) and index.ts:235-238

Template commands are escaped in buildCommandForTool(), then escaped again in launchToolWithPrompt(). This could cause improperly nested shell quotes.

🟡 Suggestions

3. Missing test updates
The PR changes loadConfig() to require a detectedTools parameter. Tests calling loadConfig() without arguments will fail.

4. Unused field in CATEGORY_PROMPTS
The requiresInput boolean (src/config.ts:21-96) is never referenced. Either remove it or implement the intended logic.

✅ Overall

Good separation of concerns in the new functions. Please address the error accumulation bug before merging.

Comment thread src/config.ts Outdated
function buildCommandForTool(tool: Tool, prompt: string): string | null {
const name = normalizeName(tool.name);
if (name === "opencode") {
return `opencode run --model opencode/big-pickle --agent plan '${escapeSingleQuotes(prompt)}'`;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return `opencode run --model opencode/big-pickle --agent plan '${escapeSingleQuotes(prompt)}'`;
return `opencode run --model opencode/minimax-m2.5-free --agent plan '${escapeSingleQuotes(prompt)}'`;

@copilot code review[agent] ❯ opencode models
opencode/big-pickle
opencode/claude-3-5-haiku
opencode/claude-haiku-4-5
opencode/claude-opus-4-1
opencode/claude-opus-4-5
opencode/claude-opus-4-6
opencode/claude-sonnet-4
opencode/claude-sonnet-4-5
opencode/claude-sonnet-4-6
opencode/gemini-3-flash
opencode/gemini-3-pro
opencode/gemini-3.1-pro
opencode/glm-4.6
opencode/glm-4.7
opencode/glm-5
opencode/gpt-5
opencode/gpt-5-codex
opencode/gpt-5-nano
opencode/gpt-5.1
opencode/gpt-5.1-codex
opencode/gpt-5.1-codex-max
opencode/gpt-5.1-codex-mini
opencode/gpt-5.2
opencode/gpt-5.2-codex
opencode/gpt-5.3-codex
opencode/kimi-k2.5
opencode/minimax-m2.1
opencode/minimax-m2.5
opencode/minimax-m2.5-free
opencode/trinity-large-preview-free

Copilot AI commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

@jellydn I've opened a new pull request, #40, to work on those changes. Once the pull request is ready, I'll request review from you.

…40)

* Initial plan

* Use opencode/minimax-m2.5-free as default model for opencode template

Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/config.ts (2)

4-5: Use @/ path aliases for src imports.

Line 5 (and the adjacent internal import on Line 4) still uses relative ./... imports inside src/, which breaks the alias rule.

♻️ Proposed fix
-import { isSafeCommand } from "./template";
-import type { Config, ConfigValidationError, Template, Tool } from "./types";
+import { isSafeCommand } from "@/template";
+import type { Config, ConfigValidationError, Template, Tool } from "@/types";

As per coding guidelines: "src/**/*.{ts,tsx}: Use path alias @/* to resolve to src/* in imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` around lines 4 - 5, Update the imports at the top of this
module to use the project path alias instead of relative paths: replace the
relative import of isSafeCommand from "./template" with "@/template" and replace
the type import of Config, ConfigValidationError, Template, Tool from "./types"
with "@/types" so the module imports use the "@/..." alias and resolve correctly
under the src/* alias; ensure import specifiers remain unchanged (isSafeCommand,
Config, ConfigValidationError, Template, Tool).

21-27: requiresInput is dead metadata right now.

requiresInput is defined in the category schema but not used when building templates, so it can drift from prompt placeholder behavior.

♻️ One way to enforce it
   for (const category of CATEGORY_PROMPTS) {
+    const hasPlaceholder = category.prompt.includes("$@");
+    if (category.requiresInput && !hasPlaceholder) {
+      throw new Error(
+        `Category "${category.name}" requires input but prompt is missing "$@"`,
+      );
+    }
+    if (!category.requiresInput && hasPlaceholder) {
+      throw new Error(
+        `Category "${category.name}" does not require input but prompt includes "$@"`,
+      );
+    }
+
     const tool = pickTool(detectedTools, category.preferredTools);
     if (!tool) {
       continue;
     }

As per coding guidelines: "Remove unused code - noUnusedLocals and noUnusedParameters must pass".

Also applies to: 156-163

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` around lines 21 - 27, The CATEGORY_PROMPTS schema currently
declares a requiresInput field that isn’t used when building templates; remove
this dead metadata by deleting the requiresInput property from the
CATEGORY_PROMPTS type definition and from the duplicate schema occurrence later
in the file, and then search for any remaining references to requiresInput and
either remove those usages or wire them into the template-building logic (e.g.,
the function that composes prompts) if you intend to preserve the flag; ensure
the file compiles with no unused locals/params after the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/config.ts`:
- Around line 199-223: The validator currently returns early on the first
failure for a tool (checks on t.name, t.command, SAFE_COMMAND_PATTERN,
t.description), so change the function (the block validating t.name / t.command
/ t.description and calling validateAliases) to accumulate errors in an array
(e.g., const errors: ValidationError[] = []), push each error object instead of
returning immediately, then at the end concat the result of
validateAliases(t.aliases, `${path}.aliases`) and return the full errors array;
ensure you still trim/check types as before and preserve the same error shape
and path strings.
- Line 10: SAFE_COMMAND_PATTERN currently uses \s which permits tabs and other
whitespace; update the regex to only allow the ASCII space character (e.g.,
replace \s in the character class with a literal space or explicit space range)
so commands validated by SAFE_COMMAND_PATTERN only accept spaces and not
tabs/newlines, then run/update tests that exercise the command validation to
ensure tab-containing strings are rejected; reference symbol:
SAFE_COMMAND_PATTERN.

---

Nitpick comments:
In `@src/config.ts`:
- Around line 4-5: Update the imports at the top of this module to use the
project path alias instead of relative paths: replace the relative import of
isSafeCommand from "./template" with "@/template" and replace the type import of
Config, ConfigValidationError, Template, Tool from "./types" with "@/types" so
the module imports use the "@/..." alias and resolve correctly under the src/*
alias; ensure import specifiers remain unchanged (isSafeCommand, Config,
ConfigValidationError, Template, Tool).
- Around line 21-27: The CATEGORY_PROMPTS schema currently declares a
requiresInput field that isn’t used when building templates; remove this dead
metadata by deleting the requiresInput property from the CATEGORY_PROMPTS type
definition and from the duplicate schema occurrence later in the file, and then
search for any remaining references to requiresInput and either remove those
usages or wire them into the template-building logic (e.g., the function that
composes prompts) if you intend to preserve the flag; ensure the file compiles
with no unused locals/params after the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: da620926-4015-4df4-bf36-a785cba48d23

📥 Commits

Reviewing files that changed from the base of the PR and between a73cf1e and 0eab64b.

📒 Files selected for processing (1)
  • src/config.ts

Comment thread src/config.ts
const CONFIG_PATH = join(CONFIG_DIR, "config.json");

const DEFAULT_TEMPLATES: Template[] = [
const SAFE_COMMAND_PATTERN = /^[a-zA-Z0-9._\s-]+$/;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "src/config\.(ts|js)" | head -5

Repository: jellydn/ai-launcher

Length of output: 76


🏁 Script executed:

cat -n src/config.ts | head -30

Repository: jellydn/ai-launcher

Length of output: 1078


🏁 Script executed:

cat -n src/config.ts | sed -n '200,220p'

Repository: jellydn/ai-launcher

Length of output: 871


🏁 Script executed:

rg "SAFE_COMMAND_PATTERN" -n

Repository: jellydn/ai-launcher

Length of output: 370


🏁 Script executed:

python3 << 'EOF'
import re

# Current pattern
current = re.compile(r'^[a-zA-Z0-9._\s-]+$')
# Proposed fix
fixed = re.compile(r'^[a-zA-Z0-9._ -]+$')

test_cases = [
    ("claude --model sonnet", "valid command with space"),
    ("claude\t--model sonnet", "command with tab (should FAIL)"),
    ("claude\nrm -rf /", "command with newline (should FAIL)"),
    ("my-tool_name", "valid with hyphen and underscore"),
    ("tool.name", "valid with period"),
    ("claude -c config.json", "valid multiword"),
]

print("CURRENT PATTERN (vulnerable):")
for cmd, desc in test_cases:
    result = bool(current.fullmatch(cmd))
    print(f"  {result}: {desc!r}")

print("\nPROPOSED FIX (secure):")
for cmd, desc in test_cases:
    result = bool(fixed.fullmatch(cmd))
    print(f"  {result}: {desc!r}")
EOF

Repository: jellydn/ai-launcher

Length of output: 555


SAFE_COMMAND_PATTERN currently allows tabs and other whitespace characters.

Line 10 uses \s, which matches tabs, spaces, and other whitespace. This permits commands with embedded tabs (e.g., "command\ttab-separated") to bypass validation at line 214, creating a command injection vector.

🛡️ Proposed fix
-const SAFE_COMMAND_PATTERN = /^[a-zA-Z0-9._\s-]+$/;
+const SAFE_COMMAND_PATTERN = /^[a-zA-Z0-9._ -]+$/;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const SAFE_COMMAND_PATTERN = /^[a-zA-Z0-9._\s-]+$/;
const SAFE_COMMAND_PATTERN = /^[a-zA-Z0-9._ -]+$/;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` at line 10, SAFE_COMMAND_PATTERN currently uses \s which
permits tabs and other whitespace; update the regex to only allow the ASCII
space character (e.g., replace \s in the character class with a literal space or
explicit space range) so commands validated by SAFE_COMMAND_PATTERN only accept
spaces and not tabs/newlines, then run/update tests that exercise the command
validation to ensure tab-containing strings are rejected; reference symbol:
SAFE_COMMAND_PATTERN.

Comment thread src/config.ts Outdated
@jellydn

jellydn commented Mar 4, 2026

Copy link
Copy Markdown
Owner Author

@copilot old have tools
json { "tools": [ { "name": "claude", "command": "claude", "description": "Anthropic Claude CLI", "aliases": ["c"], "promptCommand": "claude --permission-mode plan -p" }, { "name": "codex", "command": "codex", "description": "OpenAI Codex CLI", "aliases": ["co"], "promptCommand": "codex exec" }, { "name": "opencode", "command": "opencode", "description": "OpenCode AI assistant", "aliases": ["o", "oc"], "promptCommand": "opencode run --model zai-coding-plan/glm-4.7", "promptUseStdin": true }, { "name": "amp", "command": "amp", "description": "AI coding assistant by Modular", "aliases": ["a"], "promptCommand": "amp -x", "promptUseStdin": true } ], "templates": [ { "name": "review", "command": "opencode run --model zai-coding-plan/glm-4.7 --agent plan 'Review the following changes and provide feedback: $@'", "description": "Code review with OpenCode", "aliases": ["rev", "code-review"] }, { "name": "commit-zen", "command": "opencode run --model zai-coding-plan/glm-4.7 --agent plan 'Review the following changes on git and generate a concise git commit message, group by logical changes with commitizen convention, do atomic commit message'", "description": "Generate commit message with OpenCode", "aliases": ["zen", "logical-commit"] }, { "name": "commit-atomic", "command": "opencode run --model zai-coding-plan/glm-4.7 --agent build 'Run git diff --staged then do atomic commit message for the change with commitizen convention. Write clear, informative commit messages that explain the what and why behind changes, not just the how.'", "description": "Atomic commit message with OpenCode", "aliases": ["ac", "auto-commit"] }, { "name": "architecture-explanation", "command": "ccs gemini 'Explain this codebase architecture'", "description": "Explain architecture with Gemini", "aliases": ["arch", "arch-explanation"] }, { "name": "draft-pull-request", "command": "ccs glm --permission-mode acceptEdits -p 'Create draft pr with what why how by gh cli'", "description": "Create draft pull request with GLM", "aliases": ["pr", "draft-pr"] }, { "name": "types", "command": "ccs glm --permission-mode acceptEdits -p 'Improve TypeScript types: Remove any, add proper type guards, ensure strict mode compliance for: $@'", "description": "Enhance type safety", "aliases": ["typescript"] }, { "name": "test", "command": "ccs glm --permission-mode acceptEdits -p 'Write tests using Arrange-Act-Assert pattern. Focus on behavior, not implementation details for: $@'", "description": "Generate tests", "aliases": ["spec", "tests"] }, { "name": "docs", "command": "ccs glm --permission-mode acceptEdits -p 'Add JSDoc comments with @param and @returns. Include usage examples for: $@'", "description": "Add documentation", "aliases": ["document"] }, { "name": "explain", "command": "ccs glm --permission-mode plan -p 'Explain this code in detail: 1) What it does 2) How it works 3) Design decisions: $@'", "description": "Code explanation", "aliases": ["wtf", "explain-code"] }, { "name": "review-security", "command": "ccs glm --permission-mode plan -p 'Security review: Check for injection vulnerabilities, input validation, auth issues, and sensitive data handling in: $@'", "description": "Security-focused review", "aliases": ["sec", "security"] }, { "name": "review-refactor", "command": "ccs glm --permission-mode plan -p 'Refactor suggestion: Improve readability, eliminate complexity, and apply clean code principles to: $@'", "description": "Refactoring recommendations", "aliases": ["refactor"] }, { "name": "review-performance", "command": "ccs glm --permission-mode plan -p 'Analyze performance: Identify bottlenecks, suggest optimizations with measurable impact for: $@'", "description": "Performance review", "aliases": ["perf", "optimize"] }, { "name": "remove-verbal", "command": "ccs glm --permission-mode acceptEdits -p 'Analyze code: Identify verbal comment and remove it, and ensure consistency in style for: $@'", "description": "Clean verbal comments that explain 'what' the code is doing rather than 'why'", "aliases": ["verbal", "comments"] }, { "name": "remove-ai-slop", "command": "ccs glm --permission-mode acceptEdits -p \"You're reviewing code cleanup. Remove: 1) Excessive comments that break existing documentation style 2) Defensive checks that don't match the codebase's trust model 3) Type escape hatches (any casts, assertions) 4) Generic patterns that feel imported rather than native. Match the file's existing voice and conventions. Report what you removed in 1-3 sentences: $@\"", "description": "Remove AI-generated code patterns", "aliases": ["slop", "clean-ai"] }, { "name": "tidy-first", "command": "ccs glm --permission-mode acceptEdits -p 'Apply Tidy First principles: 1) Use guard clauses 2) Extract helper variables for complex expressions 3) Remove dead code 4) Normalize symmetries. Focus on making the code easier to understand: $@'", "description": "Tidy code before making changes", "aliases": ["tidy"] }, { "name": "simplify", "command": "ccs glm --permission-mode acceptEdits -p \"Simplify this code: Remove unnecessary complexity, eliminate over-engineering, reduce coupling. Keep solutions simple and focused on what's actually needed: $@\"", "description": "Simplify over-engineered code", "aliases": ["simple"] }, { "name": "simplifier", "command": "ccs glm --permission-mode acceptEdits -p \"@code-simplifier:code-simplifier", "description": "Simplifies and refines code for clarity, consistency, and maintainability while preserving all functionality. Focuses on recently modified code unless instructed otherwise.", "aliases": ["simplify-code", "simplify"] }, { "name": "logical-grouping-pull-request", "command": "ccs glm --permission-mode acceptEdits -p 'Analyze the git changes and create a draft PR, plan: 1) Group changes into logical, independent commits 2) Order them by dependency (low-level to high-level) 3) Run commit commit for each change group 4) Write PR description with what why how.'", "description": "Create a draft pull request for the git changes with logical grouping", "aliases": ["split-pr"] } ] }

but not on this PR

{ "tools": [], "templates": [ { "name": "review", "command": "opencode run --model opencode/minimax-m2.5-free --agent plan 'Review the following changes and provide feedback: $@'", "description": "Code review with opencode", "aliases": [ "rev", "code-review" ] }, { "name": "commit-zen", "command": "opencode run --model opencode/minimax-m2.5-free --agent plan 'Review the following changes and generate a concise git commit message, group by logical changes with commitizen convention, do atomic commit message: $@'", "description": "Generate commit message with opencode", "aliases": [ "zen", "logical-commit" ] }, { "name": "architecture-explanation", "command": "ccs gemini --permission-mode plan -p 'Explain this codebase architecture'", "description": "Explain architecture with ccs:gemini", "aliases": [ "arch", "arch-explanation" ] }, { "name": "draft-pull-request", "command": "claude --permission-mode plan -p 'Create draft pr with what why how by gh cli'", "description": "Create draft pull request with claude", "aliases": [ "pr", "draft-pr" ] }, { "name": "types", "command": "claude --permission-mode plan -p 'Improve TypeScript types: Remove any, add proper type guards, ensure strict mode compliance for: $@'", "description": "Enhance type safety with claude", "aliases": [ "typescript" ] }, { "name": "test", "command": "claude --permission-mode plan -p 'Write tests using Arrange-Act-Assert pattern. Focus on behavior, not implementation details for: $@'", "description": "Generate tests with claude", "aliases": [ "spec", "tests" ] }, { "name": "docs", "command": "claude --permission-mode plan -p 'Add JSDoc comments with @param and @returns. Include usage examples for: $@'", "description": "Add documentation with claude", "aliases": [ "document" ] }, { "name": "explain", "command": "claude --permission-mode plan -p 'Explain this code in detail: 1) What it does 2) How it works 3) Design decisions: $@'", "description": "Code explanation with claude", "aliases": [ "wtf", "explain-code" ] } ] }

Copilot AI commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

@jellydn I've opened a new pull request, #41, to work on those changes. Once the pull request is ready, I'll request review from you.

… fix validateTool (#41)

* Initial plan

* feat: add missing templates, permissionMode/agent support, fix validateTool and commit-zen prompt

Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jellydn <870029+jellydn@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/config.ts (2)

319-323: ⚠️ Potential issue | 🔴 Critical

Guard validateTool against non-object entries before property reads.

Line 322 casts tool without runtime guarding. If tool is null or a primitive value, accessing t.name, t.command, t.description, or t.aliases on lines 324–345 will throw a TypeError instead of returning validation errors.

Suggested fix
 function validateTool(tool: unknown, index: number): ConfigValidationError[] {
   const errors: ConfigValidationError[] = [];
   const path = `tools[${index}]`;
+  if (typeof tool !== "object" || tool === null) {
+    return [{ path, message: "Tool must be an object" }];
+  }
   const t = tool as Record<string, unknown>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` around lines 319 - 323, The validateTool function currently
casts tool to a Record and reads properties without checking for
null/primitives; add a runtime guard at the start of validateTool (check typeof
tool === "object" && tool !== null) and if it fails push a ConfigValidationError
for the path (`tools[${index}]`) indicating an invalid non-object entry and
return errors early; only after the guard, perform the cast to `t` and proceed
to validate t.name, t.command, t.description, and t.aliases.

348-351: ⚠️ Potential issue | 🔴 Critical

Add null/object guard before casting in validateTemplate.

Line 350 casts template to Record<string, unknown> and immediately accesses t.name at line 352 without validating that template is actually an object. If a null or non-object value is passed (which can happen when parsing malformed JSON), accessing properties will throw a runtime error.

Suggested fix
 export function validateTemplate(template: unknown, path: string): ConfigValidationError[] {
   const errors: ConfigValidationError[] = [];
+  if (typeof template !== "object" || template === null) {
+    return [{ path, message: "Template must be an object" }];
+  }
   const t = template as Record<string, unknown>;

This same issue exists in validateTool at line 322.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` around lines 348 - 351, validateTemplate currently casts
template to Record<string, unknown> (t) and immediately reads properties like
t.name without guarding for null/non-object inputs; update validateTemplate to
first check that template is a non-null plain object (e.g. typeof template ===
'object' && template !== null && !Array.isArray(template)) and if not, push a
ConfigValidationError and return early or handle appropriately before
casting/using t; make the same guard/failure path change in validateTool to
prevent runtime property access on null/non-objects (referencing
validateTemplate, validateTool, and the variable t/name access to locate the
spots to change).
♻️ Duplicate comments (1)
src/config.ts (1)

10-10: ⚠️ Potential issue | 🔴 Critical

Restrict whitespace in command allowlist regex.

Line 10 uses \s, which accepts tabs/newlines in addition to spaces; that weakens command validation.

Suggested fix
-const SAFE_COMMAND_PATTERN = /^[a-zA-Z0-9._\s-]+$/;
+const SAFE_COMMAND_PATTERN = /^[a-zA-Z0-9._ -]+$/;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` at line 10, The regex SAFE_COMMAND_PATTERN currently uses \s
which permits tabs and newlines; change it to allow only the literal space
character so commands can't include control whitespace. Locate the constant
SAFE_COMMAND_PATTERN and replace /^[a-zA-Z0-9._\s-]+$/ with a pattern that uses
a literal space (for example /^[a-zA-Z0-9._ \-]+$/ or /^[a-zA-Z0-9._ -]+$/),
ensuring the hyphen is placed or escaped so it is treated literally; run related
validation/tests after the change.
🧹 Nitpick comments (2)
src/config.ts (2)

5-5: Use the src path alias for type imports.

Line 5 uses a relative import from inside src/; repository rules require @/* aliasing.

Suggested fix
-import type { Config, ConfigValidationError, Template, Tool } from "./types";
+import type { Config, ConfigValidationError, Template, Tool } from "@/types";

As per coding guidelines, "Use path alias @/* to resolve to src/* in imports."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` at line 5, The import in src/config.ts currently uses a
relative path for types; update the import that brings in Config,
ConfigValidationError, Template, and Tool to use the repository path alias (`@/`*)
that maps to src/* (e.g., import these types from "@/types") so all type imports
follow the project's aliasing rules and resolve correctly; ensure you only
change the module path, keeping the same named type imports.

21-30: Remove or enforce requiresInput; it is currently dead metadata.

requiresInput is defined in category definitions but not used when generating templates/commands.

As per coding guidelines, "Remove unused code - noUnusedLocals and noUnusedParameters must pass."

Also applies to: 276-287

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.ts` around lines 21 - 30, The CATEGORY_PROMPTS type includes an
unused property requiresInput; remove this dead metadata by deleting
requiresInput from the type declaration and from every object inside the
CATEGORY_PROMPTS array (and the duplicate definitions around the other
occurrence), or alternatively implement consistent enforcement where
templates/command generation checks CATEGORY_PROMPTS[].requiresInput;
specifically update the type definition for CATEGORY_PROMPTS (remove
requiresInput), remove the requiresInput key from each category object, and run
the build to ensure no references remain (if any code references requiresInput,
replace that logic with the intended behavior in the generator functions instead
of leaving the property unused).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/config.ts`:
- Around line 319-323: The validateTool function currently casts tool to a
Record and reads properties without checking for null/primitives; add a runtime
guard at the start of validateTool (check typeof tool === "object" && tool !==
null) and if it fails push a ConfigValidationError for the path
(`tools[${index}]`) indicating an invalid non-object entry and return errors
early; only after the guard, perform the cast to `t` and proceed to validate
t.name, t.command, t.description, and t.aliases.
- Around line 348-351: validateTemplate currently casts template to
Record<string, unknown> (t) and immediately reads properties like t.name without
guarding for null/non-object inputs; update validateTemplate to first check that
template is a non-null plain object (e.g. typeof template === 'object' &&
template !== null && !Array.isArray(template)) and if not, push a
ConfigValidationError and return early or handle appropriately before
casting/using t; make the same guard/failure path change in validateTool to
prevent runtime property access on null/non-objects (referencing
validateTemplate, validateTool, and the variable t/name access to locate the
spots to change).

---

Duplicate comments:
In `@src/config.ts`:
- Line 10: The regex SAFE_COMMAND_PATTERN currently uses \s which permits tabs
and newlines; change it to allow only the literal space character so commands
can't include control whitespace. Locate the constant SAFE_COMMAND_PATTERN and
replace /^[a-zA-Z0-9._\s-]+$/ with a pattern that uses a literal space (for
example /^[a-zA-Z0-9._ \-]+$/ or /^[a-zA-Z0-9._ -]+$/), ensuring the hyphen is
placed or escaped so it is treated literally; run related validation/tests after
the change.

---

Nitpick comments:
In `@src/config.ts`:
- Line 5: The import in src/config.ts currently uses a relative path for types;
update the import that brings in Config, ConfigValidationError, Template, and
Tool to use the repository path alias (`@/`*) that maps to src/* (e.g., import
these types from "@/types") so all type imports follow the project's aliasing
rules and resolve correctly; ensure you only change the module path, keeping the
same named type imports.
- Around line 21-30: The CATEGORY_PROMPTS type includes an unused property
requiresInput; remove this dead metadata by deleting requiresInput from the type
declaration and from every object inside the CATEGORY_PROMPTS array (and the
duplicate definitions around the other occurrence), or alternatively implement
consistent enforcement where templates/command generation checks
CATEGORY_PROMPTS[].requiresInput; specifically update the type definition for
CATEGORY_PROMPTS (remove requiresInput), remove the requiresInput key from each
category object, and run the build to ensure no references remain (if any code
references requiresInput, replace that logic with the intended behavior in the
generator functions instead of leaving the property unused).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f6568edf-018d-4510-bbc5-6643018e9355

📥 Commits

Reviewing files that changed from the base of the PR and between 0eab64b and 2e74fbd.

📒 Files selected for processing (1)
  • src/config.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants