Compare commits

...
9 Commits
Author SHA1 Message Date
bendtherules 78784fabb6 chore(release): 0.1.3 2026-03-23 13:12:12 +05:30
bendtherules c6698e7c22 fix: load CLI version from package.json 2026-03-23 13:11:25 +05:30
bendtherules cb9c973868 chore(release): 0.1.2 2026-03-23 12:56:10 +05:30
bendtherules f58cd1e897 fix: remote argument schema
Removed unnecessary union wrapper for the `remote` argument, now using a direct string schema with optional flag and updated description.
2026-03-23 12:55:50 +05:30
bendtherules 4e20a03359 docs(AGENTS): rename JSDoc Comments heading to Documentation and add note to update README when implementation changes 2026-03-23 12:31:52 +05:30
bendtherules 114837cfae chore(release): 0.1.1 2026-03-23 12:26:17 +05:30
bendtherules 426def40f1 docs: enhance AGENTS.md 2026-03-23 12:17:29 +05:30
bendtherules 3ac3153aa9 docs: add AGENTS.md with project overview, build commands, and coding guidelines 2026-03-23 12:01:37 +05:30
bendtherules 233e211eb8 feat: add cleanup command to readme
Introduce a new `cleanup` command that discovers and deletes archive tags, with options for force, remote handling, and local-only operations. Updated README with usage examples, added help entry, and expanded documentation to describe the command and its steps. Added corresponding implementation files (`cleanup.ts`) in both core and tools directories.
2026-03-23 11:32:42 +05:30
6 changed files with 212 additions and 12 deletions
+151
View File
@@ -0,0 +1,151 @@
# AGENTS.md
## Project Overview
opencode-multi-model is a Bun/TypeScript project that provides multi-model tmux session management for AI coding assistants. It allows running multiple AI models simultaneously in isolated tmux windows with separate git worktrees.
Available as three integration modes:
- **OpenCode Plugin**: Loaded via `opencode.json` plugin array
- **Standalone CLI**: Install globally and run `opencode-multi-model open <session> -m <models...>`
- **Project Tool**: Import core functions from `"opencode-multi-model/core"` for project-level commands (used during development).
Requires: tmux, git, opencode or kilo binary, Bun runtime.
## Build, Lint, and Test Commands
```bash
# Install dependencies
bun install
# Build for production
bun run build
# Run tests
bun test
# Run tests with coverage (HTML report in ./coverage)
bun run coverage
# Run a specific test by name
bun test --test-name-pattern "fails if session name is empty"
# TypeScript type checking
bun run typecheck
# Lint with Biome
bun run lint
# Auto-fix lint issues
bun run lint:fix
# Format code with Biome
bun run format
# Full quality check
bun run typecheck && bun run lint
```
## Code Style Guidelines
### General
- Use **TypeScript** with strict mode
- Use **Bun** as the runtime and package manager
- Use ESM modules (`"type": "module"` in package.json)
- Use `node:` prefix for Node.js built-ins
### Formatting (Biome)
- **Indent**: 2 spaces (not tabs)
- **Quote style**: Double quotes for JS strings
- **Semicolons**: Required
- Run `bun run format` to format files
### TypeScript Conventions
- Use `interface` for object shapes; avoid `type` aliases unless needed
- Use explicit return types on exported functions
- Avoid `any` when possible
- Use `noUncheckedIndexedAccess: true` and `noImplicitOverride: true` in tsconfig
- All types/interfaces go in `src/types.ts`
### Naming Conventions
- **Files**: kebab-case (`multi-model-open.ts`)
- **Functions**: camelCase (`openMultiModel`)
- **Types/Interfaces**: PascalCase (`MultiModelOptions`)
- **Constants**: UPPER_SNAKE_CASE (e.g., `MAX_SUGGESTIONS`)
### Imports
- Use `import type` for type-only imports
- Group imports: external packages, internal modules, types
- Biome automatically organizes imports
### Documentation
- Use JSDoc for all exported functions
- Include `@example`, `@param`, and `@returns` descriptions
- Add inline comments for hard-to-understand code
- Modify Readme.md, if implementation details have changed.
### Error Handling
- Return result objects (e.g., `MultiModelResult`, `CloseSessionResult`, `CleanupResult`) with `success: boolean`
- Include descriptive error messages prefixed with `Error:`
- Provide actionable error messages (e.g., suggest valid models on invalid model name)
- Use `chalk` for colored terminal output (red for errors, dim for secondary info, bold for primary info)
### Shell Commands
- Use `Bun.$` template tag for shell commands
- Always use `quiet().nothrow()` to capture output without throwing
- Wrap command execution in `runCommand()` utility from `core/utils.ts`
- Use `shellQuote()` for user-provided values in shell commands
### Testing
- Use `bun:test` framework
- Mock `Bun.$` using the `mock()` function from `bun:test`
- Use `spyOn()` for fs/os module mocking
- Group tests with `describe()` blocks
- Test file naming: `*.test.ts` in `tests/` directory
## Project Structure
```
src/
├── index.ts # Plugin entry point (default export)
├── cli.ts # CLI entry point (commander setup)
├── types.ts # All TypeScript interfaces/types
├── core/
│ ├── index.ts # Core exports
│ ├── open.ts # Session launch logic
│ ├── close.ts # Session close logic
│ ├── cleanup.ts # Archive tag cleanup logic
│ └── utils.ts # Shared utilities (runCommand, shellQuote, etc.)
└── tools/
├── open.ts # OpenCode tool definition
├── close.ts # CloseCode tool definition
└── cleanup.ts # Cleanup tool definition
tests/
├── open.test.ts # Open command tests
├── close.test.ts # Close command tests
├── cleanup.test.ts # Cleanup command tests
├── open_error.test.ts # Open error handling tests
├── close_error.test.ts # Close error handling tests
└── utils.test.ts # Utility function tests
```
### Git Branch and Tag Naming
- Session branches: `opencode/<session-name>/<window-name>`
- Archive tags: `archive/<branch-name>-<timestamp>` (created by close command)
- Worktrees: `~/.local/share/opencode/multi-model/<session>/<window>/`
### Common Pitfalls
- Tmux session names use raw input; git branches/paths use sanitized names
- Window names must be unique; collisions get numeric suffixes (e.g., `gpt-5-2`, `gpt-5-2-2`)
- Window names are truncated to 24 characters to fit tmux limits
- Worktree paths are checked for existence before creation to avoid conflicts
- Archive tags preserve branch state before deletion for recovery
+54 -2
View File
@@ -62,6 +62,11 @@ sessionName: my-session, models: ["openai/gpt-5.2", "anthropic/claude-3-5-sonnet
call tool multi-model-close with sessionName: my-session call tool multi-model-close with sessionName: my-session
``` ```
**Cleanup archive tags (using tool):**
```
call tool multi-model-cleanup
```
**Close a session (using manual command):** **Close a session (using manual command):**
```bash ```bash
tmux kill-session -t my-session && rm -rf ~/.local/share/opencode/multi-model/my-session && git worktree prune && git branch -D $(git branch --format='%(refname:short)' --list 'opencode/my-session/*') tmux kill-session -t my-session && rm -rf ~/.local/share/opencode/multi-model/my-session && git worktree prune && git branch -D $(git branch --format='%(refname:short)' --list 'opencode/my-session/*')
@@ -88,6 +93,21 @@ opencode-multi-model open my-session -m openai/gpt-5.2
opencode-multi-model close my-session opencode-multi-model close my-session
``` ```
**Cleanup archive tags (CLI command):**
```bash
# Delete all archive tags from local and remote repositories
opencode-multi-model cleanup
# Delete all archive tags without confirmation prompts
opencode-multi-model cleanup --force
# Delete only local archive tags, skip remote deletion
opencode-multi-model cleanup --no-remote
# Delete archive tags and push deletions to a specific remote
opencode-multi-model cleanup --remote origin
```
**Close a session (manual command):** **Close a session (manual command):**
```bash ```bash
tmux kill-session -t my-session && rm -rf ~/.local/share/opencode/multi-model/my-session && git worktree prune && git branch -D $(git branch --format='%(refname:short)' --list 'opencode/my-session/*') tmux kill-session -t my-session && rm -rf ~/.local/share/opencode/multi-model/my-session && git worktree prune && git branch -D $(git branch --format='%(refname:short)' --list 'opencode/my-session/*')
@@ -98,6 +118,7 @@ tmux kill-session -t my-session && rm -rf ~/.local/share/opencode/multi-model/my
opencode-multi-model --help opencode-multi-model --help
opencode-multi-model open --help opencode-multi-model open --help
opencode-multi-model close --help opencode-multi-model close --help
opencode-multi-model cleanup --help
``` ```
## Configuration ## Configuration
@@ -151,6 +172,21 @@ opencode-multi-model close <session-name> [options]
| `--no-tags` | Do not create archive tags before deleting branches | | `--no-tags` | Do not create archive tags before deleting branches |
| `-f, --force` | Skip confirmation prompts | | `-f, --force` | Skip confirmation prompts |
### `cleanup` - Delete all archive tags from local and remote repositories
Archive tags have the format: `archive/<branch-name>-<timestamp>`. These are created by the close command to preserve branch state before deletion.
```bash
opencode-multi-model cleanup [options]
```
**Options:**
| Option | Description |
|--------|-------------|
| `-f, --force` | Skip confirmation prompts |
| `-r, --remote <remote>` | Remote name to push deletions to (default: first remote from git remote -v) |
| `--no-remote` | Only delete local tags, skip remote |
### Global Options ### Global Options
| Option | Description | | Option | Description |
@@ -191,6 +227,20 @@ The `close` command performs the following steps:
4. **Directory cleanup**: Removes the base session directory 4. **Directory cleanup**: Removes the base session directory
### `cleanup` command
The `cleanup` command performs the following steps:
1. **Archive tag discovery**: Finds all git tags matching the pattern `archive/*`
2. **Local tag deletion**: Deletes all archive tags from the local repository
3. **Remote tag deletion** (unless `--no-remote`):
- Determines which remote to use (user-specified, first available, or skip)
- Pushes tag deletions to the remote repository
4. **Summary**: Reports the number of tags deleted locally and remotely
## Requirements ## Requirements
- tmux - tmux
@@ -230,11 +280,13 @@ bun dist/cli.js close test-session
│ ├── core/ │ ├── core/
│ │ ├── index.ts # Core exports │ │ ├── index.ts # Core exports
│ │ ├── open.ts # Launch session logic │ │ ├── open.ts # Launch session logic
│ │ ├── close.ts # Close session logic │ │ ├── close.ts # Close session logic
│ │ ├── cleanup.ts # Cleanup archive tags logic
│ │ └── utils.ts # Helper functions │ │ └── utils.ts # Helper functions
│ └── tools/ │ └── tools/
│ ├── open.ts # Open tool definition │ ├── open.ts # Open tool definition
── close.ts # Close tool definition ── close.ts # Close tool definition
│ └── cleanup.ts # Cleanup tool definition
├── tests/ ├── tests/
│ └── multi-model.test.ts # Tests │ └── multi-model.test.ts # Tests
├── package.json ├── package.json
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "opencode-multi-model", "name": "opencode-multi-model",
"version": "0.1.0", "version": "0.1.3",
"description": "Launch multiple AI models in tmux sessions - OpenCode Plugin, CLI, and Project Tool", "description": "Launch multiple AI models in tmux sessions - OpenCode Plugin, CLI, and Project Tool",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
+2 -1
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bun #!/usr/bin/env bun
import { Command } from "commander"; import { Command } from "commander";
import pkg from "../package.json";
import { cleanupMultiModel } from "./core/cleanup"; import { cleanupMultiModel } from "./core/cleanup";
import { closeMultiModel } from "./core/close"; import { closeMultiModel } from "./core/close";
import { openMultiModel } from "./core/open"; import { openMultiModel } from "./core/open";
@@ -10,7 +11,7 @@ const program = new Command();
program program
.name("opencode-multi-model") .name("opencode-multi-model")
.description("Launch multiple OpenCode models in tmux sessions") .description("Launch multiple OpenCode models in tmux sessions")
.version("1.0.0"); .version(pkg.version);
program program
.command("open") .command("open")
+1 -7
View File
@@ -15,13 +15,7 @@ Arguments:
- \`noRemote\` (boolean, optional): When true, only delete local tags and do not push deletions to any remote.`, - \`noRemote\` (boolean, optional): When true, only delete local tags and do not push deletions to any remote.`,
args: { args: {
remote: tool.schema remote: tool.schema
.union([ .string()
tool.schema
.string()
.describe(
"Remote name to push deletions to. Error if doesn't exist.",
)
])
.optional() .optional()
.describe( .describe(
"Remote handling: string=specific remote, undefined=first available remote", "Remote handling: string=specific remote, undefined=first available remote",
+3 -1
View File
@@ -12,7 +12,9 @@
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true, "noUncheckedIndexedAccess": true,
"noImplicitOverride": true, "noImplicitOverride": true,
"types": ["bun-types"] "types": ["bun-types"],
"resolveJsonModule": true,
"esModuleInterop": true
}, },
"include": ["src/**/*", "tests/**/*"], "include": ["src/**/*", "tests/**/*"],
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]