Skip to content

Feat/ai-extension-builder#382

Merged
Xoshbin merged 6 commits into
mainfrom
feat/ai-extension-builder
May 31, 2026
Merged

Feat/ai-extension-builder#382
Xoshbin merged 6 commits into
mainfrom
feat/ai-extension-builder

Conversation

@Xoshbin
Copy link
Copy Markdown
Owner

@Xoshbin Xoshbin commented May 31, 2026

No description provided.

Xoshbin added 4 commits May 31, 2026 13:33
Add "Build Extension with AI" inside the create-extension feature: an in-app
orchestrator drives a bundled Claude Agent SDK sidecar that gates feasibility,
then scaffolds, codes, builds, and smoke-tests a working extension — with a live
progress view, questions surfaced as notifications, and auto-activation on success.
Sandboxed Bash allowlist + path-traversal guards; example knowledge fetched live
from canonical URLs.
Copy link
Copy Markdown

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

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 an AI-assisted extension builder (asyar-ext-builder) using the Claude Agent SDK and integrates it into the asyar-launcher Tauri application. The implementation includes feasibility gating, a secure command gate for Bash commands, secret scanning, and frontend views for build progress and extension management. The review feedback highlights opportunities to improve robustness and performance, such as adding safety checks for toolInput to prevent TypeErrors, optimizing sorting operations in both TypeScript and Rust, strengthening path resolution checks, and documenting command injection risks in terminal command generation.

// tool is permitted (Write/Edit are also covered by acceptEdits).
canUseTool: async (toolName, toolInput) => {
if (toolName === 'Bash') {
const command = typeof toolInput.command === 'string' ? toolInput.command : '';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

If toolInput is null or undefined, accessing toolInput.command will throw a TypeError. Add a safety check to ensure toolInput is a non-null object before accessing its properties.

Suggested change
const command = typeof toolInput.command === 'string' ? toolInput.command : '';
const command = (toolInput && typeof toolInput === 'object' && typeof toolInput.command === 'string') ? toolInput.command : '';

Comment on lines +90 to +94
if (dirs.length > 1) {
// Most recently modified wins.
dirs.sort((a, b) => statSync(join(baseDir, b)).mtimeMs - statSync(join(baseDir, a)).mtimeMs);
return assertSafeExtensionId(dirs[0], baseDir);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Calling statSync repeatedly inside dirs.sort is inefficient and unsafe, as it can throw an unhandled exception if a directory is concurrently modified or deleted. Cache the mtimeMs values first, then sort the cached results.

  if (dirs.length > 1) {\n    const dirsWithTime = dirs.map((name) => {\n      try {\n        return { name, mtimeMs: statSync(join(baseDir, name)).mtimeMs };\n      } catch {\n        return { name, mtimeMs: 0 };\n      }\n    });\n    dirsWithTime.sort((a, b) => b.mtimeMs - a.mtimeMs);\n    return assertSafeExtensionId(dirsWithTime[0].name, baseDir);\n  }

});
}
}
out.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Sorting with to_lowercase() on every comparison allocates new strings repeatedly. Use sort_by_cached_key to cache the lowercase keys and avoid redundant allocations.

Suggested change
out.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
out.sort_by_cached_key(|a| a.name.to_lowercase());

if cfg!(windows) { ".exe" } else { "" }
));
let candidates = binary_candidates(exe_dir.as_deref(), resource_dir.as_deref(), dev, name);
resolve_first(&candidates, |p| p.exists())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Use p.is_file() instead of p.exists() to ensure that the resolved path is a file and not accidentally a directory.

Suggested change
resolve_first(&candidates, |p| p.exists())
resolve_first(&candidates, |p| p.is_file())

if cfg!(windows) { ".exe" } else { "" }
));
let candidates = binary_candidates(exe_dir.as_deref(), resource_dir.as_deref(), dev, name);
resolve_first(&candidates, |p| p.exists())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Use p.is_file() instead of p.exists() to ensure that the resolved path is a file and not accidentally a directory.

Suggested change
resolve_first(&candidates, |p| p.exists())
resolve_first(&candidates, |p| p.is_file())

* @param dir - Absolute path to cd into before running `command`.
* @param command - Shell command to run in the new terminal window.
*/
export function buildTerminalCommand(plat: string, dir: string, command: string): TerminalCommand {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The command parameter is inserted directly into shell strings without escaping. Document this behavior with a JSDoc warning to ensure future callers only pass trusted, hardcoded constants.

/**\n * Builds a terminal launch command for the given platform.\n * \n * @security This function does not escape the `command` parameter. The caller\n * must ensure that `command` is a trusted, hardcoded string and does not contain\n * unsanitized user input to prevent command injection.\n */\nexport function buildTerminalCommand(plat: string, dir: string, command: string): TerminalCommand {

@Xoshbin Xoshbin merged commit 9936d28 into main May 31, 2026
1 check passed
@Xoshbin Xoshbin deleted the feat/ai-extension-builder branch May 31, 2026 15:14
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.

1 participant