Skip to content

Commit 023fd85

Browse files
Merge remote-tracking branch 'upstream/main'
2 parents dda8c40 + b173600 commit 023fd85

14 files changed

Lines changed: 1145 additions & 57 deletions

.deepcode/AGENTS.md

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,47 @@
44

55
```
66
src/
7-
├── cli.tsx # Entry point — parses args, renders Ink App
7+
├── cli.tsx # Entry point — parses args (-p, -v), renders Ink App
88
├── session.ts # SessionManager — LLM loop, compaction, tool orchestration
99
├── settings.ts # Settings resolution from ~/.deepcode/settings.json
10-
├── prompt.ts # System prompt builder, tool definitions, agent-drift-guard skill
10+
├── prompt.ts # System prompt builder, tool definitions, built-in skills
1111
├── common/
1212
│ ├── model-capabilities.ts # Model detection and thinking-mode defaults
13+
│ ├── openai-thinking.ts # OpenAI thinking request options builder
1314
│ ├── file-utils.ts # File read/write with encoding and diff preview
1415
│ ├── shell-utils.ts # Shell path resolution (Git Bash, zsh, bash)
1516
│ ├── state.ts # In-memory file state and snippet tracking
16-
│ └── runtime.ts # Tool validation runtime helpers
17+
│ ├── runtime.ts # Tool validation runtime helpers
18+
│ ├── notify.ts # Desktop notification after LLM turn completion
19+
│ ├── debug-logger.ts # Debug logging for OpenAI API calls
20+
│ └── error-logger.ts # API error logging
1721
├── ui/
1822
│ ├── App.tsx # Root Ink component — state, routing, session orchestration
19-
│ ├── PromptInput.tsx # Multi-line input with slash commands, image paste, skills
23+
│ ├── PromptInput.tsx # Multi-line input with file mentions (@), slash commands, image paste, skills
2024
│ ├── MessageView.tsx # Renders assistant/tool messages with markdown
21-
│ ├── DropdownMenu.tsx # Reusable dropdown for skill/model selection
22-
│ ├── SessionList.tsx # Session picker for /resume
23-
│ ├── promptUndoRedo.ts # Ctrl+- undo / Ctrl+Shift+- redo for prompt input
25+
│ ├── McpStatusList.tsx # MCP server connection status and available tools
26+
│ ├── ProcessStdoutView.tsx # Ctrl+O fullscreen overlay for live process stdout
27+
│ ├── UpdatePrompt.tsx # UpdatePlan task list progress display
28+
│ ├── fileMentions.ts # @-mention file scanning, filtering, and insertion
2429
│ └── ...
2530
├── mcp/
2631
│ ├── mcp-client.ts # MCP client — JSON-RPC communication with MCP servers
27-
│ └── mcp-manager.ts # MCP manager — lifecycle, tool registration, execution
32+
│ └── mcp-manager.ts # MCP manager — lifecycle, tool registration, execution, status
2833
├── tools/
29-
│ ├── executor.ts # ToolExecutor — dispatches tool calls to handlers
30-
│ ├── bash-handler.ts # Executes shell commands
31-
│ ├── read-handler.ts # Reads files and images
34+
│ ├── executor.ts # ToolExecutor — dispatches tool calls to handlers (7 built-in)
35+
│ ├── bash-handler.ts # Executes shell commands with live stdout streaming
36+
│ ├── read-handler.ts # Reads files, images, PDFs, and notebooks
3237
│ ├── write-handler.ts # Creates/overwrites files
33-
│ ├── edit-handler.ts # Scoped string replacements in files
34-
│ ├── web-search-handler.ts # Web search tool
35-
│ └── ask-user-question-handler.ts # Interactive user prompts
36-
├── tests/ # Test suite — one *.test.ts per module
38+
│ ├── edit-handler.ts # Scoped string replacements with snippet tracking
39+
│ ├── update-plan-handler.ts # Updates the task plan progress display
40+
│ ├── web-search-handler.ts # Web search via natural language queries
41+
│ └── ask-user-question-handler.ts # Interactive user prompts with options
42+
├── tests/ # One *.test.ts per source module, plus run-tests.mjs
3743
templates/
3844
├── tools/ # Tool descriptions fed to the LLM
45+
├── skills/ # Built-in skill definitions (agent-drift-guard, plan-and-execute)
3946
├── prompts/ # EJS templates (e.g., init_command.md.ejs)
40-
docs/ # User-facing documentation
47+
docs/ # User-facing documentation (configuration, MCP, skills)
4148
dist/ # Bundled CLI output (gitignored)
4249
```
4350

@@ -80,7 +87,7 @@ Run the CLI locally for manual testing: `node dist/cli.js` (after `npm run bundl
8087
- **Coverage**: Target meaningful unit tests for core logic (session management, tool handlers, settings resolution, prompt buffer). Test files are in `src/tests/` matching the source module name.
8188
- **Test naming**: `describe`/`test` blocks with descriptive names. Example: `test("SessionManager preserves structured system content when building OpenAI messages", ...)`
8289
- **Relaxed lint rules**: Test files allow `any` and unused vars.
83-
- Run all tests with `npm test` before submitting a PR.
90+
- Run all tests with `npm test` before submitting a PR. A cross-platform test runner is available at `src/tests/run-tests.mjs`.
8491

8592
## Commit & Pull Request Guidelines
8693

@@ -102,12 +109,18 @@ Run the CLI locally for manual testing: `node dist/cli.js` (after `npm run bundl
102109

103110
## Architecture Overview
104111

105-
The CLI renders a terminal UI using [Ink](https://github.com/vadimdemedes/ink) (React for terminals). `SessionManager` drives the LLM interaction loop: it builds system prompts, sends user messages with optional skills/images, streams responses, executes tool calls via `ToolExecutor`, and compacts context when token thresholds are exceeded (512K for DeepSeek V4 models, 128K for others).
112+
The CLI (`@vegamo/deepcode-cli`) renders a terminal UI using [Ink](https://github.com/vadimdemedes/ink) (React for terminals). `SessionManager` drives the LLM interaction loop: it builds system prompts, sends user messages with optional skills/images, streams responses, executes tool calls via `ToolExecutor`, and compacts context when token thresholds are exceeded (512K for DeepSeek V4 models, 128K for others).
106113

107-
Six tools are available to the LLM: `bash`, `read`, `write`, `edit`, `AskUserQuestion`, and `WebSearch`. Tool definitions are registered in `src/tools/executor.ts` and described to the LLM via `src/prompt.ts` and `templates/tools/`.
114+
Seven built-in tools are available to the LLM: `bash`, `read`, `write`, `edit`, `AskUserQuestion`, `UpdatePlan`, and `WebSearch`. Tool definitions are registered in `src/tools/executor.ts` and described to the LLM via `src/prompt.ts` and `templates/tools/`. The `UpdatePlan` tool enables the LLM to display and update a structured task list in the terminal.
115+
116+
**Slash commands**: `/model`, `/new`, `/init`, `/resume`, `/continue`, `/mcp`, `/exit`, plus dynamic `/skill-name` for each loaded skill.
117+
118+
**Key UI features**: `@` file mentions in the prompt input (scans project files), `Ctrl+O` to view live process stdout in fullscreen, `Ctrl+V` to paste images, MCP server status display.
119+
120+
**CLI flags**: `-p <prompt>` / `--prompt` to auto-submit a prompt on launch, `-v` / `--version`, `-h` / `--help`.
108121

109122
## Agent-Specific Instructions
110123

111124
- **AGENTS.md loading**: The CLI loads agent instructions from `./AGENTS.md`, `./.deepcode/AGENTS.md`, or `~/.deepcode/AGENTS.md` (first found wins). Write project-specific guidance for the LLM in any of these.
112125
- **Skills**: Place skill definitions in `~/.agents/skills/<name>/SKILL.md` (user-level) or `./.agents/skills/<name>/SKILL.md` (project-level). Legacy path `./.deepcode/skills/` is also supported. Each SKILL.md uses YAML frontmatter with `name` and `description` fields.
113-
- The built-in `agent-drift-guard` skill is always injected into every session.
126+
- **Built-in skills**: `agent-drift-guard` (detects and corrects execution drift) and `plan-and-execute` (structured task planning with progress tracking). Both are defined in `templates/skills/` and always injected into every session.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@vegamo/deepcode-cli",
3-
"version": "0.1.21",
3+
"version": "0.1.22",
44
"description": "Deep Code CLI - Vibe coding for the deepseek-v4 model in your terminal",
55
"license": "MIT",
66
"type": "module",

src/common/process-tree.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { spawnSync } from "child_process";
2+
3+
type TaskkillSpawnSync = (
4+
command: string,
5+
args: string[],
6+
options: { stdio: "ignore"; windowsHide: true }
7+
) => { status: number | null; error?: Error };
8+
9+
export type KillProcessTreeDeps = {
10+
platform?: NodeJS.Platform;
11+
killPid?: (pid: number, signal: NodeJS.Signals) => void;
12+
runTaskkill?: (pid: number) => boolean;
13+
killGroupOnNonWindows?: boolean;
14+
};
15+
16+
export function killProcessTree(
17+
pid: number,
18+
signal: NodeJS.Signals = "SIGKILL",
19+
deps: KillProcessTreeDeps = {}
20+
): boolean {
21+
if (!Number.isInteger(pid) || pid <= 0) {
22+
return false;
23+
}
24+
25+
const platform = deps.platform ?? process.platform;
26+
const killPid = deps.killPid ?? ((targetPid, targetSignal) => process.kill(targetPid, targetSignal));
27+
28+
if (platform === "win32") {
29+
const runTaskkill = deps.runTaskkill ?? runWindowsTaskkill;
30+
if (runTaskkill(pid)) {
31+
return true;
32+
}
33+
return killDirectProcess(pid, signal, killPid);
34+
}
35+
36+
if (deps.killGroupOnNonWindows !== false && killDirectProcess(-pid, signal, killPid)) {
37+
return true;
38+
}
39+
return killDirectProcess(pid, signal, killPid);
40+
}
41+
42+
export function runWindowsTaskkill(pid: number, spawnSyncImpl: TaskkillSpawnSync = spawnSync): boolean {
43+
const result = spawnSyncImpl("taskkill", ["/PID", String(pid), "/T", "/F"], {
44+
stdio: "ignore",
45+
windowsHide: true,
46+
});
47+
return !result.error && result.status === 0;
48+
}
49+
50+
function killDirectProcess(
51+
pid: number,
52+
signal: NodeJS.Signals,
53+
killPid: (pid: number, signal: NodeJS.Signals) => void
54+
): boolean {
55+
try {
56+
killPid(pid, signal);
57+
return true;
58+
} catch {
59+
return false;
60+
}
61+
}

src/mcp/mcp-client.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { spawn, type ChildProcess } from "child_process";
22
import { createInterface, type Interface } from "readline";
33
import * as os from "os";
44
import * as path from "path";
5+
import { killProcessTree } from "../common/process-tree";
56

67
type JsonRpcRequest = {
78
jsonrpc: "2.0";
@@ -292,7 +293,11 @@ export class McpClient {
292293
this.reader = null;
293294
}
294295
if (this.process) {
295-
this.process.kill();
296+
if (typeof this.process.pid === "number") {
297+
killProcessTree(this.process.pid, "SIGTERM", { killGroupOnNonWindows: false });
298+
} else {
299+
this.process.kill();
300+
}
296301
this.process = null;
297302
}
298303
}

src/session.ts

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { McpManager } from "./mcp/mcp-manager";
2222
import type { McpServerConfig } from "./settings";
2323
import { logApiError } from "./common/error-logger";
2424
import { logOpenAIChatCompletionDebug, normalizeDebugError } from "./common/debug-logger";
25+
import { killProcessTree } from "./common/process-tree";
2526

2627
const MAX_SESSION_ENTRIES = 50;
2728
const DEFAULT_NEW_PROMPT_API_URL = "https://deepcode.vegamo.cn/api/plugin/new";
@@ -1364,17 +1365,11 @@ ${skillMd}
13641365
const killedPids: number[] = [];
13651366
const failedPids: number[] = [];
13661367
for (const pid of processIds) {
1367-
const killedGroup = this.killProcessGroup(pid);
1368-
if (killedGroup) {
1368+
if (killProcessTree(pid, "SIGKILL")) {
13691369
killedPids.push(pid);
13701370
continue;
13711371
}
1372-
try {
1373-
process.kill(pid, "SIGKILL");
1374-
killedPids.push(pid);
1375-
} catch {
1376-
failedPids.push(pid);
1377-
}
1372+
failedPids.push(pid);
13781373
}
13791374

13801375
const controller = this.sessionControllers.get(sessionId);
@@ -2055,6 +2050,8 @@ ${skillMd}
20552050
}
20562051
} else if (toolName === "UpdatePlan") {
20572052
return typeof args.explanation === "string" ? args.explanation.trim() : "";
2053+
} else if (toolName === "write") {
2054+
return typeof args.file_path === "string" ? args.file_path.trim() : "";
20582055
}
20592056

20602057
const firstKey = Object.keys(args)[0];
@@ -2189,18 +2186,6 @@ ${skillMd}
21892186
);
21902187
}
21912188

2192-
private killProcessGroup(pid: number): boolean {
2193-
if (process.platform === "win32") {
2194-
return false;
2195-
}
2196-
try {
2197-
process.kill(-pid, "SIGKILL");
2198-
return true;
2199-
} catch {
2200-
return false;
2201-
}
2202-
}
2203-
22042189
private normalizeSessionEntry(entry: unknown): SessionEntry {
22052190
const value = entry && typeof entry === "object" ? (entry as Record<string, unknown>) : {};
22062191
return {

0 commit comments

Comments
 (0)