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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</p>

<p align="center">
<img alt="Tests" src="https://img.shields.io/badge/tests-1581_passing-brightgreen?style=flat-square" />
<img alt="Tests" src="https://img.shields.io/badge/tests-913_passing-brightgreen?style=flat-square" />
<img alt="Tools" src="https://img.shields.io/badge/tools-25-blue?style=flat-square" />
<img alt="Commands" src="https://img.shields.io/badge/commands-40-blue?style=flat-square" />
<img alt="npm" src="https://img.shields.io/npm/v/@ruvnet/open-claude-code?style=flat-square&label=npm" />
Expand Down Expand Up @@ -52,7 +52,7 @@ A **Cursor-style AI coding assistant** built directly into VSCode — no termina
The extension package is included in the repo and ready to install:

```bash
code --install-extension vscode-extension/open-claude-code-1.1.0.vsix
code --install-extension vscode-extension/open-claude-code-1.2.0.vsix
```

Or use **Extensions → … → Install from VSIX…** and pick the file from the `vscode-extension/` folder.
Expand All @@ -63,7 +63,7 @@ Or use **Extensions → … → Install from VSIX…** and pick the file from th
cd vscode-extension
npm install
npm run package # prepackage → package → postpackage
code --install-extension open-claude-code-1.1.0.vsix
code --install-extension open-claude-code-1.2.0.vsix
```

### Highlights
Expand All @@ -78,6 +78,7 @@ code --install-extension open-claude-code-1.1.0.vsix
- **Multi-provider** — Anthropic Claude, OpenAI GPT, Google Gemini, NVIDIA NIM
- **Model & permission-mode selector** — switch model and mode directly from the UI
- **Session stats** — token count, cost estimate, and elapsed time always visible
- **Proactive workspace analysis** — the agent explores your project automatically before answering; never asks you to paste code

See [`vscode-extension/README.md`](./vscode-extension/README.md) for the full setup and configuration guide.

Expand Down Expand Up @@ -349,8 +350,10 @@ NVIDIA_API_KEY=nvapi-... occ -m kimi-k2.5 "hello"

> **Note — NVIDIA thinking models:** Models such as `kimi-k2.5` and `deepseek-r1` use
> `chat_template_kwargs: {thinking: true}` and do not accept a `tools` array in the same
> request. Open Claude Code automatically detects these models and omits tools from the
> request, preventing the HTTP 400 error that previously made them unusable.
> request. Open Claude Code automatically detects these models, omits tools from the
> request (preventing the HTTP 400 error), and injects a compact workspace file-tree
> snapshot into the system prompt so the model still has full structural awareness of
> your project without needing live tool calls.

---

Expand Down Expand Up @@ -402,6 +405,14 @@ This is a **clean-room implementation** — no leaked source used. Architecture

## 🆕 What's New

### v1.2.0 — Proactive Workspace Analysis

**Fix: Proactive workspace analysis for all models** _(this PR)_
- All models now receive a strong agentic system prompt declaring the workspace `cwd` and instructing them to explore files with LS / Glob / Read / Grep / Bash before answering — never asking the user to paste code
- New `buildWorkspaceSnapshot` helper recursively walks the workspace (skipping `node_modules`, `.git`, `dist`, etc.) and returns a compact indented file tree capped at 200 entries
- Kimi K2.5 and DeepSeek R1 (NVIDIA thinking models) now have the file tree injected directly into their system prompt — giving them full structural awareness even though NVIDIA NIM prevents live tool calls during thinking mode
- Extension model descriptions updated; version bumped to 1.2.0

### v1.1.0 — VSCode Extension & Bug Fixes

**VSCode Extension — Cursor-style sidebar panel** _(PR #2)_
Expand Down
10 changes: 8 additions & 2 deletions v2/src/core/agent-loop.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
import { streamResponse, accumulateStream } from './streaming.mjs';
import { ContextManager } from './context-manager.mjs';
import { buildSystemPrompt } from './system-prompt.mjs';
import { buildSystemPrompt, buildWorkspaceSnapshot } from './system-prompt.mjs';
import { isNvidiaModel } from './providers.mjs';
import fs from 'fs';
import path from 'path';
Expand Down Expand Up @@ -406,12 +406,18 @@ async function callNvidia(model, state, toolDefs, settings, stream) {

// For thinking models the tool-list suffix in the system prompt would be
// misleading (no tools are sent), so use the static prefix only.
// Additionally, inject a workspace file-tree snapshot so the model can
// reason about the project structure even without tool access.
let systemPrompt = state.systemPrompt;
if (supportsThinking) {
if (!state.systemPromptStatic) {
process.stderr.write('[open-claude-code] Warning: systemPromptStatic missing — falling back to full system prompt for ' + model + '\n');
}
systemPrompt = state.systemPromptStatic || state.systemPrompt;
const base = state.systemPromptStatic || state.systemPrompt;
const snapshot = buildWorkspaceSnapshot(process.cwd());
systemPrompt = snapshot
? base + '\n\n## Workspace file structure (read-only reference)\n\n```\n' + snapshot + '\n```'
: base;
}
const effectiveState = supportsThinking
? { ...state, systemPrompt }
Expand Down
86 changes: 85 additions & 1 deletion v2/src/core/system-prompt.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,78 @@
* - Merges in order (global -> project -> local)
* - Splits at cache boundary (static prefix cached, dynamic suffix not)
* - Includes tool schemas in the system prompt
* - Exports buildWorkspaceSnapshot for injecting a file-tree into prompts
*/
import fs from 'fs';
import path from 'path';
import os from 'os';

// Directories to skip when building the workspace snapshot.
const SNAPSHOT_EXCLUDE = new Set([
'node_modules', '.git', 'dist', 'build', 'out', '.next', '.nuxt',
'__pycache__', '.cache', 'coverage', '.nyc_output', '.turbo',
]);

// Dot-files/dirs are hidden by default; these are shown because they are
// commonly checked into source control and useful for project analysis.
const SNAPSHOT_INCLUDE_DOTFILES = new Set([
'.env.example', '.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml',
'.prettierrc', '.prettierrc.js', '.prettierrc.json', '.prettierrc.yml',
'.babelrc', '.babelrc.js', '.gitignore', '.dockerignore', '.editorconfig',
]);

/**
* Build a compact, indented directory tree for a workspace.
*
* The tree is capped at `maxFiles` entries so it never bloats the prompt.
* Directories that match SNAPSHOT_EXCLUDE are skipped entirely.
* Any I/O error returns an empty string gracefully.
*
* @param {string} [cwd] - root directory to scan (defaults to process.cwd())
* @param {number} [maxFiles=200] - maximum number of entries to include
* @returns {string} indented tree string, or '' on error / empty workspace
*/
export function buildWorkspaceSnapshot(cwd = process.cwd(), maxFiles = 200) {
const lines = [];
let count = 0;

function walk(dir, indent) {
if (count >= maxFiles) return;
let entries;
try {
entries = fs.readdirSync(dir, { withFileTypes: true });
} catch {
return;
}
// Directories first, then files, both sorted alphabetically
entries.sort((a, b) => {
if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
return a.name.localeCompare(b.name);
});
for (const entry of entries) {
if (count >= maxFiles) {
lines.push(indent + '… (truncated)');
return;
}
if (SNAPSHOT_EXCLUDE.has(entry.name)) continue;
if (entry.name.startsWith('.') && !SNAPSHOT_INCLUDE_DOTFILES.has(entry.name)) continue;
lines.push(indent + entry.name + (entry.isDirectory() ? '/' : ''));
count++;
if (entry.isDirectory()) {
walk(path.join(dir, entry.name), indent + ' ');
}
}
}

try {
walk(path.resolve(cwd), '');
} catch {
return '';
}

return lines.join('\n');
}

/**
* Load all CLAUDE.md files and merge them in order.
* @param {string} [cwd] - current working directory
Expand Down Expand Up @@ -67,7 +134,24 @@ export function buildSystemPrompt({ cwd, tools, override, addDirs } = {}) {
return { staticPrefix: override, dynamicSuffix: '', full: override };
}

const parts = ['You are an AI coding assistant.'];
const workspaceRoot = path.resolve(cwd || process.cwd());
const basePreamble = [
`You are an AI coding assistant with direct access to the user's workspace on disk.`,
`Current working directory: ${workspaceRoot}`,
``,
`## Workspace exploration rules`,
``,
`- When the user asks about their project, code, or files, ALWAYS use your tools to`,
` explore the workspace first — do NOT ask the user to paste or share anything.`,
`- Use LS / Glob to discover files, Read to inspect their contents, Grep to search`,
` for patterns, and Bash to run commands (tests, builds, linters, git log, etc.).`,
`- Start broad (list the root directory) then drill into relevant subdirectories.`,
`- Prefer reading the actual source over guessing from file names alone.`,
`- Never say "I don't see any files" or ask the user to share code — you can read`,
` the workspace directly with your tools.`,
].join('\n');

const parts = [basePreamble];

// Load CLAUDE.md files
const mdFiles = loadClaudeMdFiles(cwd);
Expand Down
19 changes: 18 additions & 1 deletion v2/test/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,10 +1148,13 @@ assert(foundTruncated, 'Micro-compaction truncates old tool results');

section('Phase 2: System Prompt');

import { buildSystemPrompt, loadClaudeMdFiles, toCacheBlocks } from '../src/core/system-prompt.mjs';
import { buildSystemPrompt, loadClaudeMdFiles, toCacheBlocks, buildWorkspaceSnapshot } from '../src/core/system-prompt.mjs';

const prompt = buildSystemPrompt({ cwd: '/tmp' });
assertIncludes(prompt.full, 'AI coding assistant', 'System prompt has base text');
assertIncludes(prompt.full, 'Current working directory:', 'System prompt declares cwd');
assertIncludes(prompt.full, 'Workspace exploration rules', 'System prompt has exploration rules');
assertIncludes(prompt.full, 'do NOT ask the user to paste', 'System prompt forbids asking user to paste code');
assertType(prompt.staticPrefix, 'string', 'Has static prefix');
assertType(prompt.dynamicSuffix, 'string', 'Has dynamic suffix');

Expand All @@ -1169,6 +1172,20 @@ assertEqual(blocks.length, 2, 'Two cache blocks');
assertEqual(blocks[0].cache_control.type, 'ephemeral', 'Static block cached');
assert(blocks[1].cache_control === undefined, 'Dynamic block not cached');

// buildWorkspaceSnapshot
assertType(buildWorkspaceSnapshot, 'function', 'buildWorkspaceSnapshot is exported');
const snapshot = buildWorkspaceSnapshot('/tmp');
assertType(snapshot, 'string', 'Snapshot returns a string');
// Non-existent path returns empty string gracefully
const snapshotBad = buildWorkspaceSnapshot('/nonexistent_path_xyz_12345');
assertEqual(snapshotBad, '', 'Non-existent path returns empty string');
// Snapshot respects maxFiles cap: with maxFiles=1, we expect at most 1 entry
// plus a possible "… (truncated)" line = 2 non-blank lines maximum. Allow
// up to 3 to accommodate edge cases where /tmp itself is almost empty.
const snapshotCapped = buildWorkspaceSnapshot('/tmp', 1);
const snapshotLines = snapshotCapped.split('\n').filter(l => l.trim());
assert(snapshotLines.length <= 3, 'Snapshot respects maxFiles cap (at most 1 entry + truncation line)');

// ---------- Phase 3: CLI, UI, Commands Tests ----------

section('Phase 3: CLI Args (full flags)');
Expand Down
21 changes: 19 additions & 2 deletions vscode-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ A **Cursor-style AI coding assistant** built directly into VSCode — no termina

## Features

### 🗂️ Proactive workspace analysis (new in v1.2)
- **Automatic workspace exploration** — before answering questions the agent scans your project with LS, Glob, Read, and Grep instead of asking you to paste code
- **Workspace file tree injection for thinking models** — Kimi K2.5 and DeepSeek R1 receive a compact file-tree snapshot in their system prompt so they know your project layout even though NVIDIA NIM prevents live tool calls during thinking
- **Never "I can't see your files"** — the system prompt explicitly forbids asking you to share code; the agent reads files directly

### 🖥️ Cursor-style Sidebar Panel (new in v1.1)
- **Dedicated activity bar icon** — opens a full chat panel in the VS Code sidebar
- **Rich markdown rendering** — headers, tables, bold/italic, blockquotes
Expand All @@ -25,7 +30,7 @@ A **Cursor-style AI coding assistant** built directly into VSCode — no termina
### 💬 `@claude` Chat Participant (VSCode built-in chat)
- Ask questions, request code changes, and run agentic tools without leaving the editor
- **Full tool access** — the same 25+ tools as the CLI (Read, Write, Edit, Bash, Glob, Grep, WebFetch, …)
- **Multi-provider** — Anthropic Claude, OpenAI GPT, Google Gemini
- **Multi-provider** — Anthropic Claude, OpenAI GPT, Google Gemini, NVIDIA NIM
- **Conversation memory** — history is maintained across turns in the same VS Code session
- **Slash commands** — `/clear` to reset, `/model` to switch models mid-session
- **Configurable permission mode** — control how aggressively the agent modifies your files
Expand Down Expand Up @@ -72,7 +77,7 @@ A **Cursor-style AI coding assistant** built directly into VSCode — no termina
extension bundle so all functionality is available after installation.
3. Install it in VSCode:
```bash
code --install-extension open-claude-code-1.1.0.vsix
code --install-extension open-claude-code-1.2.0.vsix
```
Or use **Extensions → … → Install from VSIX…** in the VSCode UI.

Expand Down Expand Up @@ -212,6 +217,18 @@ The subprocess persists across chat turns so the agent's conversation history is

---

### NVIDIA thinking models (Kimi K2.5, DeepSeek R1)

NVIDIA NIM rejects requests that combine `chat_template_kwargs.thinking` with a tools array, so these models cannot make live tool calls. Open Claude Code works around this automatically:

- The agent omits tools from the request (preventing the HTTP 400 error)
- A compact workspace file tree is appended to the system prompt so the model knows your project layout without needing live tool access
- The system prompt instructs the model to reason about files by path rather than asking you to paste them

To use a thinking model, select **moonshotai/kimi-k2.5** or **deepseek-ai/deepseek-r1** from the Model dropdown and enter your `NVIDIA_API_KEY` in Settings.

---

## Troubleshooting

**"Failed to start agent"**
Expand Down
8 changes: 4 additions & 4 deletions vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "open-claude-code",
"displayName": "Open Claude Code",
"description": "AI coding assistant powered by Claude — Cursor-style chat panel with file context, code apply, tool visualization and more",
"version": "1.1.0",
"version": "1.2.0",
"publisher": "open-claude-code",
"engines": {
"vscode": "^1.90.0"
Expand Down Expand Up @@ -112,13 +112,13 @@
"OpenAI GPT-4o",
"OpenAI GPT-4o Mini",
"Google Gemini 2.0 Flash",
"NVIDIA NIM — Moonshot AI Kimi K2.5 (with thinking)",
"NVIDIA NIM — Moonshot AI Kimi K2.5 (thinking mode; workspace file tree injected automatically)",
"NVIDIA NIM — Llama 3.1 Nemotron 70B Instruct",
"NVIDIA NIM — Meta Llama 3.1 405B Instruct",
"NVIDIA NIM — Meta Llama 3.3 70B Instruct",
"NVIDIA NIM — Mistral Large 2 Instruct",
"NVIDIA NIM — Mixtral 8x22B Instruct",
"NVIDIA NIM — DeepSeek R1 (with thinking)"
"NVIDIA NIM — DeepSeek R1 (thinking mode; workspace file tree injected automatically)"
],
"description": "AI model to use for the chat participant"
},
Expand Down Expand Up @@ -170,7 +170,7 @@
"prepackage": "node scripts/prepare-bundle.js",
"package": "vsce package --no-dependencies",
"postpackage": "node scripts/cleanup-bundle.js",
"install-vsix": "code --install-extension open-claude-code-1.1.0.vsix"
"install-vsix": "code --install-extension open-claude-code-1.2.0.vsix"
},
"devDependencies": {
"@types/vscode": "^1.90.0",
Expand Down