rhachet-roles-ehmpathy 1.15.7 → 1.15.9

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,115 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = safe file copy within git repo
4
+ #
5
+ # .why = enables file copying without:
6
+ # - touching files outside the repo
7
+ # - copying from untracked sources
8
+ # - accidental path traversal attacks
9
+ #
10
+ # this is a controlled alternative to raw cp, which is
11
+ # denied in permissions due to security risks.
12
+ #
13
+ # usage:
14
+ # cpsafe.sh --src "path/to/source" --dest "path/to/dest"
15
+ #
16
+ # guarantee:
17
+ # - source must be git-tracked
18
+ # - dest must be within repo
19
+ # - creates parent directories if needed
20
+ # - fail-fast on errors
21
+ ######################################################################
22
+ set -euo pipefail
23
+
24
+ # parse named arguments
25
+ SRC=""
26
+ DEST=""
27
+
28
+ while [[ $# -gt 0 ]]; do
29
+ case $1 in
30
+ --src)
31
+ SRC="$2"
32
+ shift 2
33
+ ;;
34
+ --dest)
35
+ DEST="$2"
36
+ shift 2
37
+ ;;
38
+ *)
39
+ echo "unknown argument: $1"
40
+ echo "usage: cpsafe.sh --src 'source' --dest 'destination'"
41
+ exit 1
42
+ ;;
43
+ esac
44
+ done
45
+
46
+ # validate required args
47
+ if [[ -z "$SRC" ]]; then
48
+ echo "error: --src is required"
49
+ exit 1
50
+ fi
51
+
52
+ if [[ -z "$DEST" ]]; then
53
+ echo "error: --dest is required"
54
+ exit 1
55
+ fi
56
+
57
+ # ensure we're in a git repo
58
+ if ! git rev-parse --git-dir > /dev/null 2>&1; then
59
+ echo "error: not in a git repository"
60
+ exit 1
61
+ fi
62
+
63
+ # get repo root
64
+ REPO_ROOT=$(git rev-parse --show-toplevel)
65
+
66
+ # resolve absolute paths
67
+ SRC_ABS=$(realpath -m "$SRC")
68
+ DEST_ABS=$(realpath -m "$DEST")
69
+
70
+ # validate source is within repo
71
+ if [[ "$SRC_ABS" != "$REPO_ROOT"* ]]; then
72
+ echo "error: source must be within the git repository"
73
+ echo " repo root: $REPO_ROOT"
74
+ echo " source: $SRC_ABS"
75
+ exit 1
76
+ fi
77
+
78
+ # validate dest is within repo
79
+ if [[ "$DEST_ABS" != "$REPO_ROOT"* ]]; then
80
+ echo "error: destination must be within the git repository"
81
+ echo " repo root: $REPO_ROOT"
82
+ echo " dest: $DEST_ABS"
83
+ exit 1
84
+ fi
85
+
86
+ # get relative path for git ls-files check
87
+ SRC_REL="${SRC_ABS#$REPO_ROOT/}"
88
+
89
+ # validate source is git-tracked
90
+ if ! git ls-files --error-unmatch "$SRC_REL" > /dev/null 2>&1; then
91
+ echo "error: source file is not git-tracked: $SRC_REL"
92
+ exit 1
93
+ fi
94
+
95
+ # validate source exists
96
+ if [[ ! -f "$SRC_ABS" ]]; then
97
+ echo "error: source file does not exist: $SRC"
98
+ exit 1
99
+ fi
100
+
101
+ # create parent directories if needed
102
+ DEST_DIR=$(dirname "$DEST_ABS")
103
+ if [[ ! -d "$DEST_DIR" ]]; then
104
+ echo "creating directory: $DEST_DIR"
105
+ mkdir -p "$DEST_DIR"
106
+ fi
107
+
108
+ # perform the copy
109
+ # -P = don't follow symlinks, copy symlink itself
110
+ # why: a tracked symlink could point outside repo (e.g., ln -s /etc/passwd ./data.txt)
111
+ # without -P, cp would read the target content, bypassing our repo boundary checks
112
+ cp -P "$SRC_ABS" "$DEST_ABS"
113
+
114
+ DEST_REL="${DEST_ABS#$REPO_ROOT/}"
115
+ echo "copied: $SRC_REL -> $DEST_REL"
@@ -38,14 +38,14 @@ HOOKS_DIR="$SKILLS_DIR/claude.hooks"
38
38
  "$FINDSERT" \
39
39
  --hook-type SessionStart \
40
40
  --matcher "*" \
41
- --command "npx rhachet roles boot --repo .this --role any --if-present" \
41
+ --command "./node_modules/.bin/rhachet roles boot --repo .this --role any --if-present" \
42
42
  --name "sessionstart.boot" \
43
43
  --timeout 60
44
44
 
45
45
  "$FINDSERT" \
46
46
  --hook-type SessionStart \
47
47
  --matcher "*" \
48
- --command "npx rhachet roles boot --repo ehmpathy --role mechanic" \
48
+ --command "./node_modules/.bin/rhachet roles boot --repo ehmpathy --role mechanic" \
49
49
  --name "sessionstart.boot" \
50
50
  --timeout 60
51
51
 
@@ -159,10 +159,15 @@
159
159
  // sedreplace - safe bulk find-and-replace on git-tracked files only
160
160
  "Bash(.agent/repo=ehmpathy/role=mechanic/skills/.skills/claude.tools/sedreplace.sh:*)",
161
161
 
162
+ // cpsafe - safe file copy within git repo (source must be git-tracked)
163
+ "Bash(.agent/repo=ehmpathy/role=mechanic/skills/.skills/claude.tools/cpsafe.sh:*)",
164
+
162
165
  // npm read operations
163
166
  "Bash(npm view:*)",
164
167
  "Bash(npm list:*)",
165
168
  "Bash(npm remove:*)",
169
+ "Bash(npm help:*)",
170
+ "Bash(pnpm help:*)",
166
171
 
167
172
  // rhachet operations
168
173
  "Bash(npx rhachet roles boot --repo ehmpathy --role mechanic)",
@@ -183,6 +188,9 @@
183
188
  "Bash(npm run test:unit:*)",
184
189
  "Bash(npm run test:integration:*)",
185
190
  "Bash(npm run test:acceptance:*)",
191
+ "Bash(npm run test:unit -- path/to/file/example.ts)",
192
+ "Bash(npm run test:integration -- path/to/file/example.ts)",
193
+ "Bash(npm run test:acceptance -- path/to/file/example.ts)",
186
194
 
187
195
  // thorough test operations
188
196
  "Bash(THOROUGH=true npm run test:*)",
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.15.7",
5
+ "version": "1.15.9",
6
6
  "repository": "ehmpathy/rhachet-roles-ehmpathy",
7
7
  "homepage": "https://github.com/ehmpathy/rhachet-roles-ehmpathy",
8
8
  "keywords": [
@@ -60,7 +60,7 @@
60
60
  "helpful-errors": "1.5.3",
61
61
  "inquirer": "12.7.0",
62
62
  "openai": "5.8.2",
63
- "rhachet": "1.13.5",
63
+ "rhachet": "1.13.9",
64
64
  "rhachet-artifact": "1.0.0",
65
65
  "rhachet-artifact-git": "1.1.0",
66
66
  "serde-fns": "1.2.0",