Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion lib/skills.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# Skills: agent skill installation from git repos
# Skills: agent skill installation from git repos and the wp-coding-agents repo itself

# Install agent skills from a git repo.
# Clones the repo, copies directories containing SKILL.md to the target.
Expand All @@ -19,6 +19,7 @@ install_skills_from_repo() {
local skill_name
skill_name=$(basename "$skill_dir")
if [ -f "$skill_dir/SKILL.md" ]; then
rm -rf "$SKILLS_DIR/$skill_name"
cp -r "$skill_dir" "$SKILLS_DIR/$skill_name"
log " Installed skill: $skill_name"
fi
Expand All @@ -31,13 +32,47 @@ install_skills_from_repo() {
fi
}

# Install skills shipped in this repo ($SCRIPT_DIR/skills/).
# These are the skills that ship with wp-coding-agents itself — e.g.
# upgrade-wp-coding-agents and wp-coding-agents-setup — so every install
# can run them without a manual copy step.
install_skills_from_local_repo() {
local src_dir="$SCRIPT_DIR/skills"
[ -d "$src_dir" ] || return

if [ "$DRY_RUN" = true ]; then
for skill_dir in "$src_dir"/*/; do
[ -f "$skill_dir/SKILL.md" ] || continue
echo -e "${BLUE}[dry-run]${NC} Would install in-repo skill: $(basename "$skill_dir") → $SKILLS_DIR/"
done
return
fi

local copied=0
for skill_dir in "$src_dir"/*/; do
local skill_name
skill_name=$(basename "$skill_dir")
if [ -f "$skill_dir/SKILL.md" ]; then
rm -rf "$SKILLS_DIR/$skill_name"
cp -r "$skill_dir" "$SKILLS_DIR/$skill_name"
log " Installed skill: $skill_name"
copied=$((copied + 1))
fi
done
if [ "$copied" -gt 0 ]; then
log "wp-coding-agents in-repo skills installed ($copied)"
fi
}

install_skills() {
SKILLS_DIR="$(runtime_skills_dir)"

if [ "$INSTALL_SKILLS" = true ]; then
log "Phase 8.5: Installing agent skills..."
run_cmd mkdir -p "$SKILLS_DIR"

install_skills_from_local_repo

install_skills_from_repo "https://github.com/WordPress/agent-skills.git" "WordPress agent skills"

if [ "$INSTALL_DATA_MACHINE" = true ]; then
Expand Down
31 changes: 29 additions & 2 deletions runtimes/claude-code.sh
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,33 @@ runtime_install_hooks() {
chmod +x "$hook_dst"
log "Installed hook: $hook_dst"

# Merge SessionStart hook, workspace permissions, and disable auto-memory in settings.json
# Merge SessionStart hook, workspace permissions, and disable auto-memory in settings.json.
# additionalDirectories alone is not enough: the Bash tool is gated by explicit
# allow rules, so workspace shell ops (ls/git/studio wp datamachine-code …) would
# still prompt. Expand permissions.allow with Read/Edit/Write globs on the
# workspace plus the datamachine-code Bash surface.
local hook_cmd="\"\$CLAUDE_PROJECT_DIR\"/.claude/hooks/dm-agent-sync.sh"
local hook_entry
hook_entry=$(jq -n --arg cmd "$hook_cmd" '{matcher: "", hooks: [{type: "command", command: $cmd}]}')

local wp_prefix="wp"
if [ "$IS_STUDIO" = true ]; then
wp_prefix="studio wp"
fi

local workspace_allow_rules
workspace_allow_rules=$(jq -n \
--arg ws "$DM_WORKSPACE_DIR" \
--arg wp "$wp_prefix" \
'[
"Read(\($ws)/**)",
"Edit(\($ws)/**)",
"Write(\($ws)/**)",
"Bash(\($wp) datamachine-code workspace:*)",
"Bash(\($wp) datamachine-code github:*)",
"Bash(\($wp) datamachine-code gitsync:*)"
]')

local settings='{}'
if [ -f "$settings_file" ]; then
settings=$(cat "$settings_file")
Expand All @@ -190,6 +212,7 @@ runtime_install_hooks() {
--arg workspace "$DM_WORKSPACE_DIR" \
--argjson hook "$hook_entry" \
--arg cmd "$hook_cmd" \
--argjson allow_rules "$workspace_allow_rules" \
'
.autoMemoryEnabled = false

Expand All @@ -198,6 +221,10 @@ runtime_install_hooks() {
| if any(. == $workspace) then . else . + [$workspace] end
)

| .permissions.allow = (
((.permissions.allow // []) + $allow_rules) | unique
)

| .hooks.SessionStart = (
(.hooks.SessionStart // [])
| if any(.hooks[]?.command? == $cmd) then . else . + [$hook] end
Expand All @@ -206,7 +233,7 @@ runtime_install_hooks() {

echo "$settings" | jq . > "$settings_file"

log "Configured settings.json: SessionStart hook, workspace permissions, autoMemoryEnabled=false"
log "Configured settings.json: SessionStart hook, workspace permissions (dir + allow rules), autoMemoryEnabled=false"
}

runtime_generate_instructions() {
Expand Down
29 changes: 26 additions & 3 deletions runtimes/studio-code.sh
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,24 @@ runtime_install_hooks() {
chmod +x "$hook_dst"
log "Installed hook: $hook_dst"

# Merge SessionStart hook, workspace permissions, and disable auto-memory in settings.json
# Merge SessionStart hook, workspace permissions, and disable auto-memory in settings.json.
# additionalDirectories alone is not enough: the Bash tool is gated by explicit
# allow rules, so workspace shell ops (ls/git/studio wp datamachine-code …) would
# still prompt. Expand permissions.allow with Read/Edit/Write globs on the
# workspace plus the datamachine-code Bash surface.
local hook_cmd="\"\$CLAUDE_PROJECT_DIR\"/.claude/hooks/dm-agent-sync.sh"
local wp_prefix="wp"
if [ "$IS_STUDIO" = true ]; then
wp_prefix="studio wp"
fi

python3 -c "
import json, sys, os

settings_path = sys.argv[1]
hook_cmd = sys.argv[2]
workspace_dir = sys.argv[3]
wp_prefix = sys.argv[4]

settings = {}
if os.path.isfile(settings_path):
Expand All @@ -262,6 +271,20 @@ additional_dirs = perms.setdefault('additionalDirectories', [])
if workspace_dir not in additional_dirs:
additional_dirs.append(workspace_dir)

# Allow rules so the Bash tool + file tools don't prompt for workspace work.
allow = perms.setdefault('allow', [])
desired_rules = [
f'Read({workspace_dir}/**)',
f'Edit({workspace_dir}/**)',
f'Write({workspace_dir}/**)',
f'Bash({wp_prefix} datamachine-code workspace:*)',
f'Bash({wp_prefix} datamachine-code github:*)',
f'Bash({wp_prefix} datamachine-code gitsync:*)',
]
for rule in desired_rules:
if rule not in allow:
allow.append(rule)

# Register SessionStart hook (idempotent)
hooks = settings.setdefault('hooks', {})
session_hooks = hooks.setdefault('SessionStart', [])
Expand All @@ -280,9 +303,9 @@ if not already_registered:
with open(settings_path, 'w') as f:
json.dump(settings, f, indent=2)
f.write('\n')
" "$settings_file" "$hook_cmd" "$DM_WORKSPACE_DIR"
" "$settings_file" "$hook_cmd" "$DM_WORKSPACE_DIR" "$wp_prefix"

log "Configured settings.json: SessionStart hook, workspace permissions, autoMemoryEnabled=false"
log "Configured settings.json: SessionStart hook, workspace permissions (dir + allow rules), autoMemoryEnabled=false"
}

runtime_generate_instructions() {
Expand Down
Loading