rhachet-roles-ehmpathy 1.10.0 → 1.11.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.
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = shared helpers for git worktree management
4
+ #
5
+ # .why = centralizes path resolution and branch sanitization logic
6
+ # used by git.worktree.{get,set,del}.sh scripts
7
+ #
8
+ # .how = source this file to get access to:
9
+ # - resolve_worktrees_dir: computes $REPO_WORKTREES_DIR
10
+ # - sanitize_branch_name: converts branch to worktree name
11
+ # - get_repo_name: extracts repo name from gitroot
12
+ #
13
+ # guarantee:
14
+ # - works from root repo or from within a worktree
15
+ # - consistent path resolution across all worktree scripts
16
+ ######################################################################
17
+
18
+ # resolve the worktrees directory for this repo
19
+ # handles both root repo and worktree contexts
20
+ resolve_worktrees_dir() {
21
+ local gitroot
22
+ gitroot="$(git rev-parse --show-toplevel)"
23
+
24
+ local reponame
25
+ reponame="$(basename "$gitroot")"
26
+
27
+ # detect if we're in a worktree (path contains _worktrees)
28
+ if [[ "$gitroot" == *"_worktrees"* ]]; then
29
+ # we're in a worktree - reuse same _worktrees/$reponame dir
30
+ echo "${gitroot%/*}"
31
+ else
32
+ # root repo - compute sibling _worktrees dir
33
+ echo "$(dirname "$gitroot")/_worktrees/$reponame"
34
+ fi
35
+ }
36
+
37
+ # sanitize branch name for use as directory name
38
+ # vlad/practs => vlad.practs
39
+ sanitize_branch_name() {
40
+ local branch="$1"
41
+ echo "${branch//\//.}"
42
+ }
43
+
44
+ # get the repo name (works from root repo or worktree)
45
+ get_repo_name() {
46
+ local gitroot
47
+ gitroot="$(git rev-parse --show-toplevel)"
48
+
49
+ # detect if we're in a worktree (path contains _worktrees)
50
+ if [[ "$gitroot" == *"_worktrees"* ]]; then
51
+ # extract repo name from _worktrees/$reponame/$worktree path
52
+ # gitroot = /path/to/_worktrees/$reponame/$worktree
53
+ local worktrees_parent="${gitroot%/*}" # /path/to/_worktrees/$reponame
54
+ basename "$worktrees_parent"
55
+ else
56
+ basename "$gitroot"
57
+ fi
58
+ }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = remove a git worktree
4
+ #
5
+ # .why = clean up worktrees no longer needed
6
+ #
7
+ # .how = removes worktree at @gitroot/../_worktrees/$reponame/$branch
8
+ #
9
+ # usage:
10
+ # git.worktree.del.sh <branch>
11
+ #
12
+ # guarantee:
13
+ # - idempotent: [DELETE] if exists, [SKIP] if not found
14
+ # - works from root repo or from within a worktree
15
+ ######################################################################
16
+
17
+ set -euo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+
21
+ # source shared helpers
22
+ source "$SCRIPT_DIR/git.worktree.common.sh"
23
+
24
+ # parse arguments
25
+ BRANCH="${1:-}"
26
+
27
+ # validate branch argument
28
+ if [[ -z "$BRANCH" ]]; then
29
+ echo "error: branch name required"
30
+ echo "usage: git.worktree.del.sh <branch>"
31
+ exit 1
32
+ fi
33
+
34
+ # resolve paths
35
+ REPO_WORKTREES_DIR="$(resolve_worktrees_dir)"
36
+ WORKTREE_NAME="$(sanitize_branch_name "$BRANCH")"
37
+ WORKTREE_PATH="$REPO_WORKTREES_DIR/$WORKTREE_NAME"
38
+
39
+ # delete logic
40
+ if [[ -d "$WORKTREE_PATH" ]]; then
41
+ # remove worktree via git
42
+ git worktree remove "$WORKTREE_PATH" --force 2>/dev/null || {
43
+ # fallback: manual removal if git worktree remove fails
44
+ rm -rf "$WORKTREE_PATH"
45
+ git worktree prune
46
+ }
47
+
48
+ echo "[DELETE] $WORKTREE_NAME"
49
+ else
50
+ echo "[SKIP] $WORKTREE_NAME (not found)"
51
+ fi
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = list git worktrees for this repo
4
+ #
5
+ # .why = discover existing worktrees managed by git.worktree.sh
6
+ #
7
+ # .how = lists worktrees at @gitroot/../_worktrees/$reponame/
8
+ #
9
+ # usage:
10
+ # git.worktree.get.sh
11
+ #
12
+ # guarantee:
13
+ # - works from root repo or from within a worktree
14
+ # - shows "(no worktrees)" if none exist
15
+ ######################################################################
16
+
17
+ set -euo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+
21
+ # source shared helpers
22
+ source "$SCRIPT_DIR/git.worktree.common.sh"
23
+
24
+ # resolve worktrees directory
25
+ REPO_WORKTREES_DIR="$(resolve_worktrees_dir)"
26
+ REPO_NAME="$(get_repo_name)"
27
+
28
+ # check if worktrees directory exists
29
+ if [[ ! -d "$REPO_WORKTREES_DIR" ]]; then
30
+ echo "(no worktrees)"
31
+ exit 0
32
+ fi
33
+
34
+ # list worktrees
35
+ WORKTREES=()
36
+ for dir in "$REPO_WORKTREES_DIR"/*/; do
37
+ [[ -d "$dir" ]] && WORKTREES+=("$dir")
38
+ done
39
+
40
+ # handle empty
41
+ if [[ ${#WORKTREES[@]} -eq 0 ]]; then
42
+ echo "(no worktrees)"
43
+ exit 0
44
+ fi
45
+
46
+ # output worktree list
47
+ echo "worktrees for $REPO_NAME:"
48
+ for dir in "${WORKTREES[@]}"; do
49
+ name="$(basename "$dir")"
50
+ echo " $name => $dir"
51
+ done
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = findsert a git worktree for a branch
4
+ #
5
+ # .why = enable parallel work on same repo without nested worktrees
6
+ # worktrees are placed outside repo so git diff stays clean
7
+ #
8
+ # .how = creates worktree at @gitroot/../_worktrees/$reponame/$branch
9
+ #
10
+ # usage:
11
+ # git.worktree.set.sh <branch> # findsert worktree
12
+ # git.worktree.set.sh <branch> --open # findsert + open in codium
13
+ # git.worktree.set.sh <branch> --main # create from origin/main
14
+ #
15
+ # guarantee:
16
+ # - idempotent: [KEEP] if exists, [CREATE] if not
17
+ # - works from root repo or from within a worktree
18
+ # - worktree placed outside repo (not nested)
19
+ ######################################################################
20
+
21
+ set -euo pipefail
22
+
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+
25
+ # source shared helpers
26
+ source "$SCRIPT_DIR/git.worktree.common.sh"
27
+
28
+ # parse arguments
29
+ BRANCH=""
30
+ FLAG_OPEN=false
31
+ FLAG_MAIN=false
32
+
33
+ while [[ $# -gt 0 ]]; do
34
+ case $1 in
35
+ --open)
36
+ FLAG_OPEN=true
37
+ shift
38
+ ;;
39
+ --main)
40
+ FLAG_MAIN=true
41
+ shift
42
+ ;;
43
+ -*)
44
+ echo "error: unknown flag '$1'"
45
+ echo "usage: git.worktree.set.sh <branch> [--open] [--main]"
46
+ exit 1
47
+ ;;
48
+ *)
49
+ if [[ -z "$BRANCH" ]]; then
50
+ BRANCH="$1"
51
+ else
52
+ echo "error: unexpected argument '$1'"
53
+ exit 1
54
+ fi
55
+ shift
56
+ ;;
57
+ esac
58
+ done
59
+
60
+ # validate branch argument
61
+ if [[ -z "$BRANCH" ]]; then
62
+ echo "error: branch name required"
63
+ echo "usage: git.worktree.set.sh <branch> [--open] [--main]"
64
+ exit 1
65
+ fi
66
+
67
+ # resolve paths
68
+ REPO_WORKTREES_DIR="$(resolve_worktrees_dir)"
69
+ WORKTREE_NAME="$(sanitize_branch_name "$BRANCH")"
70
+ WORKTREE_PATH="$REPO_WORKTREES_DIR/$WORKTREE_NAME"
71
+
72
+ # ensure parent directory exists
73
+ mkdir -p "$REPO_WORKTREES_DIR"
74
+
75
+ # findsert logic
76
+ if [[ -d "$WORKTREE_PATH" ]]; then
77
+ echo "[KEEP] $WORKTREE_NAME => $WORKTREE_PATH"
78
+ else
79
+ # create worktree
80
+ if [[ "$FLAG_MAIN" == true ]]; then
81
+ # fetch latest main first
82
+ git fetch origin main 2>/dev/null || git fetch origin master 2>/dev/null || true
83
+
84
+ # create new branch from origin/main
85
+ git worktree add -b "$BRANCH" "$WORKTREE_PATH" origin/main 2>/dev/null || \
86
+ git worktree add -b "$BRANCH" "$WORKTREE_PATH" origin/master
87
+ else
88
+ # check if branch exists
89
+ if git show-ref --verify --quiet "refs/heads/$BRANCH" 2>/dev/null; then
90
+ # branch exists locally, checkout existing
91
+ git worktree add "$WORKTREE_PATH" "$BRANCH"
92
+ elif git show-ref --verify --quiet "refs/remotes/origin/$BRANCH" 2>/dev/null; then
93
+ # branch exists on remote, track it
94
+ git worktree add --track -b "$BRANCH" "$WORKTREE_PATH" "origin/$BRANCH"
95
+ else
96
+ # create new branch from current HEAD
97
+ git worktree add -b "$BRANCH" "$WORKTREE_PATH"
98
+ fi
99
+ fi
100
+
101
+ echo "[CREATE] $WORKTREE_NAME => $WORKTREE_PATH"
102
+ fi
103
+
104
+ # open in codium if requested
105
+ if [[ "$FLAG_OPEN" == true ]]; then
106
+ echo "opening in codium..."
107
+ codium "$WORKTREE_PATH"
108
+ fi
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = dispatcher for git worktree management
4
+ #
5
+ # .why = single entry point for get|set|del subcommands
6
+ # enables convenient `git.worktree.sh get|set|del` interface
7
+ #
8
+ # .how = routes to git.worktree.{get,set,del}.sh based on subcommand
9
+ #
10
+ # usage:
11
+ # git.worktree.sh get # list worktrees
12
+ # git.worktree.sh set <branch> # findsert worktree
13
+ # git.worktree.sh set <branch> --open # findsert + open in codium
14
+ # git.worktree.sh set <branch> --main # create from origin/main
15
+ # git.worktree.sh del <branch> # remove worktree
16
+ #
17
+ # guarantee:
18
+ # - dispatches to correct subcommand script
19
+ # - shows usage on invalid/missing subcommand
20
+ # - fail-fast on errors
21
+ ######################################################################
22
+
23
+ set -euo pipefail
24
+
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+ SUBCOMMAND="${1:-}"
27
+
28
+ case "$SUBCOMMAND" in
29
+ get|set|del)
30
+ shift
31
+ exec "$SCRIPT_DIR/git.worktree.$SUBCOMMAND.sh" "$@"
32
+ ;;
33
+ *)
34
+ echo "usage: git.worktree.sh <command> [args]"
35
+ echo ""
36
+ echo "commands:"
37
+ echo " get list worktrees for this repo"
38
+ echo " set <branch> findsert worktree for branch"
39
+ echo " del <branch> remove worktree for branch"
40
+ echo ""
41
+ echo "options (for set):"
42
+ echo " --open open worktree in codium after creation"
43
+ echo " --main create branch from origin/main"
44
+ exit 1
45
+ ;;
46
+ esac
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = test suite for git worktree management scripts
4
+ #
5
+ # .why = verify all worktree operations work correctly
6
+ #
7
+ # .how = sets up temp git repo, exercises all commands, cleans up
8
+ #
9
+ # usage:
10
+ # ./git.worktree.test.sh
11
+ #
12
+ # guarantee:
13
+ # - creates isolated temp environment
14
+ # - cleans up after itself
15
+ # - exits 0 on success, 1 on failure
16
+ ######################################################################
17
+
18
+ set -uo pipefail
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ TEST_DIR=""
22
+ TESTS_PASSED=0
23
+ TESTS_FAILED=0
24
+
25
+ # colors for output
26
+ RED='\033[0;31m'
27
+ GREEN='\033[0;32m'
28
+ NC='\033[0m' # No Color
29
+
30
+ # test helper: assert condition
31
+ assert() {
32
+ local description="$1"
33
+ local condition="$2"
34
+
35
+ if eval "$condition"; then
36
+ echo -e " ${GREEN}✓${NC} $description"
37
+ ((TESTS_PASSED++))
38
+ else
39
+ echo -e " ${RED}✗${NC} $description"
40
+ ((TESTS_FAILED++))
41
+ fi
42
+ }
43
+
44
+ # test helper: assert output contains
45
+ assert_output_contains() {
46
+ local description="$1"
47
+ local output="$2"
48
+ local expected="$3"
49
+
50
+ if [[ "$output" == *"$expected"* ]]; then
51
+ echo -e " ${GREEN}✓${NC} $description"
52
+ ((TESTS_PASSED++))
53
+ else
54
+ echo -e " ${RED}✗${NC} $description"
55
+ echo " expected: $expected"
56
+ echo " got: $output"
57
+ ((TESTS_FAILED++))
58
+ fi
59
+ }
60
+
61
+ # setup: create temp git repo with remote
62
+ setup() {
63
+ echo "setting up test environment..."
64
+
65
+ # create temp directory
66
+ TEST_DIR="$(mktemp -d)"
67
+
68
+ # create "remote" bare repo
69
+ mkdir -p "$TEST_DIR/remote.git"
70
+ git -C "$TEST_DIR/remote.git" init --bare -q
71
+
72
+ # create "local" repo
73
+ mkdir -p "$TEST_DIR/local"
74
+ git -C "$TEST_DIR/local" init -q
75
+ git -C "$TEST_DIR/local" config user.email "test@test.com"
76
+ git -C "$TEST_DIR/local" config user.name "Test"
77
+ git -C "$TEST_DIR/local" remote add origin "$TEST_DIR/remote.git"
78
+
79
+ # initial commit
80
+ echo "test" > "$TEST_DIR/local/README.md"
81
+ git -C "$TEST_DIR/local" add .
82
+ git -C "$TEST_DIR/local" commit -m "initial commit" -q
83
+
84
+ # push to remote (handle both main and master default branch names)
85
+ git -C "$TEST_DIR/local" push -u origin HEAD:main -q 2>/dev/null || true
86
+
87
+ echo " test dir: $TEST_DIR"
88
+ echo ""
89
+ }
90
+
91
+ # teardown: clean up temp directory
92
+ teardown() {
93
+ echo ""
94
+ echo "cleaning up..."
95
+
96
+ if [[ -n "$TEST_DIR" ]] && [[ -d "$TEST_DIR" ]]; then
97
+ rm -rf "$TEST_DIR"
98
+ echo " removed: $TEST_DIR"
99
+ fi
100
+ }
101
+
102
+ # run tests
103
+ run_tests() {
104
+ local worktree_sh="$SCRIPT_DIR/git.worktree.sh"
105
+
106
+ echo "=== test: dispatcher ==="
107
+
108
+ # test: dispatcher shows usage
109
+ local usage_output
110
+ usage_output=$("$worktree_sh" 2>&1 || true)
111
+ assert_output_contains "shows usage on no args" "$usage_output" "usage: git.worktree.sh"
112
+
113
+ echo ""
114
+ echo "=== test: get (empty) ==="
115
+
116
+ # test: get with no worktrees
117
+ local get_empty
118
+ get_empty=$(cd "$TEST_DIR/local" && "$worktree_sh" get)
119
+ assert_output_contains "shows no worktrees" "$get_empty" "(no worktrees)"
120
+
121
+ echo ""
122
+ echo "=== test: set (create) ==="
123
+
124
+ # test: set creates worktree
125
+ local set_output
126
+ set_output=$(cd "$TEST_DIR/local" && "$worktree_sh" set test/branch1)
127
+ assert_output_contains "creates worktree" "$set_output" "[CREATE]"
128
+ assert_output_contains "correct path" "$set_output" "_worktrees/local/test.branch1"
129
+ assert "worktree directory exists" "[[ -d '$TEST_DIR/_worktrees/local/test.branch1' ]]"
130
+
131
+ echo ""
132
+ echo "=== test: set (idempotent) ==="
133
+
134
+ # test: set is idempotent
135
+ local set_keep
136
+ set_keep=$(cd "$TEST_DIR/local" && "$worktree_sh" set test/branch1)
137
+ assert_output_contains "keeps existing worktree" "$set_keep" "[KEEP]"
138
+
139
+ echo ""
140
+ echo "=== test: get (with worktrees) ==="
141
+
142
+ # test: get lists worktrees
143
+ local get_list
144
+ get_list=$(cd "$TEST_DIR/local" && "$worktree_sh" get)
145
+ assert_output_contains "lists worktrees header" "$get_list" "worktrees for local:"
146
+ assert_output_contains "lists worktree entry" "$get_list" "test.branch1"
147
+
148
+ echo ""
149
+ echo "=== test: set (second worktree) ==="
150
+
151
+ # test: create second worktree
152
+ local set_second
153
+ set_second=$(cd "$TEST_DIR/local" && "$worktree_sh" set feature/auth)
154
+ assert_output_contains "creates second worktree" "$set_second" "[CREATE]"
155
+ assert "second worktree exists" "[[ -d '$TEST_DIR/_worktrees/local/feature.auth' ]]"
156
+
157
+ echo ""
158
+ echo "=== test: get from worktree ==="
159
+
160
+ # test: get from within worktree resolves same dir
161
+ local get_from_wt
162
+ get_from_wt=$(cd "$TEST_DIR/_worktrees/local/test.branch1" && "$worktree_sh" get)
163
+ assert_output_contains "lists from worktree" "$get_from_wt" "worktrees for local:"
164
+ assert_output_contains "sees both worktrees" "$get_from_wt" "feature.auth"
165
+
166
+ echo ""
167
+ echo "=== test: set from worktree ==="
168
+
169
+ # test: set from within worktree creates in same _worktrees dir
170
+ local set_from_wt
171
+ set_from_wt=$(cd "$TEST_DIR/_worktrees/local/test.branch1" && "$worktree_sh" set test/from-wt)
172
+ assert_output_contains "creates from worktree" "$set_from_wt" "[CREATE]"
173
+ assert "created in same _worktrees dir" "[[ -d '$TEST_DIR/_worktrees/local/test.from-wt' ]]"
174
+
175
+ echo ""
176
+ echo "=== test: del ==="
177
+
178
+ # test: del removes worktree
179
+ local del_output
180
+ del_output=$(cd "$TEST_DIR/local" && "$worktree_sh" del test/from-wt)
181
+ assert_output_contains "deletes worktree" "$del_output" "[DELETE]"
182
+ assert "worktree removed" "[[ ! -d '$TEST_DIR/_worktrees/local/test.from-wt' ]]"
183
+
184
+ echo ""
185
+ echo "=== test: del (skip nonexistent) ==="
186
+
187
+ # test: del skips nonexistent
188
+ local del_skip
189
+ del_skip=$(cd "$TEST_DIR/local" && "$worktree_sh" del nonexistent/branch)
190
+ assert_output_contains "skips nonexistent" "$del_skip" "[SKIP]"
191
+ assert_output_contains "shows not found" "$del_skip" "(not found)"
192
+
193
+ echo ""
194
+ echo "=== test: cleanup remaining ==="
195
+
196
+ # cleanup remaining test worktrees
197
+ cd "$TEST_DIR/local" && "$worktree_sh" del test/branch1 >/dev/null
198
+ cd "$TEST_DIR/local" && "$worktree_sh" del feature/auth >/dev/null
199
+
200
+ local final_get
201
+ final_get=$(cd "$TEST_DIR/local" && "$worktree_sh" get)
202
+ assert_output_contains "all cleaned up" "$final_get" "(no worktrees)"
203
+ }
204
+
205
+ # main
206
+ main() {
207
+ echo "git.worktree.sh test suite"
208
+ echo "=========================="
209
+ echo ""
210
+
211
+ setup
212
+
213
+ # run tests (trap ensures cleanup on failure)
214
+ trap teardown EXIT
215
+ run_tests
216
+
217
+ echo ""
218
+ echo "=========================="
219
+ echo -e "passed: ${GREEN}$TESTS_PASSED${NC}"
220
+ echo -e "failed: ${RED}$TESTS_FAILED${NC}"
221
+
222
+ if [[ $TESTS_FAILED -gt 0 ]]; then
223
+ exit 1
224
+ fi
225
+
226
+ exit 0
227
+ }
228
+
229
+ main "$@"
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "rhachet-roles-ehmpathy",
3
3
  "author": "ehmpathy",
4
4
  "description": "empathetic software construction roles and skills, via rhachet",
5
- "version": "1.10.0",
5
+ "version": "1.11.0",
6
6
  "repository": "ehmpathy/rhachet-roles-ehmpathy",
7
7
  "homepage": "https://github.com/ehmpathy/rhachet-roles-ehmpathy",
8
8
  "keywords": [