pi-var 1.0.1
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/.github/FUNDING.yml +4 -0
- package/.prettierrc +7 -0
- package/README.md +240 -0
- package/commitlint.config.cjs +3 -0
- package/extensions/pi-var/__tests__/integration/smoke.test.ts +308 -0
- package/extensions/pi-var/__tests__/unit/command.test.ts +322 -0
- package/extensions/pi-var/__tests__/unit/cow-detection.test.ts +340 -0
- package/extensions/pi-var/__tests__/unit/cow-git-merge.test.ts +404 -0
- package/extensions/pi-var/__tests__/unit/edr.test.ts +119 -0
- package/extensions/pi-var/__tests__/unit/environment.test.ts +369 -0
- package/extensions/pi-var/__tests__/unit/file-redirect.test.ts +295 -0
- package/extensions/pi-var/__tests__/unit/store.test.ts +489 -0
- package/extensions/pi-var/__tests__/unit/tools.test.ts +133 -0
- package/extensions/pi-var/__tests__/unit/variations.test.ts +352 -0
- package/extensions/pi-var/index.ts +111 -0
- package/extensions/pi-var/src/config.ts +30 -0
- package/extensions/pi-var/src/cow/clone.ts +46 -0
- package/extensions/pi-var/src/cow/detection.ts +215 -0
- package/extensions/pi-var/src/cow/index.ts +6 -0
- package/extensions/pi-var/src/edr/constants.ts +199 -0
- package/extensions/pi-var/src/edr/crowdstrike.ts +62 -0
- package/extensions/pi-var/src/edr/detection.ts +97 -0
- package/extensions/pi-var/src/edr/index.ts +6 -0
- package/extensions/pi-var/src/environment/copy-env.ts +79 -0
- package/extensions/pi-var/src/environment/detect.ts +91 -0
- package/extensions/pi-var/src/environment/index.ts +54 -0
- package/extensions/pi-var/src/environment/marker.ts +42 -0
- package/extensions/pi-var/src/environment/symlink.ts +82 -0
- package/extensions/pi-var/src/gitignore/manager.ts +77 -0
- package/extensions/pi-var/src/state/index.ts +7 -0
- package/extensions/pi-var/src/state/keys.ts +14 -0
- package/extensions/pi-var/src/state/store.ts +110 -0
- package/extensions/pi-var/src/state/types.ts +70 -0
- package/extensions/pi-var/src/tools/command/clean.ts +87 -0
- package/extensions/pi-var/src/tools/command/index.ts +95 -0
- package/extensions/pi-var/src/tools/command/status.ts +58 -0
- package/extensions/pi-var/src/tools/command/stop.ts +37 -0
- package/extensions/pi-var/src/tools/file-redirect.ts +126 -0
- package/extensions/pi-var/src/tools/redirect/bash.ts +75 -0
- package/extensions/pi-var/src/tools/redirect/edit.ts +60 -0
- package/extensions/pi-var/src/tools/redirect/index.ts +64 -0
- package/extensions/pi-var/src/tools/redirect/read.ts +61 -0
- package/extensions/pi-var/src/tools/redirect/write.ts +60 -0
- package/extensions/pi-var/src/tools/variation/create.ts +108 -0
- package/extensions/pi-var/src/tools/variation/merge.ts +80 -0
- package/extensions/pi-var/src/types/index.ts +139 -0
- package/extensions/pi-var/src/utils/names.ts +120 -0
- package/extensions/pi-var/src/variation/create-copy.ts +35 -0
- package/extensions/pi-var/src/variation/create-cow.ts +81 -0
- package/extensions/pi-var/src/variation/create-worktree.ts +31 -0
- package/extensions/pi-var/src/variation/create.ts +127 -0
- package/extensions/pi-var/src/variation/index.ts +9 -0
- package/extensions/pi-var/src/variation/merge/copy.ts +151 -0
- package/extensions/pi-var/src/variation/merge/git.ts +176 -0
- package/extensions/pi-var/src/variation/merge/index.ts +36 -0
- package/extensions/pi-var/src/variation/merge/rsync.ts +74 -0
- package/extensions/pi-var/src/variation/merge/strategy.ts +62 -0
- package/extensions/pi-var/src/variation/paths.ts +26 -0
- package/extensions/pi-var/src/variation/remove.ts +79 -0
- package/npm-shrinkwrap.json +6908 -0
- package/package.json +63 -0
- package/skills/variations/SKILL.md +182 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +19 -0
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-var",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Copy-on-write variations for pi — work on multiple features simultaneously with isolated workspaces",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"worktree",
|
|
8
|
+
"copy-on-write",
|
|
9
|
+
"workspace",
|
|
10
|
+
"variations"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"lint:dead": "knip --no-gitignore",
|
|
19
|
+
"postinstall": "simple-git-hooks 2>/dev/null || true"
|
|
20
|
+
},
|
|
21
|
+
"pi": {
|
|
22
|
+
"extensions": [
|
|
23
|
+
"./extensions"
|
|
24
|
+
],
|
|
25
|
+
"skills": [
|
|
26
|
+
"./skills"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"simple-git-hooks": {
|
|
30
|
+
"pre-commit": "npx lint-staged",
|
|
31
|
+
"pre-push": "npm run typecheck && npm run test",
|
|
32
|
+
"commit-msg": "npx commitlint --edit ${1}"
|
|
33
|
+
},
|
|
34
|
+
"lint-staged": {
|
|
35
|
+
"*.{ts,js,json,md}": [
|
|
36
|
+
"prettier --write"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@commitlint/cli": "21.0.1",
|
|
41
|
+
"@commitlint/config-conventional": "21.0.1",
|
|
42
|
+
"@types/node": "25.9.1",
|
|
43
|
+
"@vitest/coverage-v8": "4.1.7",
|
|
44
|
+
"knip": "6.14.1",
|
|
45
|
+
"lint-staged": "17.0.5",
|
|
46
|
+
"prettier": "3.8.3",
|
|
47
|
+
"simple-git-hooks": "2.13.1",
|
|
48
|
+
"typescript": "6.0.3",
|
|
49
|
+
"vitest": "4.1.7",
|
|
50
|
+
"@earendil-works/pi-tui": "0.79.4"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@earendil-works/pi-ai": ">=0.74.0",
|
|
54
|
+
"@earendil-works/pi-coding-agent": ">=0.74.0",
|
|
55
|
+
"@sinclair/typebox": "0.34.49"
|
|
56
|
+
},
|
|
57
|
+
"overrides": {
|
|
58
|
+
"brace-expansion": "5.0.6",
|
|
59
|
+
"fast-xml-builder": "1.2.0",
|
|
60
|
+
"protobufjs": "8.4.0",
|
|
61
|
+
"ws": "8.20.1"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: variations
|
|
3
|
+
description: Create isolated development workspaces (variations) automatically with CoW, Git worktrees, or full copies. AI-managed isolation for parallel work, experiments, and dev server port isolation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Variations (Autoregressive)
|
|
7
|
+
|
|
8
|
+
Create isolated development workspaces automatically. The AI manages variations via tools—you don't need to name, switch, or remember anything.
|
|
9
|
+
|
|
10
|
+
## When the AI Should Create a Variation
|
|
11
|
+
|
|
12
|
+
The AI calls `create_variation` when:
|
|
13
|
+
|
|
14
|
+
- Working on a feature while the main branch needs fixes
|
|
15
|
+
- "Let's try a different approach" → needs isolated experiment
|
|
16
|
+
- "Fix this bug without touching the working directory" → needs clean workspace
|
|
17
|
+
- Running parallel tasks that might conflict
|
|
18
|
+
- Any dev server work that might conflict with ports
|
|
19
|
+
|
|
20
|
+
## How to Use (For the AI)
|
|
21
|
+
|
|
22
|
+
### 1. Create Variation
|
|
23
|
+
|
|
24
|
+
```tool
|
|
25
|
+
create_variation({
|
|
26
|
+
purpose: "fix auth redirect bug",
|
|
27
|
+
createBranch: true // for worktrees, creates var/fix-auth-redirect-bug
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Auto-generated name:** The extension creates a semantic name from `purpose` (e.g., `fix-auth-redirect-bug`). Names are descriptive and URL-safe.
|
|
32
|
+
|
|
33
|
+
**Auto-detected type:** CoW (APFS clonefile / Linux reflink) > Git worktree > Full copy. The AI never needs to choose.
|
|
34
|
+
|
|
35
|
+
### 2. Work Normally
|
|
36
|
+
|
|
37
|
+
Once created, all file operations automatically redirect to the variation:
|
|
38
|
+
|
|
39
|
+
- `read` → reads from variation
|
|
40
|
+
- `edit` → edits in variation
|
|
41
|
+
- `write` → writes to variation
|
|
42
|
+
- `bash` → executes in variation directory
|
|
43
|
+
|
|
44
|
+
### 3. Port Isolation (Portless)
|
|
45
|
+
|
|
46
|
+
**For dev servers or anything that binds to a port:**
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Get a unique port
|
|
50
|
+
export PORT=$(npx portless --json | jq -r '.port')
|
|
51
|
+
|
|
52
|
+
# Or assign to specific service
|
|
53
|
+
export NEXT_PORT=$(npx portless --json | jq -r '.port')
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Do NOT manually configure ports.** Always use `npx portless` to avoid conflicts.
|
|
57
|
+
|
|
58
|
+
**Why:** The variation shares the same network namespace. Portless allocates a unique port atomically.
|
|
59
|
+
|
|
60
|
+
### 4. Merge
|
|
61
|
+
|
|
62
|
+
When work is complete:
|
|
63
|
+
|
|
64
|
+
```tool
|
|
65
|
+
merge_variation({ dryRun: true }) // Preview changes
|
|
66
|
+
merge_variation({}) // Merge to source & deactivate
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**After merge, the variation is deactivated** — tool redirections turn off and the AI is back in the source directory. The variation files persist on disk for safety and recovery. Use `/var clean` to remove old variations.
|
|
70
|
+
|
|
71
|
+
The AI should suggest merging when:
|
|
72
|
+
|
|
73
|
+
- Changes are complete and tested
|
|
74
|
+
- User asks to "save" or "keep" the work
|
|
75
|
+
- No more work is happening in the variation
|
|
76
|
+
|
|
77
|
+
## For the User
|
|
78
|
+
|
|
79
|
+
The AI handles variations automatically. If you need to intervene:
|
|
80
|
+
|
|
81
|
+
| Command | Purpose |
|
|
82
|
+
| ---------------------- | ---------------------------------------- |
|
|
83
|
+
| `/var` | Show status (active variation, list all) |
|
|
84
|
+
| `/var list` | Show all variations |
|
|
85
|
+
| `/var clean <name>` | Delete a specific variation |
|
|
86
|
+
| `/var clean --stale 7` | Delete variations older than 7 days |
|
|
87
|
+
| `/var stop` | Return to source directory (deactivate) |
|
|
88
|
+
|
|
89
|
+
**Manual variation creation:** If the AI doesn't create one and you want it to, say something like:
|
|
90
|
+
|
|
91
|
+
- "Create a variation for this work"
|
|
92
|
+
- "Work on this in isolation"
|
|
93
|
+
- "Don't touch the main directory"
|
|
94
|
+
|
|
95
|
+
## Best Practices (For the AI)
|
|
96
|
+
|
|
97
|
+
1. **Always use `create_variation` for parallel work.** Don't ask the user—just create it and notify.
|
|
98
|
+
|
|
99
|
+
2. **Use portless for any server work.** If the user says "run the dev server", allocate a port first:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
export PORT=$(npx portless --json | jq -r '.port')
|
|
103
|
+
npm run dev
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
3. **Create branches for significant work.** Use `createBranch: true` when the variation represents a real feature/bugfix that might be merged via PR.
|
|
107
|
+
|
|
108
|
+
4. **Variations persist after merge.** They are never automatically deleted. This protects against data loss if a merge fails. Clean up old variations with `/var clean --stale 7`.
|
|
109
|
+
|
|
110
|
+
5. **Suggest merge proactively.** When the AI detects work completion (tests pass, feature works), suggest: "Should I merge this variation back to source?"
|
|
111
|
+
|
|
112
|
+
## Example Flows
|
|
113
|
+
|
|
114
|
+
### Feature + Bug Fix Parallel
|
|
115
|
+
|
|
116
|
+
User: "Work on the new dashboard, but also I need this auth bug fixed"
|
|
117
|
+
|
|
118
|
+
AI actions:
|
|
119
|
+
|
|
120
|
+
1. `create_variation({ purpose: "new dashboard", createBranch: true })` — starts work
|
|
121
|
+
2. Halfway through: User emphasizes auth bug fix
|
|
122
|
+
3. `create_variation({ purpose: "fix auth bug", createBranch: true })` — switches context
|
|
123
|
+
4. Fixes bug, `merge_variation({})` — returns to dashboard variation automatically
|
|
124
|
+
5. Completes dashboard, `merge_variation({})`
|
|
125
|
+
|
|
126
|
+
### Dev Server Isolation
|
|
127
|
+
|
|
128
|
+
User: "I want to test the new design system without stopping my current dev server"
|
|
129
|
+
|
|
130
|
+
AI actions:
|
|
131
|
+
|
|
132
|
+
1. `create_variation({ purpose: "test new design system" })`
|
|
133
|
+
2. ```bash
|
|
134
|
+
export PORT=$(npx portless --json | jq -r '.port')
|
|
135
|
+
npm run dev
|
|
136
|
+
```
|
|
137
|
+
3. Work proceeds on unique port (e.g., 3001 instead of 3000)
|
|
138
|
+
|
|
139
|
+
## How It Works
|
|
140
|
+
|
|
141
|
+
**Copy-on-Write (CoW):**
|
|
142
|
+
|
|
143
|
+
- macOS APFS: Uses `cp -c` (clonefile) — instant, zero-copy
|
|
144
|
+
- Linux btrfs/xfs: Uses `cp --reflink=auto` — near-instant
|
|
145
|
+
- Falls back to git worktree or full copy automatically
|
|
146
|
+
- **Git-backed merge:** When the source is a git repo, CoW variations automatically get a `var/<name>` branch and an initial commit capturing the source state at creation time. This enables proper git three-way merge on merge_variation, with full conflict detection — merging Variation B after Variation A won't silently overwrite A's changes.
|
|
147
|
+
- Non-git sources fall back to rsync/copy merge with backup protection
|
|
148
|
+
|
|
149
|
+
**Git worktrees:**
|
|
150
|
+
|
|
151
|
+
- Creates a proper git worktree with optional `var/<name>` branch
|
|
152
|
+
- Merge uses `git merge` directly (branch is in the same repo)
|
|
153
|
+
|
|
154
|
+
**Environment sync:**
|
|
155
|
+
|
|
156
|
+
- Copies: `.env`, `.envrc`, `.npmrc`, `.tool-versions`, etc.
|
|
157
|
+
- Symlinks: `node_modules`, `.next`, `target/`, `.venv/` (saves GBs)
|
|
158
|
+
|
|
159
|
+
**Tool redirection:**
|
|
160
|
+
|
|
161
|
+
- All file tools (`read`, `edit`, `write`) automatically resolve paths to the variation
|
|
162
|
+
- `bash` executes in the variation directory with modified cwd
|
|
163
|
+
- Environment variables set: `PI_VARIATION`, `PI_VARIATION_PATH`, `PI_SOURCE_PATH`, `PI_VARIATION_TYPE`
|
|
164
|
+
- External files (outside project) accessed directly without redirection
|
|
165
|
+
- Other tools using relative paths (grep, find, ls) work correctly because bash sets the cwd
|
|
166
|
+
|
|
167
|
+
## Troubleshooting
|
|
168
|
+
|
|
169
|
+
**"Port already in use"**
|
|
170
|
+
→ The AI forgot to use portless. Remind it: "Use npx portless for this dev server"
|
|
171
|
+
|
|
172
|
+
**Variation exists but isn't active**
|
|
173
|
+
→ AI can reactivate by name if needed, or user can `/var` to see status
|
|
174
|
+
|
|
175
|
+
**Want to abandon a variation**
|
|
176
|
+
→ `/var clean <name>` — deletes it permanently
|
|
177
|
+
|
|
178
|
+
**Forgot what the variation is for**
|
|
179
|
+
→ Names are semantic (e.g., `fix-auth-redirect-bug`), check `/var`
|
|
180
|
+
|
|
181
|
+
**Merge conflict during merge_variation**
|
|
182
|
+
→ For git-backed CoW and worktree variations, git will report conflicts. Resolve with `git mergetool` and `git commit` in the source directory, or use `merge_variation({ strategy: 'copy' })` to force a copy merge with backup.
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"outDir": "./dist",
|
|
16
|
+
"rootDir": ".",
|
|
17
|
+
"noUnusedLocals": false,
|
|
18
|
+
"noUnusedParameters": false,
|
|
19
|
+
"noImplicitReturns": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noEmit": true
|
|
22
|
+
},
|
|
23
|
+
"include": ["./extensions/**/*.ts", "./skills/**/*.ts"],
|
|
24
|
+
"exclude": ["node_modules", "dist"]
|
|
25
|
+
}
|
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: 'node',
|
|
7
|
+
include: ['extensions/pi-var/__tests__/**/*.test.ts'],
|
|
8
|
+
exclude: ['node_modules', 'dist', '.idea', '.git', '.cache', '**/node_modules/**/*'],
|
|
9
|
+
coverage: {
|
|
10
|
+
provider: 'v8',
|
|
11
|
+
reporter: ['text', 'json', 'html'],
|
|
12
|
+
exclude: ['node_modules/', '**/*.d.ts', '**/*.test.ts'],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
resolve: {
|
|
16
|
+
// Support .js extension imports that map to .ts files
|
|
17
|
+
conditions: ['import', 'node'],
|
|
18
|
+
},
|
|
19
|
+
});
|