Skip to content
This repository was archived by the owner on Oct 10, 2026. It is now read-only.
Closed
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
2 changes: 1 addition & 1 deletion CLAUDE.md
79 changes: 25 additions & 54 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,65 +1,36 @@
# Contributing
# Contributing to T3 Code (Gemini Fork)

## Read This First
First off, thank you for considering contributing to this fork! It's people like you that make the open-source community such an amazing place to learn, inspire, and create.

We are not actively accepting contributions right now.
This fork is maintained by [mtdewwolf](https://github.com/mtdewwolf) and focuses on enhancing Gemini CLI support and improving the core orchestration and persistence layers.

You can still open an issue or PR, but please do so knowing there is a high chance we close it, defer it forever, or never look at it.
## How Can I Contribute?

If that sounds annoying, that is because it is. This project is still early and we are trying to keep scope, quality, and direction under control.
### Reporting Bugs
Before creating bug reports, please check the existing issues to see if the problem has already been reported. When you are creating a bug report, please include as many details as possible.

PRs are automatically labeled with a `vouch:*` trust status and a `size:*` diff size based on changed lines.
### Suggesting Enhancements
Enhancement suggestions are tracked as GitHub issues. When creating an enhancement suggestion, please:
* Use a clear and descriptive title.
* Provide a step-by-step description of the suggested enhancement.
* Explain why this enhancement would be useful.

If you are an external contributor, expect `vouch:unvouched` until we explicitly add you to [.github/VOUCHED.td](.github/VOUCHED.td).
### Pull Requests
We welcome pull requests! To help us review your PR effectively, please:
1. **Keep it focused:** Try to keep PRs small and focused on a single change.
2. **Test your changes:** Ensure that your changes don't break existing functionality. Add new tests if possible.
3. **Document your changes:** Update documentation (like the README) if your changes introduce new features or change existing ones.
4. **Describe your PR:** Use the pull request template to describe what changed and why.

## What We Are Most Likely To Accept
## Development Setup

Small, focused bug fixes.
1. Clone the repository: `git clone https://github.com/mtdewwolf/t3code-gemini.git`

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 | 🟠 Major

Fix clone target to the active fork repository.

The setup command currently clones mtdewwolf/t3code-gemini, which can route contributors away from this fork’s contribution flow.

Suggested doc fix
-1.  Clone the repository: `git clone https://github.com/mtdewwolf/t3code-gemini.git`
+1.  Clone the repository: `git clone https://github.com/aaditagrawal/t3code.git`

Based on learnings: "Always explicitly target the fork with --repo aaditagrawal/t3code when running gh pr create. Never run gh write commands against any repo other than aaditagrawal/t3code."

📝 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
1. Clone the repository: `git clone https://github.com/mtdewwolf/t3code-gemini.git`
1. Clone the repository: `git clone https://github.com/aaditagrawal/t3code.git`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CONTRIBUTING.md` at line 27, Update the clone target in CONTRIBUTING.md so
contributors clone this fork instead of mtdewwolf's repo: replace the URL string
"https://github.com/mtdewwolf/t3code-gemini.git" with the fork URL
"https://github.com/aaditagrawal/t3code.git" and add a brief note to always use
--repo aaditagrawal/t3code when running gh pr create to ensure PRs target this
repository.

2. Install dependencies: `bun install`
3. Run the development server: `bun run dev`

Small reliability fixes.
## Code Style
Please try to match the existing code style. We use `oxlint` for linting and `oxfmt` for formatting.

Small performance improvements.
---

Tightly scoped maintenance work that clearly improves the project without changing its direction.

## What We Are Least Likely To Accept

Large PRs.

Drive-by feature work.

Opinionated rewrites.

Anything that expands product scope without us asking for it first.

If you open a 1,000+ line PR full of new features, we will probably close it quickly and remember that you ignored the clearly written instructions.

## If You Still Want To Open A PR

Keep it small.

Explain exactly what changed.

Explain exactly why the change should exist.

Do not mix unrelated fixes together.

If the PR makes anything resembling a UI change, include clear before/after images.

If the change depends on motion, timing, transitions, or interaction details, include a short video.

If we have to guess what changed, we are much less likely to review it.

## Issues First

If you are thinking about a non-trivial change, open an issue first.

That still does not mean we will want the PR, but it gives you a chance to avoid wasting your time.

## Be Realistic

Opening a PR does not create an obligation on our side.

We may close it. We may ignore it. We may ask you to shrink it. We may reimplement the idea ourselves later.

If you are fine with that, proceed.
Special thanks to the original creators at [Pingdotgg](https://github.com/pingdotgg) and the maintainers of the [aaditagrawal fork](https://github.com/aaditagrawal/t3code) for the solid foundation.
51 changes: 22 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
# T3 Code
# T3 Code (Gemini Fork)

T3 Code is a minimal web GUI for coding agents made by [Pingdotgg](https://github.com/pingdotgg). This project is a downstream fork of [T3 Code](https://github.com/pingdotgg/t3code) customised to my utility and includes various PRs/feature additions from the upstream repo. Thanks to the team and its maintainers for keeping it OSS and an upstream to look up to.
T3 Code is a minimal web GUI for coding agents made by [Pingdotgg](https://github.com/pingdotgg). This project is a downstream fork of [aaditagrawal's T3 Code](https://github.com/aaditagrawal/t3code), which itself is a fork of the original [T3 Code](https://github.com/pingdotgg/t3code).

This fork by [mtdewwolf](https://github.com/mtdewwolf) focuses on enhancing the Gemini CLI integration, improving persistence layers, and refining the provider management system.

It supports Codex, Claude Code, Cursor, Copilot, Gemini CLI, Amp, Kilo, and OpenCode.

(NOTE: Amp /mode free is not supported, as Amp Code doesn't support it in headless mode - since they need to show ads for that business model to work.)

## Why the fork?
## Why this fork?

This fork is designed to keep up a faster rate of development customised to my needs (and if you want, _yours_ as well -> Submit an issue and I'll make a PR for it). There's certain features which will (rightly) remain out of scope/priority for the project at its scale, but might be required for someone like me.
This fork aims to provide a more robust and feature-rich experience for Gemini CLI users, with improved server management and more reliable persistence of orchestration events. It also includes several UI refinements for settings and model selection.

### Multi-provider support
### Multi-provider support (Enhanced)

Adds full provider adapters (server managers, service layers, runtime layers) for agents that are not yet on the upstream roadmap:

| Provider | What's included |
| ----------- | ------------------------------------------------------------------------- |
| Gemini CLI | **Enhanced:** Adapter + `geminiCliServerManager` with full test coverage |
| Amp | Adapter + `ampServerManager` for headless Amp sessions |
| Copilot | Adapter + CLI binary resolution + text generation layer |
| Cursor | Adapter + ACP probe integration + usage tracking |
| Gemini CLI | Adapter + `geminiCliServerManager` with full test coverage |
| Kilo | Adapter + `kiloServerManager` + OpenCode-style server URL config |
| OpenCode | Adapter + `opencodeServerManager` with hostname/port/workspace config |
| Claude Code | Full adapter with permission mode, thinking token limits, and SDK typings |

### Persistence & Orchestration Improvements

- **Normalized Provider Kinds:** Migration added to handle legacy provider kind naming consistently.
- **Improved Event Store:** Robust persistence layer for orchestration events with better error handling.
- **Session Management:** refined `ProviderSessionDirectory` for better tracking of active sessions.

### UX enhancements

| Feature | Description |
Expand All @@ -36,19 +44,6 @@ Adds full provider adapters (server managers, service layers, runtime layers) fo
| Plan sidebar | Dedicated panel for reviewing, downloading, or saving proposed agent plans |
| Terminal drawer | Theme-aware integrated terminal with accent color styling |

### Branding & build

- Custom abstract-mark app icon with macOS icon composer support
- Centralized branding constants for easy identity swaps
- Desktop icon asset generation pipeline from SVG source

### Developer tooling

- `sync-upstream-pr-tracks` script for tracking cherry-picked upstream PRs
- `cursor-acp-probe` for testing Cursor Agent Communication Protocol
- Custom alpha workflow playbook (`docs/custom-alpha-workflow.md`)
- Upstream PR tracking config (`config/upstream-pr-tracks.json`)

## Getting started

### Quick install (recommended)
Expand All @@ -57,42 +52,40 @@ Run the interactive installer — it detects your OS, checks prerequisites (git,

```bash
# macOS / Linux / WSL
bash <(curl -fsSL https://raw.githubusercontent.com/aaditagrawal/t3code/main/scripts/install.sh)
bash <(curl -fsSL https://raw.githubusercontent.com/mtdewwolf/t3code-gemini/main/scripts/install.sh)
```
Comment on lines +55 to 56

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 | 🟠 Major

Update installer and clone URLs to this fork target.

Current commands point to mtdewwolf/t3code-gemini, which can send users to a different repository than this fork’s expected workflow.

Suggested doc fix
-bash <(curl -fsSL https://raw.githubusercontent.com/mtdewwolf/t3code-gemini/main/scripts/install.sh)
+bash <(curl -fsSL https://raw.githubusercontent.com/aaditagrawal/t3code/main/scripts/install.sh)

-bash <(curl -fsSL https://raw.githubusercontent.com/mtdewwolf/t3code-gemini/main/scripts/install.sh)
+bash <(curl -fsSL https://raw.githubusercontent.com/aaditagrawal/t3code/main/scripts/install.sh)

-git clone https://github.com/mtdewwolf/t3code-gemini.git
-cd t3code-gemini
+git clone https://github.com/aaditagrawal/t3code.git
+cd t3code

Based on learnings: "Never create PRs, push branches, post comments, or perform any write operation against pingdotgg/t3code or any upstream/third-party repo. All write operations must target aaditagrawal/t3code fork only."

Also applies to: 60-61, 70-71

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

In `@README.md` around lines 55 - 56, Replace all occurrences of the installer and
clone URLs that point to "mtdewwolf/t3code-gemini" with the fork target
"aaditagrawal/t3code"; specifically update the curl install URL
"https://raw.githubusercontent.com/mtdewwolf/t3code-gemini/main/scripts/install.sh"
and any git clone or raw.githubusercontent links elsewhere (noted around the
other occurrences) so they reference
"https://raw.githubusercontent.com/aaditagrawal/t3code/..." or
"https://github.com/aaditagrawal/t3code.git" as appropriate to ensure all README
install/clone commands target the fork.


```powershell
# Windows (Git Bash, MSYS2, or WSL)
bash <(curl -fsSL https://raw.githubusercontent.com/aaditagrawal/t3code/main/scripts/install.sh)
bash <(curl -fsSL https://raw.githubusercontent.com/mtdewwolf/t3code-gemini/main/scripts/install.sh)
```

The installer supports **npm, yarn, pnpm, bun, and deno** detection, and will auto-install bun if no suitable package manager is found. It provides OS-specific install instructions for any missing prerequisites (Homebrew on macOS, apt/dnf/pacman on Linux, winget on Windows).

### Manual build

> [!WARNING]
> You need at least one supported coding agent installed and authorized. See the supported agents list below.

```bash
# Prerequisites: Bun >=1.3.9, Node >=24.13.1
git clone https://github.com/aaditagrawal/t3code.git
cd t3code
git clone https://github.com/mtdewwolf/t3code-gemini.git
cd t3code-gemini
bun install
bun run dev
```

## Supported agents

- [Codex CLI](https://github.com/openai/codex) (requires v0.37.0 or later)
- [Gemini CLI](https://github.com/google-gemini/gemini-cli)
- [Claude Code](https://github.com/anthropics/claude-code)
- [Cursor](https://cursor.sh)
- [Codex CLI](https://github.com/openai/codex) (requires v0.37.0 or later)
- [Copilot](https://github.com/features/copilot)
- [Gemini CLI](https://github.com/google-gemini/gemini-cli)
- [Amp](https://ampcode.com)
- [Kilo](https://kilo.dev)
- [OpenCode](https://opencode.ai)

## Notes

- This project is very early in development. Expect bugs. (Especially with my fork)
- This project is very early in development. Expect bugs.
- Interested in contributing? See [CONTRIBUTING.md](CONTRIBUTING.md).
- Maintaining a custom fork or alpha branch? See [docs/custom-alpha-workflow.md](docs/custom-alpha-workflow.md).
- Special thanks to [Pingdotgg](https://github.com/pingdotgg) for the original project and [aaditagrawal](https://github.com/aaditagrawal) for the foundational fork.
119 changes: 119 additions & 0 deletions apps/server/src/commandPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { accessSync, constants, statSync } from "node:fs";
import { extname, join } from "node:path";

interface CommandPathOptions {
readonly platform?: NodeJS.Platform;
readonly env?: NodeJS.ProcessEnv;
}

function stripWrappingQuotes(value: string): string {
return value.replace(/^"+|"+$/g, "");
}

function resolvePathEnvironmentVariable(env: NodeJS.ProcessEnv): string {
return env.PATH ?? env.Path ?? env.path ?? "";
}

function resolveWindowsPathExtensions(env: NodeJS.ProcessEnv): ReadonlyArray<string> {
const rawValue = env.PATHEXT;
const fallback = [".COM", ".EXE", ".BAT", ".CMD"];
if (!rawValue) return fallback;

const parsed = rawValue
.split(";")
.map((entry) => entry.trim())
.filter((entry) => entry.length > 0)
.map((entry) => (entry.startsWith(".") ? entry.toUpperCase() : `.${entry.toUpperCase()}`));
return parsed.length > 0 ? Array.from(new Set(parsed)) : fallback;
}

function resolvePathDelimiter(platform: NodeJS.Platform): string {
return platform === "win32" ? ";" : ":";
}

function resolveCommandCandidates(
command: string,
platform: NodeJS.Platform,
windowsPathExtensions: ReadonlyArray<string>,
): ReadonlyArray<string> {
if (platform !== "win32") return [command];
const extension = extname(command);
const normalizedExtension = extension.toUpperCase();

if (extension.length > 0 && windowsPathExtensions.includes(normalizedExtension)) {
const commandWithoutExtension = command.slice(0, -extension.length);
return Array.from(
new Set([
command,
`${commandWithoutExtension}${normalizedExtension}`,
`${commandWithoutExtension}${normalizedExtension.toLowerCase()}`,
]),
);
}

const candidates: string[] = [];
for (const windowsPathExtension of windowsPathExtensions) {
candidates.push(`${command}${windowsPathExtension}`);
candidates.push(`${command}${windowsPathExtension.toLowerCase()}`);
}
return Array.from(new Set(candidates));
}

function isExecutableFile(
filePath: string,
platform: NodeJS.Platform,
windowsPathExtensions: ReadonlyArray<string>,
): boolean {
try {
const stat = statSync(filePath);
if (!stat.isFile()) return false;
if (platform === "win32") {
const extension = extname(filePath);
if (extension.length === 0) return false;
return windowsPathExtensions.includes(extension.toUpperCase());
}
accessSync(filePath, constants.X_OK);
return true;
} catch {
return false;
}
}

export function resolveCommandPath(
command: string,
options: CommandPathOptions = {},
): string | undefined {
const platform = options.platform ?? process.platform;
const env = options.env ?? process.env;
const windowsPathExtensions = platform === "win32" ? resolveWindowsPathExtensions(env) : [];
const commandCandidates = resolveCommandCandidates(command, platform, windowsPathExtensions);

if (command.includes("/") || command.includes("\\")) {
return commandCandidates.find((candidate) =>
isExecutableFile(candidate, platform, windowsPathExtensions),
);
}

const pathValue = resolvePathEnvironmentVariable(env);
if (pathValue.length === 0) return undefined;

const pathEntries = pathValue
.split(resolvePathDelimiter(platform))
.map((entry) => stripWrappingQuotes(entry.trim()))
.filter((entry) => entry.length > 0);

for (const pathEntry of pathEntries) {
for (const candidate of commandCandidates) {
const resolvedPath = join(pathEntry, candidate);
if (isExecutableFile(resolvedPath, platform, windowsPathExtensions)) {
return resolvedPath;
}
}
}

return undefined;
}

export function isCommandAvailable(command: string, options: CommandPathOptions = {}): boolean {
return resolveCommandPath(command, options) !== undefined;
}
Loading
Loading