Skip to content

Commit 6921898

Browse files
committed
feat: add try do command to graduate tries to permanent projects
Moves current try directory to ~/Work (TRY_DO_PATH), strips the date prefix for a clean name, and ensures git is initialized. Leaves a symlink behind so graduated tries still appear in the TUI with a ⭐.
1 parent 559279c commit 6921898

File tree

4 files changed

+369
-6
lines changed

4 files changed

+369
-6
lines changed

README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ try ./path/to/repo [name] # Use another repo as the worktre
131131
try worktree dir [name] # Same as above, explicit CLI form
132132
try clone https://github.com/user/repo.git # Clone repo into date-prefixed directory
133133
try https://github.com/user/repo.git # Shorthand for clone (same as above)
134+
try do # Graduate current try dir to ~/Work
134135
try --help # See all options
135136
```
136137

@@ -165,6 +166,26 @@ Supported git URI formats:
165166

166167
The `.git` suffix is automatically removed from URLs when generating directory names.
167168

169+
### Graduating a Try
170+
171+
When an experiment becomes a real project, `try do` moves it out of the tries directory:
172+
173+
```bash
174+
$ cd ~/src/tries/2025-08-17-redis-pool
175+
$ try do
176+
177+
From: ~/src/tries/2025-08-17-redis-pool
178+
To: ~/Work/redis-pool
179+
180+
Name [redis-pool]:
181+
```
182+
183+
- Strips the date prefix to propose a clean name
184+
- Moves the directory to `~/Work` (configurable via `TRY_DO_PATH`)
185+
- Leaves a symlink behind so it still appears in the TUI with a ⭐
186+
- Initializes a git repo if one doesn't already exist
187+
- Works from any subdirectory within the try
188+
168189
### Keyboard Shortcuts
169190

170191
- `↑/↓` or `Ctrl-P/N/J/K` - Navigate
@@ -184,6 +205,14 @@ export TRY_PATH=~/code/sketches
184205

185206
Default: `~/src/tries`
186207

208+
Set `TRY_DO_PATH` to change where graduated projects land:
209+
210+
```bash
211+
export TRY_DO_PATH=~/projects
212+
```
213+
214+
Default: `~/Work`
215+
187216
## Nix
188217

189218
### Quick start
@@ -259,7 +288,7 @@ A: Because you have 200 directories and can't remember if you called it `test-re
259288
A: fzf is great for files. This is specifically for project directories, with time-awareness and auto-creation built in.
260289

261290
**Q: Can I use this for real projects?**
262-
A: You can, but it's designed for experiments. Real projects deserve real names in real locations.
291+
A: It's designed for experiments. When one sticks, `try do` graduates it to a permanent home with a clean name.
263292

264293
**Q: What if I have thousands of experiments?**
265294
A: First, welcome to the club. Second, it handles it fine - the scoring algorithm ensures relevant stuff stays on top.

spec/command_line.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,46 @@ try . <name> # Shorthand (requires name)
9898
- Returns shell script to cd into worktree
9999
- `try .` without a name is NOT supported (too easy to invoke accidentally)
100100

101+
### do
102+
103+
Move a try directory to a permanent work location.
104+
105+
```
106+
try do
107+
try exec do
108+
```
109+
110+
**Arguments:**
111+
- None. Operates on the current working directory.
112+
113+
**Behavior:**
114+
- Must be run from inside a try directory (or subdirectory of one)
115+
- Extracts the immediate child directory of the tries path
116+
- Strips date prefix (`YYYY-MM-DD-`) to propose a clean name
117+
- Prompts for name confirmation (enter to accept, or type a new name)
118+
- Checks destination doesn't already exist
119+
- Leaves a symlink at the original location pointing to the new path
120+
- Graduated entries appear with ⭐ instead of 📁 in the TUI
121+
- Returns shell script to move directory and ensure git is initialized
122+
- Errors if the entry is already a symlink (already graduated)
123+
124+
**Examples:**
125+
```
126+
$ cd ~/src/tries/2025-08-17-redis-pool
127+
$ try do
128+
Name [redis-pool]:
129+
# Moves to ~/Work/redis-pool, ensures git init
130+
131+
$ try do
132+
Name [redis-pool]: my-pool
133+
# Moves to ~/Work/my-pool instead
134+
```
135+
136+
**Error cases:**
137+
- Not inside tries directory → error + exit 1
138+
- At tries root (not in a specific try) → "cd into a try directory first"
139+
- Destination already exists → error + exit 1
140+
101141
### init
102142

103143
Output shell function definition for shell integration.
@@ -180,6 +220,7 @@ Commands are chained with `&& \` for readability, with 2-space indent on continu
180220
| `HOME` | Used to resolve default tries path (`$HOME/src/tries`) |
181221
| `SHELL` | Used by `init` to detect shell type |
182222
| `NO_COLOR` | If set, disables colors (equivalent to `--no-colors`) |
223+
| `TRY_DO_PATH` | Override destination for `try do` (default: `~/Work`) |
183224

184225
## Defaults
185226

@@ -246,6 +287,10 @@ try clone https://github.com/user/repo my-fork
246287
# Create git worktree (from within a repo)
247288
try worktree feature-branch
248289

290+
# Graduate a try to a permanent project
291+
cd ~/src/tries/2025-08-17-redis-pool
292+
try do
293+
249294
# Show version
250295
try --version
251296

spec/tests/test_32_do.sh

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Do command tests
2+
# Spec: try do moves a try directory to a permanent work directory
3+
4+
section "do"
5+
6+
# Setup: Create isolated test directories
7+
DO_TRIES_DIR=$(mktemp -d)
8+
DO_WORK_DIR=$(mktemp -d)
9+
mkdir -p "$DO_TRIES_DIR/2025-08-17-redis-pool"
10+
touch "$DO_TRIES_DIR/2025-08-17-redis-pool/README.md"
11+
mkdir -p "$DO_TRIES_DIR/2025-09-01-no-date-strip"
12+
mkdir -p "$DO_TRIES_DIR/plain-project"
13+
14+
# Test: try do from outside tries dir shows error
15+
output=$(TRY_DO_PATH="$DO_WORK_DIR" try_run --path="$DO_TRIES_DIR" do 2>&1)
16+
exit_code=$?
17+
if echo "$output" | grep -qi "not inside tries directory"; then
18+
pass
19+
else
20+
fail "do outside tries dir should error" "not inside tries directory" "$output"
21+
fi
22+
23+
# Test: try do from outside tries dir exits non-zero
24+
if [ $exit_code -ne 0 ]; then
25+
pass
26+
else
27+
fail "do outside tries dir should exit non-zero" "exit code != 0" "exit code: $exit_code"
28+
fi
29+
30+
# Test: try exec do from outside tries dir shows error
31+
output=$(TRY_DO_PATH="$DO_WORK_DIR" try_run --path="$DO_TRIES_DIR" exec do 2>&1)
32+
exit_code=$?
33+
if echo "$output" | grep -qi "not inside tries directory"; then
34+
pass
35+
else
36+
fail "exec do outside tries dir should error" "not inside tries directory" "$output"
37+
fi
38+
39+
# Test: try do at tries root shows specific message
40+
output=$(cd "$DO_TRIES_DIR" && TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>&1)
41+
if echo "$output" | grep -qi "cd into a try directory first"; then
42+
pass
43+
else
44+
fail "do at tries root should say cd into a try directory" "cd into a try directory first" "$output"
45+
fi
46+
47+
# Test: try do generates mv command (accept default name)
48+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
49+
if echo "$output" | grep -q "mv"; then
50+
pass
51+
else
52+
fail "do should generate mv command" "mv in output" "$output"
53+
fi
54+
55+
# Test: try do strips date prefix for default name
56+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
57+
if echo "$output" | grep -q "redis-pool"; then
58+
pass
59+
else
60+
fail "do should use stripped name as default" "redis-pool in output" "$output"
61+
fi
62+
63+
# Test: try do destination uses DO_WORK_DIR
64+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
65+
if echo "$output" | grep -q "$DO_WORK_DIR/redis-pool"; then
66+
pass
67+
else
68+
fail "do should move to DO_WORK_DIR" "$DO_WORK_DIR/redis-pool" "$output"
69+
fi
70+
71+
# Test: try do includes git init
72+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
73+
if echo "$output" | grep -q "git init"; then
74+
pass
75+
else
76+
fail "do should include git init" "git init in output" "$output"
77+
fi
78+
79+
# Test: try do with custom name uses that name
80+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "my-project" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
81+
if echo "$output" | grep -q "$DO_WORK_DIR/my-project"; then
82+
pass
83+
else
84+
fail "do with custom name should use it" "$DO_WORK_DIR/my-project" "$output"
85+
fi
86+
87+
# Test: try do prompts for name on stderr
88+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>&1 1>/dev/null)
89+
if echo "$output" | grep -q "Name \[redis-pool\]:"; then
90+
pass
91+
else
92+
fail "do should prompt for name on stderr" "Name [redis-pool]:" "$output"
93+
fi
94+
95+
# Test: try do shows From path on stderr
96+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>&1 1>/dev/null)
97+
if echo "$output" | grep -q "From:"; then
98+
pass
99+
else
100+
fail "do should show From: on stderr" "From:" "$output"
101+
fi
102+
103+
# Test: try do shows To path on stderr
104+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>&1 1>/dev/null)
105+
if echo "$output" | grep -q "To:"; then
106+
pass
107+
else
108+
fail "do should show To: on stderr" "To:" "$output"
109+
fi
110+
111+
# Test: try do when destination exists shows error
112+
mkdir -p "$DO_WORK_DIR/redis-pool"
113+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>&1)
114+
exit_code=$?
115+
if echo "$output" | grep -qi "destination already exists"; then
116+
pass
117+
else
118+
fail "do should error when destination exists" "destination already exists" "$output"
119+
fi
120+
rmdir "$DO_WORK_DIR/redis-pool"
121+
122+
# Test: try do when destination exists exits non-zero
123+
mkdir -p "$DO_WORK_DIR/redis-pool"
124+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>&1)
125+
exit_code=$?
126+
if [ $exit_code -ne 0 ]; then
127+
pass
128+
else
129+
fail "do should exit non-zero when destination exists" "exit code != 0" "exit code: $exit_code"
130+
fi
131+
rmdir "$DO_WORK_DIR/redis-pool"
132+
133+
# Test: try do from subdirectory of try dir still works (uses parent try dir)
134+
mkdir -p "$DO_TRIES_DIR/2025-08-17-redis-pool/src/lib"
135+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool/src/lib" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
136+
if echo "$output" | grep -q "2025-08-17-redis-pool"; then
137+
pass
138+
else
139+
fail "do from subdirectory should use parent try dir" "2025-08-17-redis-pool in output" "$output"
140+
fi
141+
142+
# Test: try do never includes gh repo create
143+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
144+
if ! echo "$output" | grep -q "gh repo create"; then
145+
pass
146+
else
147+
fail "do should not include gh repo create" "no gh repo create" "$output"
148+
fi
149+
150+
# Test: try do script ends with cd to destination
151+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
152+
last_line=$(echo "$output" | tail -1)
153+
if echo "$last_line" | grep -q "cd "; then
154+
pass
155+
else
156+
fail "do script should end with cd" "cd as last command" "$output"
157+
fi
158+
159+
# Test: try do script includes ln -s (leaves symlink behind)
160+
output=$(cd "$DO_TRIES_DIR/2025-08-17-redis-pool" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>/dev/null)
161+
if echo "$output" | grep -q "ln -s"; then
162+
pass
163+
else
164+
fail "do should include ln -s" "ln -s in output" "$output"
165+
fi
166+
167+
# Test: try do on already-graduated symlink shows error
168+
mkdir -p "$DO_WORK_DIR/already-done"
169+
ln -s "$DO_WORK_DIR/already-done" "$DO_TRIES_DIR/2025-09-01-graduated"
170+
output=$(cd "$DO_TRIES_DIR/2025-09-01-graduated" && echo "" | TRY_DO_PATH="$DO_WORK_DIR" eval $TRY_CMD --path="$DO_TRIES_DIR" do 2>&1)
171+
exit_code=$?
172+
if echo "$output" | grep -qi "already graduated"; then
173+
pass
174+
else
175+
fail "do on symlink should say already graduated" "already graduated" "$output"
176+
fi
177+
178+
# Test: try do on already-graduated symlink exits non-zero
179+
if [ $exit_code -ne 0 ]; then
180+
pass
181+
else
182+
fail "do on symlink should exit non-zero" "exit code != 0" "exit code: $exit_code"
183+
fi
184+
rm -f "$DO_TRIES_DIR/2025-09-01-graduated"
185+
rm -rf "$DO_WORK_DIR/already-done"
186+
187+
# Test: symlinked entries show star emoji in TUI
188+
ln -s "$DO_WORK_DIR" "$DO_TRIES_DIR/2025-10-01-starred"
189+
output=$(try_run --path="$DO_TRIES_DIR" --and-exit exec 2>&1)
190+
if echo "$output" | grep -q ""; then
191+
pass
192+
else
193+
fail "symlinked entry should show star emoji" "⭐ in output" "$output"
194+
fi
195+
rm -f "$DO_TRIES_DIR/2025-10-01-starred"
196+
197+
# Test: help text includes try do
198+
output=$(try_run --help 2>&1)
199+
if echo "$output" | grep -q "try do"; then
200+
pass
201+
else
202+
fail "help should mention try do" "try do in help" "$output"
203+
fi
204+
205+
# Test: help text mentions TRY_DO_PATH
206+
output=$(try_run --help 2>&1)
207+
if echo "$output" | grep -q "TRY_DO_PATH"; then
208+
pass
209+
else
210+
fail "help should mention TRY_DO_PATH" "TRY_DO_PATH in help" "$output"
211+
fi
212+
213+
# Cleanup
214+
rm -rf "$DO_TRIES_DIR" "$DO_WORK_DIR"

0 commit comments

Comments
 (0)