#!/usr/bin/env bash # Shared AI ignore / permission defaults for init and sync flows. AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN="agent-task/archive/**" AGENT_OPS_ROADMAP_ARCHIVE_IGNORE_PATTERN="agent-ops/roadmap/archive/**" AGENT_OPS_AI_IGNORE_BLOCK_BEGIN="# BEGIN Agent-Ops managed ignore" AGENT_OPS_AI_IGNORE_BLOCK_END="# END Agent-Ops managed ignore" AGENT_OPS_AI_IGNORE_FILES=(".geminiignore" ".aiexclude" ".cursorignore" ".clineignore") AGENT_OPS_AI_PERMISSION_FILES=(".claude/settings.json" "opencode.json") AGENT_OPS_AI_SYNC_FILES=("${AGENT_OPS_AI_IGNORE_FILES[@]}" "${AGENT_OPS_AI_PERMISSION_FILES[@]}") agent_ops_ensure_ai_ignore_block() { local file="$1" local tmp touch "$file" if grep -qxF "$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" "$file" \ && grep -qxF "$AGENT_OPS_AI_IGNORE_BLOCK_END" "$file"; then tmp="$(mktemp "$file.tmp.XXXXXX")" awk \ -v begin="$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" \ -v end="$AGENT_OPS_AI_IGNORE_BLOCK_END" \ -v task="$AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN" \ -v roadmap="$AGENT_OPS_ROADMAP_ARCHIVE_IGNORE_PATTERN" ' $0 == begin { print begin print task print roadmap print end in_block = 1 next } $0 == end && in_block { in_block = 0 next } !in_block { print } ' "$file" > "$tmp" mv "$tmp" "$file" else if [[ -s "$file" ]]; then printf "\n" >> "$file" fi printf "%s\n" "$AGENT_OPS_AI_IGNORE_BLOCK_BEGIN" >> "$file" printf "%s\n" "$AGENT_OPS_TASK_ARCHIVE_IGNORE_PATTERN" >> "$file" printf "%s\n" "$AGENT_OPS_ROADMAP_ARCHIVE_IGNORE_PATTERN" >> "$file" printf "%s\n" "$AGENT_OPS_AI_IGNORE_BLOCK_END" >> "$file" fi } agent_ops_merge_json_with_jq() { local file="$1" local filter="$2" local fallback_note="$3" local tmp if ! command -v jq >/dev/null 2>&1; then echo "$fallback_note" return fi tmp="$(mktemp "$file.tmp.XXXXXX")" if jq "$filter" "$file" > "$tmp"; then mv "$tmp" "$file" else rm -f "$tmp" echo "$fallback_note" fi } agent_ops_merge_claude_settings() { local file="$1" local filter=' def append_unique($item): if index($item) then . else . + [$item] end; def as_array: if type == "array" then . elif . == null then [] else [.] end; def as_object: if type == "object" then . else {} end; .permissions = ((.permissions // {}) | as_object) | .permissions.deny = ( (.permissions.deny | as_array) | map(select(. != "Read(./agent-ops/roadmap/archive/**)")) | append_unique("Read(./agent-task/archive/**)") ) ' agent_ops_merge_json_with_jq \ "$file" \ "$filter" \ "Note: .claude/settings.json exists; add Read(./agent-task/archive/**) and remove Read(./agent-ops/roadmap/archive/**) manually." } agent_ops_claude_settings_complete() { local file="$1" local filter=' def as_array: if type == "array" then . elif . == null then [] else [.] end; (.permissions.deny | as_array) as $deny | (($deny | index("Read(./agent-task/archive/**)")) != null) and (($deny | index("Read(./agent-ops/roadmap/archive/**)")) == null) ' if command -v jq >/dev/null 2>&1; then jq -e "$filter" "$file" >/dev/null 2>&1 else grep -q "agent-task/archive" "$file" fi } agent_ops_merge_opencode_config() { local file="$1" local filter=' def append_unique($item): if index($item) then . else . + [$item] end; def as_array: if type == "array" then . elif . == null then [] else [.] end; def as_object: if type == "object" then . else {} end; .permission = ((.permission // {}) | as_object) | .permission.read = ((.permission.read // {}) | as_object) | .permission.read["agent-task/archive/**"] = "deny" | del(.permission.read["agent-ops/roadmap/archive/**"]) | .permission.glob = ((.permission.glob // {}) | as_object) | .permission.glob["agent-task/archive/**"] = "deny" | del(.permission.glob["agent-ops/roadmap/archive/**"]) | .watcher = ((.watcher // {}) | as_object) | .watcher.ignore = ( (.watcher.ignore | as_array) | append_unique("agent-task/archive/**") | append_unique("agent-ops/roadmap/archive/**") ) ' agent_ops_merge_json_with_jq \ "$file" \ "$filter" \ "Note: opencode.json exists; add agent-task/archive/** read/glob deny, remove agent-ops/roadmap/archive/** read/glob hard deny, and add agent-task/archive/** plus agent-ops/roadmap/archive/** watcher ignore manually." } agent_ops_opencode_config_complete() { local file="$1" local filter=' def as_array: if type == "array" then . elif . == null then [] else [.] end; (.permission.read["agent-task/archive/**"] == "deny") and (.permission.glob["agent-task/archive/**"] == "deny") and (.permission.read["agent-ops/roadmap/archive/**"] != "deny") and (.permission.glob["agent-ops/roadmap/archive/**"] != "deny") and ((.watcher.ignore | as_array) as $ignore | (($ignore | index("agent-task/archive/**")) != null) and (($ignore | index("agent-ops/roadmap/archive/**")) != null)) ' if command -v jq >/dev/null 2>&1; then jq -e "$filter" "$file" >/dev/null 2>&1 else grep -q "agent-task/archive" "$file" && grep -q "agent-ops/roadmap/archive" "$file" fi } ensure_agent_ops_ai_ignore_config() { local target_dir="$1" local ignore_file if [[ -f "$target_dir/.agent-ops-source" ]]; then echo " AI ignore 보강 건너뜀: .agent-ops-source repo" return fi for ignore_file in "${AGENT_OPS_AI_IGNORE_FILES[@]}"; do agent_ops_ensure_ai_ignore_block "$target_dir/$ignore_file" done mkdir -p "$target_dir/.claude" if [[ ! -f "$target_dir/.claude/settings.json" ]]; then cat > "$target_dir/.claude/settings.json" <<'EOF' { "$schema": "https://json.schemastore.org/claude-code-settings.json", "permissions": { "deny": [ "Read(./agent-task/archive/**)" ] } } EOF elif ! agent_ops_claude_settings_complete "$target_dir/.claude/settings.json"; then agent_ops_merge_claude_settings "$target_dir/.claude/settings.json" fi if [[ ! -f "$target_dir/opencode.json" ]]; then cat > "$target_dir/opencode.json" <<'EOF' { "$schema": "https://opencode.ai/config.json", "permission": { "read": { "agent-task/archive/**": "deny" }, "glob": { "agent-task/archive/**": "deny" } }, "watcher": { "ignore": [ "agent-task/archive/**", "agent-ops/roadmap/archive/**" ] } } EOF elif ! agent_ops_opencode_config_complete "$target_dir/opencode.json"; then agent_ops_merge_opencode_config "$target_dir/opencode.json" fi echo " AI permission 표준 적용: roadmap archive hard deny 제거" }