@aku11i/phantom 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Shuhei Akutagawa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # 👻 Phantom
2
+
3
+ <div align="center">
4
+
5
+ **A powerful CLI tool for seamless parallel development with Git worktrees**
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@aku11i/phantom.svg)](https://www.npmjs.com/package/@aku11i/phantom)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![Node.js Version](https://img.shields.io/node/v/@aku11i/phantom.svg)](https://nodejs.org)
10
+
11
+ [Installation](#-installation) • [Quick Start](#-quick-start) • [Why Phantom?](#-why-phantom) • [Documentation](#-documentation)
12
+
13
+ </div>
14
+
15
+ ## ✨ Key Features
16
+
17
+ - 🚀 **Simplified Worktree Management** - Create and manage Git worktrees with intuitive commands
18
+ - 🔄 **Seamless Context Switching** - Jump between different features without stashing or committing
19
+ - 🤖 **AI-Friendly** - Perfect for running multiple AI coding agents in parallel
20
+ - 🎯 **Branch-Worktree Sync** - Automatically creates matching branches for each worktree
21
+ - 🐚 **Interactive Shell** - SSH-like experience for worktree navigation
22
+ - ⚡ **Zero Configuration** - Works out of the box with sensible defaults
23
+
24
+ ## 🤔 Why Phantom?
25
+
26
+ Modern development workflows often require working on multiple features simultaneously. Whether you're running AI coding agents in parallel, reviewing PRs while developing, or simply multitasking across features, managing multiple Git worktrees can be cumbersome.
27
+
28
+ **The Problem:**
29
+ - Git worktree commands are verbose and complex
30
+ - Managing branches and worktrees separately is error-prone
31
+ - Switching contexts requires multiple commands
32
+ - Running parallel AI agents on the same codebase is challenging
33
+
34
+ **The Phantom Solution:**
35
+ - One command to create both worktree and branch: `phantom garden create feature-x`
36
+ - Instant context switching: `phantom shell feature-x`
37
+ - Execute commands without changing directories: `phantom exec feature-x npm test`
38
+ - Perfect for "parallel vibe coding" with multiple AI agents
39
+
40
+ ## 🚀 Quick Start
41
+
42
+ ```bash
43
+ # Install Phantom
44
+ npm install -g @aku11i/phantom
45
+
46
+ # Create a new development space (garden)
47
+ phantom garden create feature-awesome
48
+
49
+ # Jump into the new space
50
+ phantom shell feature-awesome
51
+
52
+ # Or execute commands directly
53
+ phantom exec feature-awesome npm install
54
+ phantom exec feature-awesome npm test
55
+
56
+ # List all your gardens
57
+ phantom garden list
58
+
59
+ # Clean up when done
60
+ phantom garden delete feature-awesome
61
+ ```
62
+
63
+ ## 📦 Installation
64
+
65
+ ### Using npm (recommended)
66
+ ```bash
67
+ npm install -g @aku11i/phantom
68
+ ```
69
+
70
+ ### Using pnpm
71
+ ```bash
72
+ pnpm add -g @aku11i/phantom
73
+ ```
74
+
75
+ ### Using yarn
76
+ ```bash
77
+ yarn global add @aku11i/phantom
78
+ ```
79
+
80
+ ### Build from source
81
+ ```bash
82
+ git clone https://github.com/aku11i/phantom.git
83
+ cd phantom
84
+ pnpm install
85
+ npm link
86
+ ```
87
+
88
+ ## 📖 Documentation
89
+
90
+ ### Core Concepts
91
+
92
+ **Gardens** 🌳 - Git worktrees managed by Phantom. Each garden is an isolated workspace for a specific branch or feature.
93
+
94
+ **Phantoms** 👻 - Processes or agents that work within gardens.
95
+
96
+ ### Commands Overview
97
+
98
+ #### Gardens Management
99
+
100
+ ```bash
101
+ # Create a new garden with a matching branch
102
+ phantom garden create <name>
103
+
104
+ # List all gardens with their current status
105
+ phantom garden list
106
+
107
+ # Get the absolute path to a garden
108
+ phantom garden where <name>
109
+
110
+ # Delete a garden and its branch
111
+ phantom garden delete <name>
112
+ phantom garden delete <name> --force # Force delete with uncommitted changes
113
+ ```
114
+
115
+ #### Working with Gardens
116
+
117
+ ```bash
118
+ # Execute any command in a garden's context
119
+ phantom exec <garden> <command> [args...]
120
+
121
+ # Examples:
122
+ phantom exec feature-auth npm install
123
+ phantom exec feature-auth npm run test
124
+ phantom exec feature-auth git status
125
+
126
+ # Open an interactive shell session in a garden
127
+ phantom shell <garden>
128
+ ```
129
+
130
+ ### Environment Variables
131
+
132
+ When working within a Phantom context, these environment variables are available:
133
+
134
+ - `PHANTOM_GARDEN` - Name of the current garden
135
+ - `PHANTOM_GARDEN_PATH` - Absolute path to the garden directory
136
+
137
+ ## 🔄 Phantom vs Git Worktree
138
+
139
+ | Feature | Git Worktree | Phantom |
140
+ |---------|--------------|---------|
141
+ | Create worktree + branch | `git worktree add -b feature ../project-feature` | `phantom garden create feature` |
142
+ | List worktrees | `git worktree list` | `phantom garden list` |
143
+ | Navigate to worktree | `cd ../project-feature` | `phantom shell feature` |
144
+ | Run command in worktree | `cd ../project-feature && npm test` | `phantom exec feature npm test` |
145
+ | Remove worktree | `git worktree remove ../project-feature` | `phantom garden delete feature` |
146
+
147
+ ## 🛠️ Development
148
+
149
+ ```bash
150
+ # Clone and setup
151
+ git clone https://github.com/aku11i/phantom.git
152
+ cd phantom
153
+ pnpm install
154
+
155
+ # Run tests
156
+ pnpm test
157
+
158
+ # Type checking
159
+ pnpm type-check
160
+
161
+ # Linting
162
+ pnpm lint
163
+
164
+ # Run all checks
165
+ pnpm ready
166
+ ```
167
+
168
+ ## 🤝 Contributing
169
+
170
+ Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
171
+
172
+ Please make sure to:
173
+ - Update tests as appropriate
174
+ - Follow the existing code style
175
+ - Run `pnpm ready` before submitting
176
+
177
+ ## 📄 License
178
+
179
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
180
+
181
+ ## 🙏 Acknowledgments
182
+
183
+ - Inspired by the need for better parallel development workflows
184
+ - Built for the AI-assisted development era
185
+ - Special thanks to all contributors
186
+
187
+ ---
188
+
189
+ <div align="center">
190
+ Made with 👻 by <a href="https://github.com/aku11i">aku11i</a>
191
+ </div>
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@aku11i/phantom",
3
+ "version": "0.1.0",
4
+ "description": "A CLI tool for managing Git worktrees (gardens) with enhanced functionality",
5
+ "keywords": [
6
+ "git",
7
+ "worktree",
8
+ "cli",
9
+ "phantom",
10
+ "gardens",
11
+ "workspace",
12
+ "development"
13
+ ],
14
+ "homepage": "https://github.com/aku11i/phantom#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/aku11i/phantom/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/aku11i/phantom.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": "aku11i",
24
+ "type": "module",
25
+ "bin": {
26
+ "phantom": "./src/bin/phantom.ts",
27
+ "garden": "./src/bin/garden.ts"
28
+ },
29
+ "engines": {
30
+ "node": ">=20.0.0"
31
+ },
32
+ "files": [
33
+ "src/",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "devDependencies": {
38
+ "@biomejs/biome": "^1.9.4",
39
+ "@types/node": "^22.15.29",
40
+ "typescript": "^5.8.3"
41
+ },
42
+ "scripts": {
43
+ "start": "node ./src/bin/phantom.ts",
44
+ "phantom": "node ./src/bin/phantom.ts",
45
+ "garden": "node ./src/bin/garden.ts",
46
+ "type-check": "tsc --noEmit",
47
+ "test": "node --test --experimental-test-module-mocks src/**/*.test.ts",
48
+ "lint": "biome check .",
49
+ "fix": "biome check --write .",
50
+ "ready": "pnpm fix && pnpm type-check && pnpm test",
51
+ "ready:check": "pnpm lint && pnpm type-check && pnpm test"
52
+ }
53
+ }
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { argv, exit } from "node:process";
4
+ import { gardensCreateHandler } from "../gardens/commands/create.ts";
5
+ import { gardensDeleteHandler } from "../gardens/commands/delete.ts";
6
+ import { gardensListHandler } from "../gardens/commands/list.ts";
7
+ import { gardensWhereHandler } from "../gardens/commands/where.ts";
8
+
9
+ interface Command {
10
+ name: string;
11
+ description: string;
12
+ handler?: (args: string[]) => void | Promise<void>;
13
+ }
14
+
15
+ const commands: Command[] = [
16
+ {
17
+ name: "create",
18
+ description: "Create a new worktree (garden)",
19
+ handler: gardensCreateHandler,
20
+ },
21
+ {
22
+ name: "list",
23
+ description: "List all gardens",
24
+ handler: gardensListHandler,
25
+ },
26
+ {
27
+ name: "where",
28
+ description: "Output the path of a specific garden",
29
+ handler: gardensWhereHandler,
30
+ },
31
+ {
32
+ name: "delete",
33
+ description: "Delete a garden (use --force for dirty gardens)",
34
+ handler: gardensDeleteHandler,
35
+ },
36
+ ];
37
+
38
+ function printHelp() {
39
+ console.log("Usage: garden <command> [options]\n");
40
+ console.log("Commands:");
41
+ for (const cmd of commands) {
42
+ console.log(` ${cmd.name.padEnd(20)} ${cmd.description}`);
43
+ }
44
+ console.log("\nThis is an alias for 'phantom garden' commands.");
45
+ }
46
+
47
+ function findCommand(cmdName: string, commands: Command[]): Command | null {
48
+ return commands.find((cmd) => cmd.name === cmdName) || null;
49
+ }
50
+
51
+ const args = argv.slice(2);
52
+
53
+ if (args.length === 0 || args[0] === "-h" || args[0] === "--help") {
54
+ printHelp();
55
+ exit(0);
56
+ }
57
+
58
+ const command = findCommand(args[0], commands);
59
+
60
+ if (!command || !command.handler) {
61
+ console.error(`Error: Unknown command '${args[0]}'\n`);
62
+ printHelp();
63
+ exit(1);
64
+ }
65
+
66
+ try {
67
+ await command.handler(args.slice(1));
68
+ } catch (error) {
69
+ console.error(
70
+ "Error:",
71
+ error instanceof Error ? error.message : String(error),
72
+ );
73
+ exit(1);
74
+ }
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { argv, exit } from "node:process";
4
+ import { execHandler } from "../commands/exec.ts";
5
+ import { shellHandler } from "../commands/shell.ts";
6
+ import { gardensCreateHandler } from "../gardens/commands/create.ts";
7
+ import { gardensDeleteHandler } from "../gardens/commands/delete.ts";
8
+ import { gardensListHandler } from "../gardens/commands/list.ts";
9
+ import { gardensWhereHandler } from "../gardens/commands/where.ts";
10
+
11
+ interface Command {
12
+ name: string;
13
+ description: string;
14
+ subcommands?: Command[];
15
+ handler?: (args: string[]) => void | Promise<void>;
16
+ }
17
+
18
+ const commands: Command[] = [
19
+ {
20
+ name: "garden",
21
+ description: "Manage git worktrees (gardens)",
22
+ subcommands: [
23
+ {
24
+ name: "create",
25
+ description: "Create a new worktree (garden)",
26
+ handler: gardensCreateHandler,
27
+ },
28
+ {
29
+ name: "list",
30
+ description: "List all gardens",
31
+ handler: gardensListHandler,
32
+ },
33
+ {
34
+ name: "where",
35
+ description: "Output the path of a specific garden",
36
+ handler: gardensWhereHandler,
37
+ },
38
+ {
39
+ name: "delete",
40
+ description: "Delete a garden (use --force for dirty gardens)",
41
+ handler: gardensDeleteHandler,
42
+ },
43
+ ],
44
+ },
45
+ {
46
+ name: "exec",
47
+ description: "Execute a command in a garden directory",
48
+ handler: execHandler,
49
+ },
50
+ {
51
+ name: "shell",
52
+ description: "Open interactive shell in a garden directory",
53
+ handler: shellHandler,
54
+ },
55
+ ];
56
+
57
+ function printHelp(commands: Command[], prefix = "") {
58
+ console.log("Usage: phantom <command> [options]\n");
59
+ console.log("Commands:");
60
+ for (const cmd of commands) {
61
+ console.log(` ${prefix}${cmd.name.padEnd(20)} ${cmd.description}`);
62
+ if (cmd.subcommands) {
63
+ for (const subcmd of cmd.subcommands) {
64
+ console.log(` ${subcmd.name.padEnd(18)} ${subcmd.description}`);
65
+ }
66
+ }
67
+ }
68
+ }
69
+
70
+ function findCommand(
71
+ args: string[],
72
+ commands: Command[],
73
+ ): { command: Command | null; remainingArgs: string[] } {
74
+ if (args.length === 0) {
75
+ return { command: null, remainingArgs: [] };
76
+ }
77
+
78
+ const [cmdName, ...rest] = args;
79
+ const command = commands.find((cmd) => cmd.name === cmdName);
80
+
81
+ if (!command) {
82
+ return { command: null, remainingArgs: args };
83
+ }
84
+
85
+ if (command.subcommands && rest.length > 0) {
86
+ const { command: subcommand, remainingArgs } = findCommand(
87
+ rest,
88
+ command.subcommands,
89
+ );
90
+ if (subcommand) {
91
+ return { command: subcommand, remainingArgs };
92
+ }
93
+ }
94
+
95
+ return { command, remainingArgs: rest };
96
+ }
97
+
98
+ const args = argv.slice(2);
99
+
100
+ if (args.length === 0 || args[0] === "-h" || args[0] === "--help") {
101
+ printHelp(commands);
102
+ exit(0);
103
+ }
104
+
105
+ const { command, remainingArgs } = findCommand(args, commands);
106
+
107
+ if (!command || !command.handler) {
108
+ console.error(`Error: Unknown command '${args.join(" ")}'\n`);
109
+ printHelp(commands);
110
+ exit(1);
111
+ }
112
+
113
+ try {
114
+ await command.handler(remainingArgs);
115
+ } catch (error) {
116
+ console.error(
117
+ "Error:",
118
+ error instanceof Error ? error.message : String(error),
119
+ );
120
+ exit(1);
121
+ }