rhachet-roles-ehmpathy 1.13.13 → 1.15.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.
- package/dist/logic/roles/mechanic/.skills/claude.hooks/sessionstart.notify-permissions.sh +106 -0
- package/dist/logic/roles/mechanic/.skills/{init.claude.hooks.pretooluse.sh → init.claude.hooks.pretooluse.check-permissions.sh} +4 -4
- package/dist/logic/roles/mechanic/.skills/{init.claude.hooks.forbid.stderr.redirect.sh → init.claude.hooks.pretooluse.forbid-stderr-redirect.sh} +4 -4
- package/dist/logic/roles/mechanic/.skills/init.claude.hooks.sessionstart.notify-permissions.sh +121 -0
- package/dist/logic/roles/mechanic/.skills/init.claude.hooks.sh +4 -2
- package/dist/logic/roles/mechanic/.skills/init.claude.permissions.jsonc +86 -4
- package/package.json +1 -1
- package/dist/logic/roles/mechanic/.skills/claude.tools/mvsafe.sh +0 -75
- /package/dist/logic/roles/mechanic/.skills/claude.hooks/{check.pretooluse.permissions.sh → pretooluse.check-permissions.sh} +0 -0
- /package/dist/logic/roles/mechanic/.skills/claude.hooks/{forbid.stderr.redirect.sh → pretooluse.forbid-stderr-redirect.sh} +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
######################################################################
|
|
3
|
+
# .what = SessionStart hook to notify Claude of allowed permissions
|
|
4
|
+
#
|
|
5
|
+
# .why = proactively informing Claude of pre-approved Bash commands
|
|
6
|
+
# at session start reduces interruptions from permission
|
|
7
|
+
# prompts by guiding it to use allowed patterns upfront.
|
|
8
|
+
#
|
|
9
|
+
# this complements the PreToolUse hook which blocks/nudges
|
|
10
|
+
# when Claude attempts unapproved commands, by providing
|
|
11
|
+
# the information before any attempts are made.
|
|
12
|
+
#
|
|
13
|
+
# .how = reads .claude/settings.local.json, extracts Bash permissions,
|
|
14
|
+
# outputs a formatted list of allowed commands for Claude
|
|
15
|
+
# to reference throughout the session.
|
|
16
|
+
#
|
|
17
|
+
# usage:
|
|
18
|
+
# configure in .claude/settings.local.json under hooks.SessionStart
|
|
19
|
+
#
|
|
20
|
+
# guarantee:
|
|
21
|
+
# ✔ non-blocking: always exits 0
|
|
22
|
+
# ✔ informational only: no side effects
|
|
23
|
+
# ✔ graceful fallback: exits silently if no settings found
|
|
24
|
+
######################################################################
|
|
25
|
+
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
|
|
28
|
+
# Find the .claude directory (search upward from current directory)
|
|
29
|
+
find_claude_dir() {
|
|
30
|
+
local dir="$PWD"
|
|
31
|
+
while [[ "$dir" != "/" ]]; do
|
|
32
|
+
if [[ -d "$dir/.claude" ]]; then
|
|
33
|
+
echo "$dir/.claude"
|
|
34
|
+
return 0
|
|
35
|
+
fi
|
|
36
|
+
dir="$(dirname "$dir")"
|
|
37
|
+
done
|
|
38
|
+
return 1
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Find the settings file
|
|
42
|
+
find_settings_file() {
|
|
43
|
+
local claude_dir
|
|
44
|
+
claude_dir=$(find_claude_dir) || return 1
|
|
45
|
+
local settings_file="$claude_dir/settings.local.json"
|
|
46
|
+
if [[ -f "$settings_file" ]]; then
|
|
47
|
+
echo "$settings_file"
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
return 1
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
SETTINGS_FILE=$(find_settings_file) || {
|
|
54
|
+
# No settings file found, exit silently
|
|
55
|
+
exit 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Extract Bash permissions from settings file
|
|
59
|
+
# Patterns look like: "Bash(npm run test:*)" -> extract "npm run test:*"
|
|
60
|
+
mapfile -t ALLOWED_PATTERNS < <(
|
|
61
|
+
jq -r '.permissions.allow // [] | .[] | select(startswith("Bash(")) | sub("^Bash\\("; "") | sub("\\)$"; "")' "$SETTINGS_FILE" 2>/dev/null
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# If no Bash permissions found, exit silently
|
|
65
|
+
if [[ ${#ALLOWED_PATTERNS[@]} -eq 0 ]]; then
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Transform raw permission pattern to compact bracket notation for display
|
|
70
|
+
format_pattern() {
|
|
71
|
+
local pattern="$1"
|
|
72
|
+
|
|
73
|
+
# Check if pattern ends with :*
|
|
74
|
+
if [[ "$pattern" == *":*" ]]; then
|
|
75
|
+
# Remove :* suffix and format with [p]: label (prefix match)
|
|
76
|
+
local prefix="${pattern%:*}"
|
|
77
|
+
echo "[p]: $prefix"
|
|
78
|
+
else
|
|
79
|
+
# Exact match - format with [e]: label
|
|
80
|
+
echo "[e]: $pattern"
|
|
81
|
+
fi
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Output the allowed permissions notification
|
|
85
|
+
echo ""
|
|
86
|
+
echo "=================================================="
|
|
87
|
+
echo "PRE-APPROVED BASH PERMISSIONS"
|
|
88
|
+
echo "=================================================="
|
|
89
|
+
echo ""
|
|
90
|
+
echo "The following Bash commands are pre-approved and can be used without"
|
|
91
|
+
echo "requesting permission from the user:"
|
|
92
|
+
echo ""
|
|
93
|
+
echo "([e] = exact match, [p] = prefix match - anything starting with this)"
|
|
94
|
+
echo ""
|
|
95
|
+
for pattern in "${ALLOWED_PATTERNS[@]}"; do
|
|
96
|
+
echo " $(format_pattern "$pattern")"
|
|
97
|
+
done
|
|
98
|
+
echo ""
|
|
99
|
+
echo "IMPORTANT: If you attempt a Bash command NOT on this list, you will be"
|
|
100
|
+
echo "blocked and asked to reconsider. Please check this list first before"
|
|
101
|
+
echo "using Bash commands to minimize interruptions to the user."
|
|
102
|
+
echo ""
|
|
103
|
+
echo "=================================================="
|
|
104
|
+
echo ""
|
|
105
|
+
|
|
106
|
+
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
######################################################################
|
|
3
|
-
# .what = bind
|
|
3
|
+
# .what = bind pretooluse.check-permissions hook to Claude settings
|
|
4
4
|
#
|
|
5
5
|
# .why = when Claude attempts a Bash command not covered by existing
|
|
6
6
|
# permissions, this hook provides feedback asking it to
|
|
@@ -24,7 +24,7 @@ set -euo pipefail
|
|
|
24
24
|
PROJECT_ROOT="$PWD"
|
|
25
25
|
SETTINGS_FILE="$PROJECT_ROOT/.claude/settings.local.json"
|
|
26
26
|
SKILLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
-
HOOK_SCRIPT="$SKILLS_DIR/claude.hooks/
|
|
27
|
+
HOOK_SCRIPT="$SKILLS_DIR/claude.hooks/pretooluse.check-permissions.sh"
|
|
28
28
|
|
|
29
29
|
# Verify hook script exists
|
|
30
30
|
if [[ ! -f "$HOOK_SCRIPT" ]]; then
|
|
@@ -104,7 +104,7 @@ jq --argjson hook "$HOOK_CONFIG" '
|
|
|
104
104
|
# Check if any changes were made
|
|
105
105
|
if diff -q "$SETTINGS_FILE" "$SETTINGS_FILE.tmp" >/dev/null 2>&1; then
|
|
106
106
|
rm "$SETTINGS_FILE.tmp"
|
|
107
|
-
echo "👌
|
|
107
|
+
echo "👌 pretooluse.check-permissions hook already bound"
|
|
108
108
|
echo " $SETTINGS_FILE"
|
|
109
109
|
exit 0
|
|
110
110
|
fi
|
|
@@ -112,7 +112,7 @@ fi
|
|
|
112
112
|
# Atomic replace
|
|
113
113
|
mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
|
|
114
114
|
|
|
115
|
-
echo "🔗
|
|
115
|
+
echo "🔗 pretooluse.check-permissions hook bound successfully!"
|
|
116
116
|
echo " $SETTINGS_FILE"
|
|
117
117
|
echo ""
|
|
118
118
|
echo "✨ Claude will now be reminded to check existing permissions before requesting new ones"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
######################################################################
|
|
3
|
-
# .what = bind forbid
|
|
3
|
+
# .what = bind pretooluse.forbid-stderr-redirect hook to Claude settings
|
|
4
4
|
#
|
|
5
5
|
# .why = when Claude uses 2>&1, error messages are hidden and
|
|
6
6
|
# debugging becomes harder. this hook blocks such commands.
|
|
@@ -21,7 +21,7 @@ set -euo pipefail
|
|
|
21
21
|
PROJECT_ROOT="$PWD"
|
|
22
22
|
SETTINGS_FILE="$PROJECT_ROOT/.claude/settings.local.json"
|
|
23
23
|
SKILLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
24
|
-
HOOK_SCRIPT="$SKILLS_DIR/claude.hooks/forbid
|
|
24
|
+
HOOK_SCRIPT="$SKILLS_DIR/claude.hooks/pretooluse.forbid-stderr-redirect.sh"
|
|
25
25
|
|
|
26
26
|
# Verify hook script exists
|
|
27
27
|
if [[ ! -f "$HOOK_SCRIPT" ]]; then
|
|
@@ -102,7 +102,7 @@ jq --argjson hook "$HOOK_CONFIG" '
|
|
|
102
102
|
# Check if any changes were made
|
|
103
103
|
if diff -q "$SETTINGS_FILE" "$SETTINGS_FILE.tmp" >/dev/null 2>&1; then
|
|
104
104
|
rm "$SETTINGS_FILE.tmp"
|
|
105
|
-
echo "👌 forbid
|
|
105
|
+
echo "👌 pretooluse.forbid-stderr-redirect hook already bound"
|
|
106
106
|
echo " $SETTINGS_FILE"
|
|
107
107
|
exit 0
|
|
108
108
|
fi
|
|
@@ -110,7 +110,7 @@ fi
|
|
|
110
110
|
# Atomic replace
|
|
111
111
|
mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
|
|
112
112
|
|
|
113
|
-
echo "🔗 forbid
|
|
113
|
+
echo "🔗 pretooluse.forbid-stderr-redirect hook bound successfully!"
|
|
114
114
|
echo " $SETTINGS_FILE"
|
|
115
115
|
echo ""
|
|
116
116
|
echo "✨ Claude will now be blocked from using 2>&1 in commands"
|
package/dist/logic/roles/mechanic/.skills/init.claude.hooks.sessionstart.notify-permissions.sh
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
######################################################################
|
|
3
|
+
# .what = bind sessionstart.notify-permissions hook to Claude settings
|
|
4
|
+
#
|
|
5
|
+
# .why = proactively informing Claude of pre-approved Bash commands
|
|
6
|
+
# at session start reduces interruptions from permission
|
|
7
|
+
# prompts by guiding it to use allowed patterns upfront.
|
|
8
|
+
#
|
|
9
|
+
# this script "findserts" (find-or-insert) the SessionStart
|
|
10
|
+
# hook into .claude/settings.local.json, ensuring:
|
|
11
|
+
# - the hook is present after running this skill
|
|
12
|
+
# - no duplication if already present
|
|
13
|
+
# - idempotent: safe to rerun
|
|
14
|
+
#
|
|
15
|
+
# .how = uses jq to merge the SessionStart hook configuration
|
|
16
|
+
# into the existing hooks structure, creating it if absent.
|
|
17
|
+
#
|
|
18
|
+
# guarantee:
|
|
19
|
+
# ✔ creates .claude/settings.local.json if missing
|
|
20
|
+
# ✔ preserves existing settings (permissions, other hooks)
|
|
21
|
+
# ✔ idempotent: no-op if hook already present
|
|
22
|
+
# ✔ fail-fast on errors
|
|
23
|
+
######################################################################
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
|
|
27
|
+
PROJECT_ROOT="$PWD"
|
|
28
|
+
SETTINGS_FILE="$PROJECT_ROOT/.claude/settings.local.json"
|
|
29
|
+
SKILLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
30
|
+
HOOK_SCRIPT="$SKILLS_DIR/claude.hooks/sessionstart.notify-permissions.sh"
|
|
31
|
+
|
|
32
|
+
# Verify hook script exists
|
|
33
|
+
if [[ ! -f "$HOOK_SCRIPT" ]]; then
|
|
34
|
+
echo "❌ hook script not found: $HOOK_SCRIPT" >&2
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Define the hook configuration to findsert
|
|
39
|
+
HOOK_CONFIG=$(cat <<EOF
|
|
40
|
+
{
|
|
41
|
+
"hooks": {
|
|
42
|
+
"SessionStart": [
|
|
43
|
+
{
|
|
44
|
+
"matcher": "*",
|
|
45
|
+
"hooks": [
|
|
46
|
+
{
|
|
47
|
+
"type": "command",
|
|
48
|
+
"command": "$HOOK_SCRIPT",
|
|
49
|
+
"timeout": 5
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
EOF
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Ensure .claude directory exists
|
|
60
|
+
mkdir -p "$(dirname "$SETTINGS_FILE")"
|
|
61
|
+
|
|
62
|
+
# Initialize settings file if it doesn't exist
|
|
63
|
+
if [[ ! -f "$SETTINGS_FILE" ]]; then
|
|
64
|
+
echo "{}" > "$SETTINGS_FILE"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Findsert: merge the hook configuration if not already present
|
|
68
|
+
jq --argjson hook "$HOOK_CONFIG" '
|
|
69
|
+
# Define the target command for comparison
|
|
70
|
+
def targetCmd: $hook.hooks.SessionStart[0].hooks[0].command;
|
|
71
|
+
|
|
72
|
+
# Check if hook already exists
|
|
73
|
+
def hookExists:
|
|
74
|
+
(.hooks.SessionStart // [])
|
|
75
|
+
| map(select(.matcher == "*") | .hooks // [])
|
|
76
|
+
| flatten
|
|
77
|
+
| map(.command)
|
|
78
|
+
| any(. == targetCmd);
|
|
79
|
+
|
|
80
|
+
# If hook already exists, return unchanged
|
|
81
|
+
if hookExists then
|
|
82
|
+
.
|
|
83
|
+
else
|
|
84
|
+
# Ensure .hooks exists
|
|
85
|
+
.hooks //= {} |
|
|
86
|
+
|
|
87
|
+
# Ensure .hooks.SessionStart exists
|
|
88
|
+
.hooks.SessionStart //= [] |
|
|
89
|
+
|
|
90
|
+
# Check if our matcher already exists
|
|
91
|
+
if (.hooks.SessionStart | map(.matcher) | index("*")) then
|
|
92
|
+
# Matcher exists, add our hook to its hooks array
|
|
93
|
+
.hooks.SessionStart |= map(
|
|
94
|
+
if .matcher == "*" then
|
|
95
|
+
.hooks += $hook.hooks.SessionStart[0].hooks
|
|
96
|
+
else
|
|
97
|
+
.
|
|
98
|
+
end
|
|
99
|
+
)
|
|
100
|
+
else
|
|
101
|
+
# Matcher does not exist, add the entire entry
|
|
102
|
+
.hooks.SessionStart += $hook.hooks.SessionStart
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
' "$SETTINGS_FILE" > "$SETTINGS_FILE.tmp"
|
|
106
|
+
|
|
107
|
+
# Check if any changes were made
|
|
108
|
+
if diff -q "$SETTINGS_FILE" "$SETTINGS_FILE.tmp" >/dev/null 2>&1; then
|
|
109
|
+
rm "$SETTINGS_FILE.tmp"
|
|
110
|
+
echo "👌 sessionstart.notify-permissions hook already bound"
|
|
111
|
+
echo " $SETTINGS_FILE"
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Atomic replace
|
|
116
|
+
mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
|
|
117
|
+
|
|
118
|
+
echo "🔗 sessionstart.notify-permissions hook bound successfully!"
|
|
119
|
+
echo " $SETTINGS_FILE"
|
|
120
|
+
echo ""
|
|
121
|
+
echo "✨ Claude will now see allowed permissions at the start of each session"
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
#
|
|
5
5
|
# .why = the mechanic role uses multiple hooks:
|
|
6
6
|
# • SessionStart: boot mechanic on every session
|
|
7
|
+
# • SessionStart: notify Claude of allowed permissions upfront
|
|
7
8
|
# • PreToolUse: check existing permissions before new requests
|
|
8
9
|
#
|
|
9
10
|
# this script dispatches to each hook initializer.
|
|
@@ -21,5 +22,6 @@ SKILLS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
21
22
|
|
|
22
23
|
# Dispatch to each hook initializer
|
|
23
24
|
"$SKILLS_DIR/init.claude.hooks.sessionstart.sh"
|
|
24
|
-
"$SKILLS_DIR/init.claude.hooks.
|
|
25
|
-
"$SKILLS_DIR/init.claude.hooks.pretooluse.sh"
|
|
25
|
+
"$SKILLS_DIR/init.claude.hooks.sessionstart.notify-permissions.sh"
|
|
26
|
+
"$SKILLS_DIR/init.claude.hooks.pretooluse.forbid-stderr-redirect.sh"
|
|
27
|
+
"$SKILLS_DIR/init.claude.hooks.pretooluse.check-permissions.sh"
|
|
@@ -41,7 +41,59 @@
|
|
|
41
41
|
// test runners - should use npm run test:* scripts instead
|
|
42
42
|
// direct invocation bypasses project test configuration
|
|
43
43
|
"Bash(npx biome:*)",
|
|
44
|
-
"Bash(npx jest:*)"
|
|
44
|
+
"Bash(npx jest:*)",
|
|
45
|
+
|
|
46
|
+
// github cli write operations - require explicit user approval
|
|
47
|
+
// pr mutations
|
|
48
|
+
"Bash(gh pr create:*)",
|
|
49
|
+
"Bash(gh pr merge:*)",
|
|
50
|
+
"Bash(gh pr close:*)",
|
|
51
|
+
"Bash(gh pr edit:*)",
|
|
52
|
+
"Bash(gh pr review:*)",
|
|
53
|
+
"Bash(gh pr comment:*)",
|
|
54
|
+
"Bash(gh pr reopen:*)",
|
|
55
|
+
// issue mutations
|
|
56
|
+
"Bash(gh issue create:*)",
|
|
57
|
+
"Bash(gh issue close:*)",
|
|
58
|
+
"Bash(gh issue edit:*)",
|
|
59
|
+
"Bash(gh issue comment:*)",
|
|
60
|
+
"Bash(gh issue reopen:*)",
|
|
61
|
+
// repo mutations
|
|
62
|
+
"Bash(gh repo create:*)",
|
|
63
|
+
"Bash(gh repo delete:*)",
|
|
64
|
+
"Bash(gh repo fork:*)",
|
|
65
|
+
"Bash(gh repo edit:*)",
|
|
66
|
+
// release mutations
|
|
67
|
+
"Bash(gh release create:*)",
|
|
68
|
+
"Bash(gh release delete:*)",
|
|
69
|
+
"Bash(gh release edit:*)",
|
|
70
|
+
// workflow/run mutations
|
|
71
|
+
"Bash(gh run cancel:*)",
|
|
72
|
+
"Bash(gh run rerun:*)",
|
|
73
|
+
"Bash(gh workflow disable:*)",
|
|
74
|
+
"Bash(gh workflow enable:*)",
|
|
75
|
+
"Bash(gh workflow run:*)",
|
|
76
|
+
// gist mutations
|
|
77
|
+
"Bash(gh gist create:*)",
|
|
78
|
+
"Bash(gh gist delete:*)",
|
|
79
|
+
"Bash(gh gist edit:*)",
|
|
80
|
+
// label mutations
|
|
81
|
+
"Bash(gh label create:*)",
|
|
82
|
+
"Bash(gh label delete:*)",
|
|
83
|
+
"Bash(gh label edit:*)",
|
|
84
|
+
// project mutations
|
|
85
|
+
"Bash(gh project create:*)",
|
|
86
|
+
"Bash(gh project delete:*)",
|
|
87
|
+
"Bash(gh project edit:*)",
|
|
88
|
+
// api write methods - can mutate anything
|
|
89
|
+
"Bash(gh api -X POST:*)",
|
|
90
|
+
"Bash(gh api -X PUT:*)",
|
|
91
|
+
"Bash(gh api -X PATCH:*)",
|
|
92
|
+
"Bash(gh api -X DELETE:*)",
|
|
93
|
+
"Bash(gh api --method POST:*)",
|
|
94
|
+
"Bash(gh api --method PUT:*)",
|
|
95
|
+
"Bash(gh api --method PATCH:*)",
|
|
96
|
+
"Bash(gh api --method DELETE:*)"
|
|
45
97
|
],
|
|
46
98
|
|
|
47
99
|
// commands that require explicit user approval each time
|
|
@@ -83,8 +135,9 @@
|
|
|
83
135
|
"Bash(mkdir:*)",
|
|
84
136
|
"Bash(pwd)",
|
|
85
137
|
|
|
86
|
-
// safe
|
|
87
|
-
"Bash(
|
|
138
|
+
// git mv/rm are safe - constrained to repo, all changes revertable
|
|
139
|
+
"Bash(git mv:*)",
|
|
140
|
+
"Bash(git rm:*)",
|
|
88
141
|
|
|
89
142
|
// npm read operations
|
|
90
143
|
"Bash(npm view:*)",
|
|
@@ -126,8 +179,37 @@
|
|
|
126
179
|
"Bash(npm run fix:lint:*)",
|
|
127
180
|
|
|
128
181
|
// github cli read operations
|
|
129
|
-
|
|
182
|
+
// pr reads
|
|
183
|
+
"Bash(gh pr list:*)",
|
|
184
|
+
"Bash(gh pr view:*)",
|
|
130
185
|
"Bash(gh pr status:*)",
|
|
186
|
+
"Bash(gh pr checks:*)",
|
|
187
|
+
"Bash(gh pr diff:*)",
|
|
188
|
+
// issue reads
|
|
189
|
+
"Bash(gh issue list:*)",
|
|
190
|
+
"Bash(gh issue view:*)",
|
|
191
|
+
"Bash(gh issue status:*)",
|
|
192
|
+
// repo reads
|
|
193
|
+
"Bash(gh repo list:*)",
|
|
194
|
+
"Bash(gh repo view:*)",
|
|
195
|
+
// run/workflow reads
|
|
196
|
+
"Bash(gh run list:*)",
|
|
197
|
+
"Bash(gh run view:*)",
|
|
198
|
+
"Bash(gh run watch:*)",
|
|
199
|
+
"Bash(gh workflow list:*)",
|
|
200
|
+
"Bash(gh workflow view:*)",
|
|
201
|
+
// release reads
|
|
202
|
+
"Bash(gh release list:*)",
|
|
203
|
+
"Bash(gh release view:*)",
|
|
204
|
+
// search (all read-only)
|
|
205
|
+
"Bash(gh search code:*)",
|
|
206
|
+
"Bash(gh search repos:*)",
|
|
207
|
+
"Bash(gh search issues:*)",
|
|
208
|
+
"Bash(gh search prs:*)",
|
|
209
|
+
"Bash(gh search commits:*)",
|
|
210
|
+
// api read operations (explicit GET for safe suffix matching)
|
|
211
|
+
"Bash(gh api -X GET:*)",
|
|
212
|
+
"Bash(gh api --method GET:*)",
|
|
131
213
|
|
|
132
214
|
// skill sourcing
|
|
133
215
|
"Bash(source .agent/repo=.this/skills/*)"
|
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.
|
|
5
|
+
"version": "1.15.0",
|
|
6
6
|
"repository": "ehmpathy/rhachet-roles-ehmpathy",
|
|
7
7
|
"homepage": "https://github.com/ehmpathy/rhachet-roles-ehmpathy",
|
|
8
8
|
"keywords": [
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
######################################################################
|
|
3
|
-
# .what = safe mv wrapper that constrains moves to within the repo
|
|
4
|
-
#
|
|
5
|
-
# .why = mv can move/overwrite files anywhere on the filesystem.
|
|
6
|
-
# this wrapper ensures both source and destination resolve
|
|
7
|
-
# to paths within the current working directory (repo root).
|
|
8
|
-
#
|
|
9
|
-
# .how = uses realpath to resolve absolute paths, then validates
|
|
10
|
-
# both are prefixed by $PWD before executing mv.
|
|
11
|
-
#
|
|
12
|
-
# usage:
|
|
13
|
-
# bash mvsafe.sh <source> <destination>
|
|
14
|
-
#
|
|
15
|
-
# guarantee:
|
|
16
|
-
# ✔ fails if source is outside repo
|
|
17
|
-
# ✔ fails if destination is outside repo
|
|
18
|
-
# ✔ fails if source doesn't exist
|
|
19
|
-
# ✔ passes all arguments to mv if validation passes
|
|
20
|
-
######################################################################
|
|
21
|
-
|
|
22
|
-
set -euo pipefail
|
|
23
|
-
|
|
24
|
-
if [[ $# -lt 2 ]]; then
|
|
25
|
-
echo "error: mvsafe requires at least 2 arguments" >&2
|
|
26
|
-
echo "usage: mvsafe.sh <source> <destination>" >&2
|
|
27
|
-
exit 1
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
REPO_ROOT="$PWD"
|
|
31
|
-
|
|
32
|
-
# get the last argument (destination)
|
|
33
|
-
DEST="${*: -1}"
|
|
34
|
-
|
|
35
|
-
# get all arguments except the last (sources, could be multiple)
|
|
36
|
-
SOURCES=("${@:1:$#-1}")
|
|
37
|
-
|
|
38
|
-
# resolve destination path
|
|
39
|
-
# if dest doesn't exist yet, resolve its parent directory
|
|
40
|
-
if [[ -e "$DEST" ]]; then
|
|
41
|
-
DEST_RESOLVED="$(realpath "$DEST")"
|
|
42
|
-
else
|
|
43
|
-
DEST_PARENT="$(dirname "$DEST")"
|
|
44
|
-
if [[ ! -d "$DEST_PARENT" ]]; then
|
|
45
|
-
echo "error: destination parent directory does not exist: $DEST_PARENT" >&2
|
|
46
|
-
exit 1
|
|
47
|
-
fi
|
|
48
|
-
DEST_RESOLVED="$(realpath "$DEST_PARENT")/$(basename "$DEST")"
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
# validate destination is within repo
|
|
52
|
-
if [[ "$DEST_RESOLVED" != "$REPO_ROOT"* ]]; then
|
|
53
|
-
echo "error: destination is outside repo: $DEST_RESOLVED" >&2
|
|
54
|
-
echo " repo root: $REPO_ROOT" >&2
|
|
55
|
-
exit 1
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
# validate each source is within repo
|
|
59
|
-
for SRC in "${SOURCES[@]}"; do
|
|
60
|
-
if [[ ! -e "$SRC" ]]; then
|
|
61
|
-
echo "error: source does not exist: $SRC" >&2
|
|
62
|
-
exit 1
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
SRC_RESOLVED="$(realpath "$SRC")"
|
|
66
|
-
|
|
67
|
-
if [[ "$SRC_RESOLVED" != "$REPO_ROOT"* ]]; then
|
|
68
|
-
echo "error: source is outside repo: $SRC_RESOLVED" >&2
|
|
69
|
-
echo " repo root: $REPO_ROOT" >&2
|
|
70
|
-
exit 1
|
|
71
|
-
fi
|
|
72
|
-
done
|
|
73
|
-
|
|
74
|
-
# all validations passed, execute mv
|
|
75
|
-
exec mv "$@"
|
|
File without changes
|