Skip to content

Bug Fixes#20

Open
DUptain1993 wants to merge 9 commits into
veniceai:mainfrom
DUptain1993:main
Open

Bug Fixes#20
DUptain1993 wants to merge 9 commits into
veniceai:mainfrom
DUptain1993:main

Conversation

@DUptain1993

Copy link
Copy Markdown

This pull request introduces the initial, production-ready release of the Venice Code AI Coding Assistant CLI. It adds all core source files, configuration, documentation, licensing, and package management needed for a complete, installable, and extensible CLI tool. The most important changes are grouped below.

Project Foundation and Source Code

  • Introduced the complete CLI source code, including the core agent loop in src/agent/agent.ts that implements multi-turn tool-calling and streaming AI interactions, with robust error handling and extensible tool support.
  • Added package.json with all runtime and development dependencies, CLI entry point, scripts, and metadata for global installation and publishing.

Documentation and User Guidance

  • Added comprehensive installation instructions in INSTALLATION.md, covering system requirements, setup steps, configuration, troubleshooting, and usage examples.
  • Added a detailed implementation completion report in IMPLEMENTATION_COMPLETE.md, summarizing all features, architecture, commands, tools, and key project statistics.

Project Management and Licensing

  • Added a .gitignore file to exclude build artifacts, dependencies, editor files, and test files from version control.
  • Added an MIT license file to clarify legal usage and distribution terms.

Complete implementation of venice-code, a full-featured AI coding assistant built on Venice AI.

Features:
- 9 commands (init, index, chat, explain, fix, edit, refactor, testgen, search)
- 8 filesystem tools with real implementations
- Autonomous agent system with tool-calling loop
- Patch engine with LCS-based diff generation
- Embeddings system with vector store and semantic search
- Git integration and shell command execution
- Comprehensive documentation and safety features

All 34 TypeScript source files compiled successfully.
Built, tested, and ready for use.
Security fixes:
- Add path sandboxing to prevent directory traversal attacks
- Add command allowlist to run_shell tool (safe commands only)
- Fix command injection vulnerability in git diff (use execFile)
- Add warning for API key input echoing to terminal
- Check VENICE_API_KEY environment variable first

Bug fixes:
- Fix TypeScript moduleResolution (NodeNext for .js imports)
- Add DOM libs for fetch, ReadableStream, etc.
- Fix glob pattern matching in scanner (use micromatch properly)
- Fix patch hunk newStart calculation (account for offsets)
- Fix streaming tool call ID handling (update in later deltas)
- Fix Windows path separator handling in backups
- Fix listFiles pattern matching with matchBase option
- Fix type safety in patch applier (use PatchHunk type)

Documentation:
- Remove hard-coded paths from INSTALLATION.md
- Fix fetch timeout example in README.md

All changes tested and build passes successfully.
Copilot AI review requested due to automatic review settings March 30, 2026 20:07

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

Introduces the initial release of the Venice Code AI coding assistant CLI, including the full TypeScript codebase, tooling, configuration, packaging, and end-user documentation.

Changes:

  • Added the CLI entry point plus command suite (init, index, chat, explain, fix, edit, refactor, testgen, search) and an agent loop for tool-calling.
  • Implemented core capabilities: filesystem tools, patch parsing/applying, git helpers, shell execution, and embeddings-based indexing/search.
  • Added project scaffolding and docs (TypeScript config, npm package metadata/lockfile, README, installation guide, license).

Reviewed changes

Copilot reviewed 41 out of 43 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
venice-code/tsconfig.json TypeScript build configuration for Node ESM output.
venice-code/src/utils/logger.ts Centralized logging + spinner utilities for CLI UX.
venice-code/src/utils/git.ts Git command helpers used by git tools/commands.
venice-code/src/utils/fs-helpers.ts File IO, recursive listing/search, backups, and path normalization.
venice-code/src/types/index.ts Shared type definitions for API, tools, agent, and config.
venice-code/src/tools/write-file.ts Tool to write files with optional backups.
venice-code/src/tools/search-files.ts Tool to regex-search across files.
venice-code/src/tools/run-shell.ts Tool to execute shell commands for workflows/tests.
venice-code/src/tools/read-file.ts Tool to read files with size limits.
venice-code/src/tools/list-files.ts Tool to list files with ignore/pattern support.
venice-code/src/tools/index.ts Tool registry + tool definition conversion.
venice-code/src/tools/git-status.ts Tool wrapper for git status.
venice-code/src/tools/git-diff.ts Tool wrapper for git diff.
venice-code/src/tools/apply-patch.ts Tool to parse + apply unified diff patches.
venice-code/src/patch/parser.ts Unified diff parser and basic validation.
venice-code/src/patch/generator.ts LCS-based diff generation and unified diff formatting.
venice-code/src/patch/applier.ts Patch application logic with optional backups.
venice-code/src/index.ts CLI entry point registering commands and global error handling.
venice-code/src/embeddings/vector-store.ts Vector store persistence, embedding creation, and similarity search.
venice-code/src/embeddings/scanner.ts Project scan + chunk production for indexing.
venice-code/src/embeddings/chunker.ts File chunking heuristics for embeddings.
venice-code/src/config/defaults.ts Default config values and config/backup paths.
venice-code/src/config/config.ts Config load/save/update + API key retrieval.
venice-code/src/cli/commands/testgen.ts CLI command to generate tests via the agent.
venice-code/src/cli/commands/search.ts CLI command to search the vector index.
venice-code/src/cli/commands/refactor.ts CLI command to refactor a target via the agent.
venice-code/src/cli/commands/init.ts CLI command to initialize config/API key.
venice-code/src/cli/commands/index-project.ts CLI command to scan and build/save embeddings index.
venice-code/src/cli/commands/fix.ts CLI command to fix a target via the agent.
venice-code/src/cli/commands/explain.ts CLI command to explain a target via the agent.
venice-code/src/cli/commands/edit.ts CLI command to apply requested edits via the agent.
venice-code/src/cli/commands/chat.ts CLI chat command (single-shot + interactive).
venice-code/src/api/client.ts Venice API client for chat completions and embeddings (incl. SSE parsing).
venice-code/src/agent/prompts.ts Base + per-command system prompts.
venice-code/src/agent/agent.ts Core agent loop: tool-calling, streaming, and step tracking.
venice-code/README.md Primary user documentation and usage examples.
venice-code/package.json Package metadata, scripts, deps, and CLI bin mapping.
venice-code/package-lock.json Locked dependency tree for reproducible installs.
venice-code/LICENSE MIT license.
venice-code/INSTALLATION.md Detailed installation/setup guide.
venice-code/IMPLEMENTATION_COMPLETE.md Implementation report / feature checklist.
venice-code/bin/venice-code.js Bin shim to execute compiled CLI.
venice-code/.gitignore Ignore rules for builds, deps, and editor artifacts.
Files not reviewed (1)
  • venice-code/package-lock.json: Language not supported

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

Comment on lines +12 to +52
// Allowlist of safe command prefixes
const SAFE_COMMANDS = ['git ', 'npm ', 'node ', 'tsc', 'eslint', 'jest', 'test', 'build', 'yarn ', 'pnpm '];

export const runShellTool: Tool = {
name: 'run_shell',
description: 'Execute a shell command and return its output. Use for running tests, builds, git commands, etc. Command runs in the current working directory. Only safe commands are allowed (git, npm, node, test runners, build tools).',
parameters: {
type: 'object',
properties: {
command: {
type: 'string',
description: 'Shell command to execute',
},
timeout: {
type: 'number',
description: 'Timeout in milliseconds (default: 30000)',
},
},
required: ['command'],
},
execute: async (args: { command: string; timeout?: number }): Promise<string> => {
const { command, timeout = 30000 } = args;

if (!command) {
return 'Error: command is required';
}

// Security: Check if command is safe
const autoApprove = await getConfigValue('auto_approve');
const isSafe = SAFE_COMMANDS.some(prefix => command.trim().startsWith(prefix));

if (!isSafe && !autoApprove) {
return `Error: Command "${command}" is not in the safe command list. Set auto_approve: true in config to allow all commands.`;
}

try {
const { stdout, stderr } = await execAsync(command, {
timeout,
maxBuffer: 1024 * 1024 * 10, // 10MB
cwd: process.cwd(),
});

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

run_shell uses child_process.exec() with a prefix allowlist, but the allowlist can be trivially bypassed with shell metacharacters (e.g. git status && <malicious>), since exec() runs through a shell. Consider switching to execFile/spawn with an explicit command + args model, or reject metacharacters (;, &&, |, $(), redirections) before execution; the current implementation is not a reliable security boundary.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +52
try {
// Parse patch
const patches = parsePatch(patchString);

if (patches.length === 0) {
return 'Error: No valid patches found in input';
}

// Get backup setting
const backupEnabled = await getConfigValue('backup_enabled');

// Apply patches
const results = [];
for (const patch of patches) {
const result = await applyPatch(patch, {
dryRun: dry_run,
backup: backupEnabled,
});
results.push(result);

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

apply_patch applies patches to whatever paths are present in the unified diff without any normalization or project-root restriction. This allows a patch to read/write arbitrary paths (including absolute paths or ../ escapes) even though other tools use normalizePath() for sandboxing. Consider normalizing/validating patch.oldPath/patch.newPath (and rejecting paths outside the project unless explicitly allowed) before calling applyPatch().

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +56
if (stream && iteration === 1) {
// Stream only the first user-facing response
const streamResponse = await createChatCompletionStream({
model: config.default_model,
messages: conversationMessages,
tools,
tool_choice: 'auto',
temperature: 0.7,
});

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

When stream: true, the agent only streams iteration 1. If iteration 1 ends with tool_calls (common for read/edit workflows), the final answer is produced in later non-streaming iterations and never forwarded to onChunk, while the CLI commands also don’t print result.message. This can result in missing/empty user-facing output. Consider either streaming every iteration, or calling onChunk with the full message in the non-streaming branch (or having callers print result.message when streaming didn’t output a final response).

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +96
if (!currentToolCalls[index]) {
currentToolCalls[index] = {
id: toolCall.id || '',
type: 'function',
function: {
name: toolCall.function?.name || '',
arguments: toolCall.function?.arguments || '',
},
};
} else {
// Update ID if it arrives in a later delta
if (toolCall.id && !currentToolCalls[index].id) {
currentToolCalls[index].id = toolCall.id;
}
// Only concatenate arguments (name should be set once)
if (toolCall.function?.arguments) {
currentToolCalls[index].function.arguments += toolCall.function.arguments;
}
}

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

Streaming tool-call accumulation doesn’t update function.name if it arrives in a later delta (only id and arguments are updated). If the name is omitted in the first delta, this can lead to executing a tool with an empty name. Update the merge logic to fill in function.name when provided in later deltas.

Copilot uses AI. Check for mistakes.
Comment on lines +122 to +140
conversationMessages.push({
role: 'assistant',
content: currentMessage || '',
tool_calls: currentToolCalls.filter(tc => tc.id),
});

steps.push({
type: 'tool_call',
content: currentToolCalls,
timestamp: new Date(),
});

if (onStep) {
onStep(steps[steps.length - 1]);
}

// Execute tools
await executeToolCalls(currentToolCalls, conversationMessages, steps, config.verbose, onStep);

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

currentToolCalls is treated as a dense array, but it may contain holes during streaming accumulation. Passing it directly into executeToolCalls() means the for...of loop can yield undefined entries, causing a runtime crash when accessing toolCall.function. Filter out falsy entries (and/or build tool calls in a Map keyed by index) before pushing/executing.

Copilot uses AI. Check for mistakes.

// Interactive mode if no message provided
if (!message) {
await runInteractiveChatMode(config, options.context);

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

Interactive chat ignores the --no-context / options.context flag: runInteractiveChatMode receives _includeContext but never uses it, so context behavior can’t be controlled in interactive mode. Either implement context retrieval per turn when enabled, or remove the option/parameter for interactive chat to avoid a misleading flag.

Suggested change
await runInteractiveChatMode(config, options.context);
await runInteractiveChatMode(config);

Copilot uses AI. Check for mistakes.
Comment on lines +48 to +52
async function runSingleChat(
userMessage: string,
config: any,
includeContext: boolean
): Promise<void> {

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

runSingleChat / runInteractiveChatMode type config as any, which weakens type safety in a strict TS project and makes it easy to pass incomplete configs. Prefer using the Config type from src/types for these parameters.

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +23
export async function getGitStatus(): Promise<string> {
try {
const { stdout } = await execAsync('git status --porcelain', {
cwd: process.cwd(),
});
return stdout;
} catch (error) {
throw new Error(`Failed to get git status: ${error instanceof Error ? error.message : String(error)}`);
}

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

execAsync('git …') runs through a shell; while current calls are constant strings, getGitDiff correctly uses execFile to avoid shell parsing. For consistency (and to reduce accidental future injection risk if args become dynamic), consider switching the remaining git helpers to execFile as well.

Copilot uses AI. Check for mistakes.
Comment on lines +186 to +196
export function normalizePath(path: string, allowOutside = false): string {
const absolutePath = resolve(path);

// Security: Ensure path is within project directory unless explicitly allowed
if (!allowOutside) {
const projectRoot = process.cwd();
const relativePath = relative(projectRoot, absolutePath);

if (relativePath.startsWith('..') || resolve(relativePath) !== absolutePath) {
throw new Error(`Path outside project directory not allowed: ${path}`);
}

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

normalizePath() constrains paths based on path.resolve()/path.relative(), but it does not account for symlink traversal (e.g., a path inside the repo that is a symlink to outside the repo). File operations will follow symlinks, so this check can be bypassed. If this is meant to be a hard sandbox, consider validating fs.realpath() against the project root’s realpath (or explicitly disallow symlinks).

Copilot uses AI. Check for mistakes.
Comment thread venice-code/README.md
Comment on lines +1 to +8
# Venice Code

> AI-powered coding assistant CLI built on Venice AI

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A complete, production-ready coding assistant CLI that extends Venice AI with autonomous coding capabilities including file operations, multi-file refactoring, patch generation, semantic search, and intelligent agent workflows.

Copilot AI Mar 30, 2026

Copy link

Choose a reason for hiding this comment

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

The PR title (“Bug Fixes”) doesn’t match the PR description and scope of changes (this is effectively an initial full CLI release adding many new files). Consider updating the PR title to reflect the actual change set so reviewers and release notes aren’t misleading.

Copilot uses AI. Check for mistakes.
claude and others added 7 commits June 27, 2026 06:22
…hell suggestions

- Add `venice repl` — interactive REPL with persistent conversation, slash
  commands (/clear /model /files /file /tools /approve /history /save /help),
  and auto-loaded file+shell tools for full agentic capability
- Add `venice suggest <description>` — shell command suggestion like
  gh copilot suggest; auto-detects Termux/macOS/Linux platform and uses
  pkg-aware prompts for Android
- Add `venice setup` — enhanced first-time wizard with live model picker
  (fetches available models, lets user choose by number), connection test,
  and quick-start guide
- Add 6 new built-in tools: read_file, write_file, list_files, search_files,
  delete_file, run_shell — all pure Node.js built-ins, zero new dependencies
- Add `venice chat --codebase [dir]` — load entire project into context with
  80k token budget, .gitignore respect, and smart file prioritization
- Add `venice chat --file <paths...>` — inject specific files into context
- Add `venice chat --agent` — enable all file+shell tools automatically
- Add `venice chat --auto-approve` — skip tool confirmation prompts
- Add install.sh — POSIX shell one-liner for Termux (detects Android,
  installs Node.js via pkg, fixes npm prefix, verifies PATH)
- Add isTermux() detection and Termux-aware defaults to config lib
- Wrap update-notifier in try/catch to prevent startup failures on Termux
- Bump version to 2.2.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C
feat: v2.2.0 - Termux-native support, agentic file tools, REPL, and shell suggestions
* docs: update README for v2.2.0 with Termux, REPL, suggest, and agent tools

Documents all new features added in v2.2.0: Termux one-liner install,
venice setup wizard, interactive REPL with slash commands, shell suggestion
command, codebase context loading, agentic file tools (read/write/list/search/
delete/run_shell), and all new --file/--codebase/--agent/--auto-approve flags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

* refactor: strip to Termux-native core — chat, repl, suggest, config only

Remove image, audio, video, search, models, characters, embeddings, history,
usage, completions, and tee commands. Drop e2ee, media, and tee libs along
with all @noble/ciphers, @noble/hashes, and elliptic dependencies. Strip
E2EE/TEE code paths from chat.ts and pare api.ts down to chatCompletion,
chatCompletionStream, and listModels. Bump to v2.3.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

* fix: install from GitHub source so setup command works

Change install.sh to clone and build from GitHub instead of pulling from
the npm registry (which has old v2.1.0 without the setup/repl/suggest
commands). Add prepare script so npm install from git auto-compiles TypeScript.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

---------

Co-authored-by: Claude <noreply@anthropic.com>
* docs: update README for v2.2.0 with Termux, REPL, suggest, and agent tools

Documents all new features added in v2.2.0: Termux one-liner install,
venice setup wizard, interactive REPL with slash commands, shell suggestion
command, codebase context loading, agentic file tools (read/write/list/search/
delete/run_shell), and all new --file/--codebase/--agent/--auto-approve flags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

* refactor: strip to Termux-native core — chat, repl, suggest, config only

Remove image, audio, video, search, models, characters, embeddings, history,
usage, completions, and tee commands. Drop e2ee, media, and tee libs along
with all @noble/ciphers, @noble/hashes, and elliptic dependencies. Strip
E2EE/TEE code paths from chat.ts and pare api.ts down to chatCompletion,
chatCompletionStream, and listModels. Bump to v2.3.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

* fix: install from GitHub source so setup command works

Change install.sh to clone and build from GitHub instead of pulling from
the npm registry (which has old v2.1.0 without the setup/repl/suggest
commands). Add prepare script so npm install from git auto-compiles TypeScript.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

* config: set gemma-4-uncensored as default model

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

---------

Co-authored-by: Claude <noreply@anthropic.com>
startSpinner() now always stops the existing spinner before creating a new
one. Previously, config.ts started a "Testing connection..." spinner then
listModels() started a second one internally — orphaning the first whose
animation timer kept firing and corrupting readline prompts indefinitely.

Also pass showSpinner:false to listModels() from the wizard (config owns
the spinner), add clearSpinner() guards before every readline question,
and explicitly save gemma-4-uncensored when the user presses Enter at the
model prompt instead of silently leaving it unset.


Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

Co-authored-by: Claude <noreply@anthropic.com>
…-Enter (#5)

* fix: stop orphaned spinner that froze setup wizard

startSpinner() now always stops the existing spinner before creating a new
one. Previously, config.ts started a "Testing connection..." spinner then
listModels() started a second one internally — orphaning the first whose
animation timer kept firing and corrupting readline prompts indefinitely.

Also pass showSpinner:false to listModels() from the wizard (config owns
the spinner), add clearSpinner() guards before every readline question,
and explicitly save gemma-4-uncensored when the user presses Enter at the
model prompt instead of silently leaving it unset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

* fix: switch setup wizard to readline/promises to survive Termux empty-Enter

The callback-based readline.createInterface closes on empty Enter in Termux,
which empties the event loop and silently exits the process before any
subsequent steps run. Switching to readline/promises (same API as repl.ts)
with terminal:true keeps the interface alive. Wrapping rl.question() in a
try/catch means a premature close resolves with '' (accept default) instead
of hanging forever.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

---------

Co-authored-by: Claude <noreply@anthropic.com>
… on Enter (#6)

readline (both callback and promise forms) becomes unreliable on Termux
after async HTTP operations — the event loop empties and Node exits
silently when the user presses Enter. Replace with raw process.stdin
data listener that survives across async boundaries.

Also simplify wizard to one interactive step (API key only); chat and
image models are auto-configured from the live model list without
further prompts, eliminating all remaining interactive readline paths.


Claude-Session: https://claude.ai/code/session_01Fs4ujw12eFunruUmRZEw7C

Co-authored-by: Claude <noreply@anthropic.com>
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