@tokenbooks/wt 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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +395 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +71 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/doctor.d.ts +7 -0
  7. package/dist/commands/doctor.js +153 -0
  8. package/dist/commands/doctor.js.map +1 -0
  9. package/dist/commands/list.d.ts +6 -0
  10. package/dist/commands/list.js +15 -0
  11. package/dist/commands/list.js.map +1 -0
  12. package/dist/commands/new.d.ts +8 -0
  13. package/dist/commands/new.js +160 -0
  14. package/dist/commands/new.js.map +1 -0
  15. package/dist/commands/remove.d.ts +7 -0
  16. package/dist/commands/remove.js +133 -0
  17. package/dist/commands/remove.js.map +1 -0
  18. package/dist/commands/setup.d.ts +10 -0
  19. package/dist/commands/setup.js +157 -0
  20. package/dist/commands/setup.js.map +1 -0
  21. package/dist/core/database.d.ts +11 -0
  22. package/dist/core/database.js +69 -0
  23. package/dist/core/database.js.map +1 -0
  24. package/dist/core/env-patcher.d.ts +11 -0
  25. package/dist/core/env-patcher.js +133 -0
  26. package/dist/core/env-patcher.js.map +1 -0
  27. package/dist/core/git.d.ts +19 -0
  28. package/dist/core/git.js +102 -0
  29. package/dist/core/git.js.map +1 -0
  30. package/dist/core/registry.d.ts +20 -0
  31. package/dist/core/registry.js +103 -0
  32. package/dist/core/registry.js.map +1 -0
  33. package/dist/core/slot-allocator.d.ts +17 -0
  34. package/dist/core/slot-allocator.js +37 -0
  35. package/dist/core/slot-allocator.js.map +1 -0
  36. package/dist/output.d.ts +11 -0
  37. package/dist/output.js +91 -0
  38. package/dist/output.js.map +1 -0
  39. package/dist/schemas/config.schema.d.ts +57 -0
  40. package/dist/schemas/config.schema.js +33 -0
  41. package/dist/schemas/config.schema.js.map +1 -0
  42. package/dist/schemas/registry.schema.d.ts +22 -0
  43. package/dist/schemas/registry.schema.js +19 -0
  44. package/dist/schemas/registry.schema.js.map +1 -0
  45. package/dist/types.d.ts +26 -0
  46. package/dist/types.js +3 -0
  47. package/dist/types.js.map +1 -0
  48. package/package.json +44 -0
  49. package/skills/wt/SKILL.md +211 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.schema.js","sourceRoot":"","sources":["../../src/schemas/config.schema.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAExB,MAAM,WAAW,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAU,CAAC;AAElE,6CAA6C;AAChC,QAAA,WAAW,GAAG,OAAC,CAAC,MAAM,CAAC;IAClC,GAAG,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACtB,IAAI,EAAE,OAAC,CAAC,IAAI,CAAC,WAAW,CAAC;IACzB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEH,+CAA+C;AAClC,QAAA,aAAa,GAAG,OAAC,CAAC,MAAM,CAAC;IACpC,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC;AAEH,+CAA+C;AAClC,QAAA,aAAa,GAAG,OAAC,CAAC,MAAM,CAAC;IACpC,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,OAAO,EAAE,OAAC,CAAC,KAAK,CAAC,mBAAW,CAAC;CAC9B,CAAC,CAAC;AAEH,gCAAgC;AACnB,QAAA,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IACnC,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACnC,gBAAgB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;IACzD,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;IACpD,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACrD,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,qBAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,qBAAa,CAAC;IAChC,SAAS,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IAC1C,WAAW,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CACvC,CAAC,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ /** Schema for a single worktree allocation */
3
+ export declare const allocationSchema: z.ZodObject<{
4
+ worktreePath: z.ZodString;
5
+ branchName: z.ZodString;
6
+ dbName: z.ZodString;
7
+ redisDb: z.ZodNumber;
8
+ ports: z.ZodRecord<z.ZodString, z.ZodNumber>;
9
+ createdAt: z.ZodString;
10
+ }, z.core.$strip>;
11
+ /** Schema for .worktree-registry.json */
12
+ export declare const registrySchema: z.ZodObject<{
13
+ version: z.ZodLiteral<1>;
14
+ allocations: z.ZodRecord<z.ZodString, z.ZodObject<{
15
+ worktreePath: z.ZodString;
16
+ branchName: z.ZodString;
17
+ dbName: z.ZodString;
18
+ redisDb: z.ZodNumber;
19
+ ports: z.ZodRecord<z.ZodString, z.ZodNumber>;
20
+ createdAt: z.ZodString;
21
+ }, z.core.$strip>>;
22
+ }, z.core.$strip>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registrySchema = exports.allocationSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ /** Schema for a single worktree allocation */
6
+ exports.allocationSchema = zod_1.z.object({
7
+ worktreePath: zod_1.z.string().min(1),
8
+ branchName: zod_1.z.string().min(1),
9
+ dbName: zod_1.z.string().min(1),
10
+ redisDb: zod_1.z.number().int().min(1),
11
+ ports: zod_1.z.record(zod_1.z.string(), zod_1.z.number().int().positive()),
12
+ createdAt: zod_1.z.string().datetime(),
13
+ });
14
+ /** Schema for .worktree-registry.json */
15
+ exports.registrySchema = zod_1.z.object({
16
+ version: zod_1.z.literal(1),
17
+ allocations: zod_1.z.record(zod_1.z.string(), exports.allocationSchema),
18
+ });
19
+ //# sourceMappingURL=registry.schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.schema.js","sourceRoot":"","sources":["../../src/schemas/registry.schema.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AAExB,8CAA8C;AACjC,QAAA,gBAAgB,GAAG,OAAC,CAAC,MAAM,CAAC;IACvC,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACzB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IACxD,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC,CAAC;AAEH,yCAAyC;AAC5B,QAAA,cAAc,GAAG,OAAC,CAAC,MAAM,CAAC;IACrC,OAAO,EAAE,OAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,WAAW,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,wBAAgB,CAAC;CACpD,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { z } from 'zod';
2
+ import type { configSchema, serviceSchema, envFileSchema, patchSchema } from './schemas/config.schema';
3
+ import type { registrySchema, allocationSchema } from './schemas/registry.schema';
4
+ /** Declarative config loaded from wt.config.json */
5
+ export type WtConfig = z.infer<typeof configSchema>;
6
+ export type ServiceConfig = z.infer<typeof serviceSchema>;
7
+ export type EnvFileConfig = z.infer<typeof envFileSchema>;
8
+ export type PatchConfig = z.infer<typeof patchSchema>;
9
+ /** Registry persisted at .worktree-registry.json */
10
+ export type Registry = z.infer<typeof registrySchema>;
11
+ export type Allocation = z.infer<typeof allocationSchema>;
12
+ /** Context passed to env patcher with computed values for a slot */
13
+ export interface PatchContext {
14
+ readonly dbName: string;
15
+ readonly redisDb: number;
16
+ readonly ports: Record<string, number>;
17
+ }
18
+ /** Result of CLI operations for --json output */
19
+ export interface CliResult<T = unknown> {
20
+ readonly success: boolean;
21
+ readonly data?: T;
22
+ readonly error?: {
23
+ code: string;
24
+ message: string;
25
+ };
26
+ }
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@tokenbooks/wt",
3
+ "version": "0.1.0",
4
+ "description": "Git worktree environment isolation CLI",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/tokenbooks/wt.git"
9
+ },
10
+ "main": "dist/cli.js",
11
+ "bin": {
12
+ "wt": "./dist/cli.js"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "skills",
17
+ "README.md"
18
+ ],
19
+ "dependencies": {
20
+ "commander": "^14.0.0",
21
+ "pg": "^8.16.0",
22
+ "zod": "^4.2.1"
23
+ },
24
+ "devDependencies": {
25
+ "@jest/globals": "^30.2.0",
26
+ "@types/node": "^24.10.0",
27
+ "@types/pg": "^8.11.0",
28
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
29
+ "@typescript-eslint/parser": "^8.53.0",
30
+ "eslint": "^9.39.0",
31
+ "jest": "^30.2.0",
32
+ "ts-jest": "^29.4.1",
33
+ "typescript": "^5.9.2"
34
+ },
35
+ "engines": {
36
+ "node": ">=20.19.0 || >=22.3.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc && node scripts/link-cli.js",
40
+ "dev": "tsc --watch",
41
+ "test": "jest",
42
+ "lint": "eslint 'src/**/*.ts' '__tests__/**/*.ts'"
43
+ }
44
+ }
@@ -0,0 +1,211 @@
1
+ ---
2
+ name: wt
3
+ description: Manage git worktree isolation — create, list, remove worktrees with isolated databases, Redis, and ports
4
+ argument-hint: "[new|list|remove|doctor|setup|init] [args...]"
5
+ allowed-tools: Bash, Read, Write, Edit, Grep, Glob
6
+ ---
7
+
8
+ # wt — Git Worktree Isolation
9
+
10
+ You are managing git worktrees with isolated Postgres databases, Redis DB indexes, and ports using the `wt` CLI.
11
+
12
+ ## Routing
13
+
14
+ Based on the first argument, perform one of the following:
15
+
16
+ ### `init` — First-time setup for a repository
17
+
18
+ The user wants to set up `wt` in their project for the first time. Follow these steps exactly:
19
+
20
+ **Step 1: Discover the project**
21
+
22
+ Search the repository to find:
23
+ - All `.env` files (not `.env.example`): `find . -name '.env' -not -path '*/node_modules/*' -not -path '*/.git/*'`
24
+ - The `DATABASE_URL` value to extract the base database name (path segment after the port, before `?`)
25
+ - Any `REDIS_URL` values and their format (with or without trailing `/0`)
26
+ - All services and their default ports — check `package.json` scripts, `docker-compose.yml`, framework config files
27
+ - The package manager in use (`pnpm`, `npm`, `yarn`) — check for lockfiles
28
+
29
+ **Step 2: Map env vars to patch types**
30
+
31
+ For each `.env` file, examine every variable and classify:
32
+
33
+ | Variable contains | Patch type | Needs `service`? |
34
+ |---|---|---|
35
+ | Postgres connection URL (`postgresql://...`) | `database` | No |
36
+ | Redis connection URL (`redis://...`) | `redis` | No |
37
+ | Just a port number | `port` | Yes |
38
+ | A URL containing a service port (`http://localhost:3000/...`) | `url` | Yes |
39
+ | Anything else (API keys, secrets, flags) | Skip — do not patch | — |
40
+
41
+ **Step 3: Generate `wt.config.json`**
42
+
43
+ Build the config file at the repository root:
44
+
45
+ ```json
46
+ {
47
+ "baseDatabaseName": "<extracted from DATABASE_URL>",
48
+ "baseWorktreePath": ".worktrees",
49
+ "portStride": 100,
50
+ "maxSlots": 15,
51
+ "services": [
52
+ { "name": "<service>", "defaultPort": <port> }
53
+ ],
54
+ "envFiles": [
55
+ {
56
+ "source": "<relative path to .env file>",
57
+ "patches": [
58
+ { "var": "<VAR_NAME>", "type": "<database|redis|port|url>", "service": "<name>" }
59
+ ]
60
+ }
61
+ ],
62
+ "postSetup": ["<install command>"],
63
+ "autoInstall": true
64
+ }
65
+ ```
66
+
67
+ Validation rules:
68
+ - Every `port` and `url` patch must have a `service` that exists in `services`
69
+ - `portStride` * `maxSlots` + max default port must be < 65535
70
+ - `baseDatabaseName` must match the actual DB name in `DATABASE_URL`
71
+
72
+ **Step 4: Install wt**
73
+
74
+ ```bash
75
+ # Detect package manager and install
76
+ pnpm add -D @tokenbooks/wt
77
+ ```
78
+
79
+ **Step 5: Update .gitignore**
80
+
81
+ Add `.worktree-registry.json` if not already present.
82
+
83
+ **Step 6: Add convenience scripts to root package.json**
84
+
85
+ ```json
86
+ {
87
+ "scripts": {
88
+ "wt": "wt",
89
+ "wt:new": "wt new",
90
+ "wt:list": "wt list",
91
+ "wt:doctor": "wt doctor"
92
+ }
93
+ }
94
+ ```
95
+
96
+ **Step 7: Create `.husky/post-checkout` hook** (if husky is installed)
97
+
98
+ ```bash
99
+ #!/bin/bash
100
+ prev_head="$1"
101
+ new_head="$2"
102
+ is_branch="$3"
103
+ [ "$is_branch" = "0" ] && exit 0
104
+ git_common=$(git rev-parse --git-common-dir 2>/dev/null)
105
+ git_dir=$(git rev-parse --git-dir 2>/dev/null)
106
+ [ "$git_common" = "$git_dir" ] && exit 0
107
+ main_worktree=$(cd "$git_common/.." && pwd -P)
108
+ wt_bin="$main_worktree/node_modules/.bin/wt"
109
+ if [ ! -f "$wt_bin" ]; then
110
+ wt_bin=$(command -v wt 2>/dev/null || true)
111
+ fi
112
+ if [ -z "$wt_bin" ]; then
113
+ echo "Warning: wt CLI not found."
114
+ exit 0
115
+ fi
116
+ echo "Setting up worktree environment..."
117
+ "$wt_bin" setup "$(pwd -P)" --json 2>/dev/null && echo "Worktree ready!" || {
118
+ echo "Warning: Auto-setup failed. Run 'wt setup' manually."
119
+ exit 0
120
+ }
121
+ ```
122
+
123
+ Make it executable: `chmod +x .husky/post-checkout`
124
+
125
+ **Step 8: Smoke test**
126
+
127
+ ```bash
128
+ wt list # Should show "No worktree allocations found."
129
+ wt doctor # Should show "All checks passed."
130
+ wt new test/wt-smoke --no-install # Create a test worktree
131
+ wt list # Should show the allocation
132
+ wt remove test-wt-smoke # Clean up
133
+ ```
134
+
135
+ Present the results to the user.
136
+
137
+ ---
138
+
139
+ ### `new $1` — Create a new worktree
140
+
141
+ Run:
142
+ ```bash
143
+ wt new $1
144
+ ```
145
+
146
+ If it fails, check `wt doctor` for diagnostics. Common issues:
147
+ - All slots occupied → suggest `wt list` to find stale ones, then `wt remove`
148
+ - Database connection failed → check that Postgres is running and `DATABASE_URL` in root `.env` is correct
149
+
150
+ ---
151
+
152
+ ### `list` — List all worktree allocations
153
+
154
+ Run:
155
+ ```bash
156
+ wt list
157
+ ```
158
+
159
+ ---
160
+
161
+ ### `remove $1` — Remove a worktree
162
+
163
+ Run:
164
+ ```bash
165
+ wt remove $1
166
+ ```
167
+
168
+ Accepts either a path (`.worktrees/feat-auth`) or a slot number (`3`).
169
+
170
+ If the user wants to keep the database, use `--keep-db`.
171
+
172
+ ---
173
+
174
+ ### `doctor` — Diagnose issues
175
+
176
+ Run:
177
+ ```bash
178
+ wt doctor
179
+ ```
180
+
181
+ If issues are found, ask the user if they want to auto-fix:
182
+ ```bash
183
+ wt doctor --fix
184
+ ```
185
+
186
+ ---
187
+
188
+ ### `setup` — Set up an existing worktree
189
+
190
+ Run:
191
+ ```bash
192
+ wt setup $1
193
+ ```
194
+
195
+ Use this when a worktree was created with plain `git worktree add` instead of `wt new`.
196
+
197
+ ---
198
+
199
+ ### No arguments or unrecognized command
200
+
201
+ Show a brief help:
202
+
203
+ ```
204
+ Available commands:
205
+ /wt init — Set up wt in a new repository (discovers env files, generates config)
206
+ /wt new <branch> — Create a worktree with isolated DB, Redis, and ports
207
+ /wt list — List all worktree allocations
208
+ /wt remove <target> — Remove a worktree and clean up resources
209
+ /wt doctor — Diagnose and fix environment issues
210
+ /wt setup [path] — Set up an existing worktree
211
+ ```