chore: auto-commit (799 archivos)
- .claude/CLAUDE.md - .claude/commands/subagentes.md - .claude/rules/INDEX.md - .mcp.json - bash/functions/cybersecurity/analyze_dns.md - bash/functions/cybersecurity/audit_http_headers.md - bash/functions/cybersecurity/audit_ssh_config.md - bash/functions/cybersecurity/check_firewall.md - bash/functions/cybersecurity/detect_suspicious_users.md - bash/functions/cybersecurity/encrypt_file.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+234
@@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env bash
|
||||
# PostToolUse hook: registra cada invocacion del agente en
|
||||
# projects/fn_monitoring/apps/call_monitor/operations.db (issue 0085b).
|
||||
#
|
||||
# Identifica tool, extrae function_id cuando es posible, clasifica el patron
|
||||
# (mcp_*, fn_cli_run, heredoc_py, sqlite_direct, edit_registry, ...) y
|
||||
# detecta antipatrones para registrar violations.
|
||||
#
|
||||
# NUNCA bloquea la herramienta. Falla silenciosamente si la BD no esta lista.
|
||||
# Solo guarda args_hash, jamas valores concretos.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---- Resolve registry root (walks up from cwd looking for registry.db) ----
|
||||
resolve_root() {
|
||||
local d="${PWD}"
|
||||
while [ "$d" != "/" ]; do
|
||||
if [ -f "$d/registry.db" ]; then
|
||||
printf '%s' "$d"
|
||||
return 0
|
||||
fi
|
||||
d=$(dirname "$d")
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
ROOT=$(resolve_root) || exit 0
|
||||
DB="$ROOT/projects/fn_monitoring/apps/call_monitor/operations.db"
|
||||
|
||||
# Si la BD aun no existe, el hook no hace nada (esperando init).
|
||||
[ -f "$DB" ] || exit 0
|
||||
|
||||
# ---- Read stdin JSON ----
|
||||
INPUT=$(cat)
|
||||
if [ -z "$INPUT" ]; then exit 0; fi
|
||||
|
||||
# Required jq presence
|
||||
command -v jq >/dev/null 2>&1 || exit 0
|
||||
command -v sqlite3 >/dev/null 2>&1 || exit 0
|
||||
|
||||
TOOL_NAME=$(printf '%s' "$INPUT" | jq -r '.tool_name // ""')
|
||||
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""')
|
||||
TS=$(date -u +%s)
|
||||
|
||||
# Tool response success/error
|
||||
SUCCESS=1
|
||||
ERROR_CLASS=""
|
||||
ERROR_SNIPPET=""
|
||||
RESP_IS_ERROR=$(printf '%s' "$INPUT" | jq -r 'if (.tool_response | type) == "object" then (.tool_response.is_error // false) else false end')
|
||||
if [ "$RESP_IS_ERROR" = "true" ]; then
|
||||
SUCCESS=0
|
||||
ERROR_SNIPPET=$(printf '%s' "$INPUT" | jq -r 'if (.tool_response | type) == "object" then (.tool_response.error // .tool_response.content // "") else "" end' | head -c 240 | tr '\n' ' ')
|
||||
fi
|
||||
|
||||
# args_hash: sha256 truncado del tool_input (sin valores)
|
||||
ARGS_HASH=$(printf '%s' "$INPUT" | jq -c '.tool_input // {}' | sha256sum | cut -c1-16)
|
||||
|
||||
# Helpers SQL
|
||||
sql_escape() { printf '%s' "$1" | sed "s/'/''/g"; }
|
||||
|
||||
insert_call() {
|
||||
local fn_id="$1" tool_used="$2" duration_ms="${3:-0}"
|
||||
local fn_esc tu_esc ec_esc es_esc sid_esc ah_esc
|
||||
fn_esc=$(sql_escape "$fn_id")
|
||||
tu_esc=$(sql_escape "$tool_used")
|
||||
ec_esc=$(sql_escape "$ERROR_CLASS")
|
||||
es_esc=$(sql_escape "$ERROR_SNIPPET")
|
||||
sid_esc=$(sql_escape "$SESSION_ID")
|
||||
ah_esc=$(sql_escape "$ARGS_HASH")
|
||||
sqlite3 "$DB" "INSERT INTO calls (session_id, function_id, tool_used, args_hash, duration_ms, success, error_class, error_snippet, ts) VALUES ('$sid_esc','$fn_esc','$tu_esc','$ah_esc',$duration_ms,$SUCCESS,'$ec_esc','$es_esc',$TS);" 2>/dev/null || true
|
||||
}
|
||||
|
||||
insert_code_write() {
|
||||
local fn_id="$1" file_path="$2" added="${3:-0}" removed="${4:-0}"
|
||||
local fn_esc fp_esc sid_esc
|
||||
fn_esc=$(sql_escape "$fn_id")
|
||||
fp_esc=$(sql_escape "$file_path")
|
||||
sid_esc=$(sql_escape "$SESSION_ID")
|
||||
sqlite3 "$DB" "INSERT INTO code_writes (session_id, function_id, file_path, lines_added, lines_removed, ts) VALUES ('$sid_esc','$fn_esc','$fp_esc',$added,$removed,$TS);" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Snapshot a function version row when an edit lands on a registry file.
|
||||
# Uses sha256 of file bytes as content_hash (separate namespace from index source).
|
||||
insert_edit_version() {
|
||||
local fn_id="$1" abs_path="$2"
|
||||
[ -f "$abs_path" ] || return 0
|
||||
command -v sha256sum >/dev/null 2>&1 || return 0
|
||||
local hash
|
||||
hash=$(sha256sum "$abs_path" 2>/dev/null | awk '{print $1}')
|
||||
[ -z "$hash" ] && return 0
|
||||
local fn_esc h_esc
|
||||
fn_esc=$(sql_escape "$fn_id")
|
||||
h_esc=$(sql_escape "$hash")
|
||||
sqlite3 "$DB" "INSERT OR IGNORE INTO function_versions (function_id, content_hash, version, snapped_at, source, lines_added, lines_removed) VALUES ('$fn_esc','$h_esc','',$TS,'edit_hook',0,0);" 2>/dev/null || true
|
||||
}
|
||||
|
||||
insert_violation() {
|
||||
local rule_id="$1" fn_id="$2" snippet="$3" severity="${4:-warning}"
|
||||
local r_esc fn_esc sn_esc sev_esc sid_esc
|
||||
r_esc=$(sql_escape "$rule_id")
|
||||
fn_esc=$(sql_escape "$fn_id")
|
||||
sn_esc=$(sql_escape "$(printf '%s' "$snippet" | head -c 240 | tr '\n' ' ')")
|
||||
sev_esc=$(sql_escape "$severity")
|
||||
sid_esc=$(sql_escape "$SESSION_ID")
|
||||
sqlite3 "$DB" "INSERT INTO violations (session_id, rule_id, function_id, command_snippet, severity, ts) VALUES ('$sid_esc','$r_esc','$fn_esc','$sn_esc','$sev_esc',$TS);" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# ---- Derive function_id from registry file path ----
|
||||
# Matches paths under functions/<domain>/<name>.<ext>, python/functions/<domain>/<name>.py,
|
||||
# bash/functions/<domain>/<name>.sh, frontend/functions/<domain>/<name>.ts(x)
|
||||
derive_fn_id_from_path() {
|
||||
local p="$1"
|
||||
[ -z "$p" ] && return 1
|
||||
case "$p" in
|
||||
functions/*/*.go|*/functions/*/*.go)
|
||||
local dom name
|
||||
dom=$(printf '%s' "$p" | sed -E 's|.*functions/([^/]+)/.*|\1|')
|
||||
name=$(printf '%s' "$p" | sed -E 's|.*functions/[^/]+/([^/.]+)\..*|\1|')
|
||||
[ -n "$dom" ] && [ -n "$name" ] && printf '%s_go_%s' "$name" "$dom" && return 0 ;;
|
||||
python/functions/*/*.py)
|
||||
local dom name
|
||||
dom=$(printf '%s' "$p" | sed -E 's|python/functions/([^/]+)/.*|\1|')
|
||||
name=$(printf '%s' "$p" | sed -E 's|python/functions/[^/]+/([^/.]+)\..*|\1|')
|
||||
[ -n "$dom" ] && [ -n "$name" ] && printf '%s_py_%s' "$name" "$dom" && return 0 ;;
|
||||
bash/functions/*/*.sh)
|
||||
local dom name
|
||||
dom=$(printf '%s' "$p" | sed -E 's|bash/functions/([^/]+)/.*|\1|')
|
||||
name=$(printf '%s' "$p" | sed -E 's|bash/functions/[^/]+/([^/.]+)\..*|\1|')
|
||||
[ -n "$dom" ] && [ -n "$name" ] && printf '%s_bash_%s' "$name" "$dom" && return 0 ;;
|
||||
frontend/functions/*/*.ts|frontend/functions/*/*.tsx)
|
||||
local dom name
|
||||
dom=$(printf '%s' "$p" | sed -E 's|frontend/functions/([^/]+)/.*|\1|')
|
||||
name=$(printf '%s' "$p" | sed -E 's|frontend/functions/[^/]+/([^/.]+)\..*|\1|')
|
||||
[ -n "$dom" ] && [ -n "$name" ] && printf '%s_ts_%s' "$name" "$dom" && return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---- Dispatch by tool ----
|
||||
case "$TOOL_NAME" in
|
||||
mcp__registry__fn_search)
|
||||
insert_call "" "mcp_fn_search"
|
||||
;;
|
||||
mcp__registry__fn_show)
|
||||
ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""')
|
||||
insert_call "$ID" "mcp_fn_show"
|
||||
;;
|
||||
mcp__registry__fn_code)
|
||||
ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""')
|
||||
insert_call "$ID" "mcp_fn_code"
|
||||
;;
|
||||
mcp__registry__fn_uses)
|
||||
ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""')
|
||||
insert_call "$ID" "mcp_fn_uses"
|
||||
;;
|
||||
mcp__registry__fn_run)
|
||||
ID=$(printf '%s' "$INPUT" | jq -r '.tool_input.id // ""')
|
||||
insert_call "$ID" "mcp_fn_run"
|
||||
;;
|
||||
mcp__registry__fn_list_domains)
|
||||
insert_call "" "mcp_fn_list_domains"
|
||||
;;
|
||||
mcp__registry__fn_proposal)
|
||||
insert_call "" "mcp_fn_proposal"
|
||||
;;
|
||||
mcp__registry__fn_doctor)
|
||||
insert_call "" "mcp_fn_doctor"
|
||||
;;
|
||||
mcp__registry__fn_create_function)
|
||||
insert_call "" "mcp_fn_create_function"
|
||||
;;
|
||||
|
||||
Edit|Write|MultiEdit)
|
||||
FILE_PATH=$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // ""')
|
||||
ABS_PATH="$FILE_PATH"
|
||||
# Make path relative to root if absolute and inside root
|
||||
case "$FILE_PATH" in
|
||||
"$ROOT"/*) FILE_PATH="${FILE_PATH#$ROOT/}" ;;
|
||||
/*) ABS_PATH="$FILE_PATH" ;;
|
||||
*) ABS_PATH="$ROOT/$FILE_PATH" ;;
|
||||
esac
|
||||
FN_ID=$(derive_fn_id_from_path "$FILE_PATH" || true)
|
||||
if [ -n "$FN_ID" ]; then
|
||||
insert_code_write "$FN_ID" "$FILE_PATH" 0 0
|
||||
insert_call "$FN_ID" "edit_registry"
|
||||
insert_edit_version "$FN_ID" "$ABS_PATH"
|
||||
fi
|
||||
;;
|
||||
|
||||
Bash)
|
||||
CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // ""')
|
||||
CMD_HEAD=$(printf '%s' "$CMD" | head -c 200 | tr '\n' ' ')
|
||||
|
||||
# Classify
|
||||
TOOL_USED="bash_other"
|
||||
FN_ID=""
|
||||
|
||||
if printf '%s' "$CMD" | grep -qE '(^|[[:space:]])\./fn[[:space:]]+run[[:space:]]+'; then
|
||||
TOOL_USED="fn_cli_run"
|
||||
FN_ID=$(printf '%s' "$CMD" | sed -nE 's/.*\.\/fn[[:space:]]+run[[:space:]]+([A-Za-z0-9_]+).*/\1/p' | head -n1)
|
||||
elif printf '%s' "$CMD" | grep -qE 'python/\.venv/bin/python3[[:space:]]+-[[:space:]]+<<'; then
|
||||
TOOL_USED="heredoc_py"
|
||||
elif printf '%s' "$CMD" | grep -qE 'sqlite3[[:space:]][^|]*\bregistry\.db\b'; then
|
||||
TOOL_USED="sqlite_direct"
|
||||
fi
|
||||
|
||||
insert_call "$FN_ID" "$TOOL_USED"
|
||||
|
||||
# ---- Violation rules ----
|
||||
# 1. sqlite3 directo SELECT sobre registry.db (excepto schema/pragma/count/join)
|
||||
if [ "$TOOL_USED" = "sqlite_direct" ]; then
|
||||
if ! printf '%s' "$CMD" | grep -qiE '(\.schema|\.tables|PRAGMA[[:space:]]+(table_info|index_list)|COUNT\(|GROUP[[:space:]]+BY|JOIN[[:space:]])'; then
|
||||
insert_violation "sqlite3_registry_select" "" "$CMD_HEAD" "warning"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2. python -c "import X; dir(X)"
|
||||
if printf '%s' "$CMD" | grep -qE 'python[3]?[[:space:]]+-c[[:space:]]+["'\''].*import.*(dir|help)\('; then
|
||||
insert_violation "python_dir_inspect" "" "$CMD_HEAD" "info"
|
||||
fi
|
||||
|
||||
# 3. from <pkg> import * (en heredoc python)
|
||||
if [ "$TOOL_USED" = "heredoc_py" ]; then
|
||||
if printf '%s' "$CMD" | grep -qE 'from[[:space:]]+[A-Za-z0-9_.]+[[:space:]]+import[[:space:]]+\*'; then
|
||||
insert_violation "import_star_in_heredoc" "" "$CMD_HEAD" "warning"
|
||||
fi
|
||||
if printf '%s' "$CMD" | grep -qE 'client\._http\.request\('; then
|
||||
insert_violation "client_http_request_direct" "" "$CMD_HEAD" "warning"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
# PostToolUse hook: gate "tag de capability group obligatorio" tras crear/modificar
|
||||
# funciones del registry. Issue 0086 paso 9/gate.
|
||||
#
|
||||
# Comportamiento:
|
||||
# - Detecta .md de funciones (functions/, python/functions/, bash/functions/,
|
||||
# frontend/functions/, cpp/functions/) modificados en los ultimos 60s.
|
||||
# - Lee frontmatter `tags:` y verifica si al menos uno coincide con un capability
|
||||
# group declarado en docs/capabilities/INDEX.md.
|
||||
# - Si NO hay match -> emite additionalContext con la lista de funciones afectadas.
|
||||
# - NUNCA bloquea. Solo warning visible.
|
||||
#
|
||||
# Salida JSON consumida por Claude Code:
|
||||
# { "hookSpecificOutput": { "hookEventName": "PostToolUse",
|
||||
# "additionalContext": "..." } }
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
resolve_root() {
|
||||
local d="${PWD}"
|
||||
while [ "$d" != "/" ]; do
|
||||
if [ -f "$d/registry.db" ]; then
|
||||
printf '%s' "$d"
|
||||
return 0
|
||||
fi
|
||||
d=$(dirname "$d")
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
ROOT=$(resolve_root) || exit 0
|
||||
INDEX="$ROOT/docs/capabilities/INDEX.md"
|
||||
|
||||
# Si no existe el INDEX aun, no hay grupos definidos -> nada que verificar.
|
||||
[ -f "$INDEX" ] || exit 0
|
||||
|
||||
# Consume stdin (sin parsear — no necesitamos session_id para este gate)
|
||||
cat >/dev/null
|
||||
|
||||
# Solo correr si hay jq disponible
|
||||
command -v jq >/dev/null 2>&1 || exit 0
|
||||
|
||||
# 1. Cargar lista de capability groups desde el INDEX.
|
||||
# Formato esperado en INDEX.md: | [name](name.md) | N | descripcion |
|
||||
CAP_GROUPS=$(grep -oE '\[[a-z][a-z0-9_-]*\]\([a-z][a-z0-9_-]*\.md\)' "$INDEX" \
|
||||
| sed -E 's/^\[([^]]+)\].*/\1/' \
|
||||
| sort -u)
|
||||
|
||||
[ -z "$CAP_GROUPS" ] && exit 0
|
||||
|
||||
# 2. Encontrar .md de funciones modificados en ultimos 60s.
|
||||
RECENT=$(find "$ROOT/functions" "$ROOT/python/functions" "$ROOT/bash/functions" \
|
||||
"$ROOT/frontend/functions" "$ROOT/cpp/functions" \
|
||||
-maxdepth 4 -type f -name '*.md' -mmin -1 2>/dev/null || true)
|
||||
|
||||
[ -z "$RECENT" ] && exit 0
|
||||
|
||||
# 3. Para cada .md reciente: extraer tags del frontmatter, comparar con groups.
|
||||
MISSING=""
|
||||
while IFS= read -r mdfile; do
|
||||
[ -z "$mdfile" ] && continue
|
||||
# Extrae el bloque entre los dos `---` del inicio
|
||||
front=$(awk '/^---$/{c++; next} c==1 {print} c>=2 {exit}' "$mdfile" 2>/dev/null || true)
|
||||
[ -z "$front" ] && continue
|
||||
|
||||
# tags: [a, b, c] o tags:\n - a\n - b
|
||||
tags_inline=$( { printf '%s\n' "$front" | grep -E '^tags:[[:space:]]*\[' | head -1 \
|
||||
| sed -E 's/^tags:[[:space:]]*\[(.*)\].*$/\1/' \
|
||||
| tr ',' '\n' | sed -E 's/^[[:space:]"]+|[[:space:]"]+$//g'; } || true )
|
||||
|
||||
tags_block=$( { printf '%s\n' "$front" | awk '
|
||||
/^tags:[[:space:]]*$/ {intag=1; next}
|
||||
intag && /^[[:space:]]*-[[:space:]]/ {sub(/^[[:space:]]*-[[:space:]]*/, ""); print; next}
|
||||
intag && !/^[[:space:]]/ {intag=0}
|
||||
' | sed -E 's/^[[:space:]"]+|[[:space:]"]+$//g'; } || true )
|
||||
|
||||
tags=$( { printf '%s\n%s\n' "$tags_inline" "$tags_block" | grep -v '^$'; } || true )
|
||||
|
||||
matched=0
|
||||
while IFS= read -r g; do
|
||||
[ -z "$g" ] && continue
|
||||
if printf '%s\n' "$tags" | grep -qx "$g"; then
|
||||
matched=1
|
||||
break
|
||||
fi
|
||||
done <<< "$CAP_GROUPS"
|
||||
|
||||
if [ "$matched" -eq 0 ]; then
|
||||
rel="${mdfile#$ROOT/}"
|
||||
MISSING="${MISSING}${rel}\n"
|
||||
fi
|
||||
done <<< "$RECENT"
|
||||
|
||||
# 4. Si hay funciones sin tag de grupo, emitir aviso.
|
||||
if [ -n "$MISSING" ]; then
|
||||
CAP_GROUPS_CSV=$(printf '%s' "$CAP_GROUPS" | tr '\n' ',' | sed 's/,$//')
|
||||
WARN="CAPABILITY-GAP (issue 0086): funcion(es) recien tocada(s) sin tag de capability group: $(printf '%b' "$MISSING" | tr '\n' ' ')"
|
||||
WARN+="| Grupos disponibles: ${CAP_GROUPS_CSV}. Anade al menos uno al frontmatter \`tags:\` y corre \`./fn index\`. Si la funcion no encaja en ningun grupo existente, considera crear grupo nuevo (>=3 funciones) o dejarla con tag plano (no de grupo)."
|
||||
jq -n --arg ctx "$WARN" '{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PostToolUse",
|
||||
additionalContext: $ctx
|
||||
}
|
||||
}'
|
||||
fi
|
||||
|
||||
exit 0
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# UserPromptSubmit hook: recordatorio compacto de patrones canonicos del registry.
|
||||
# Inyectado como additionalContext en cada turno del usuario.
|
||||
# Issue 0085 (hardening 2).
|
||||
#
|
||||
# NUNCA bloquea. Solo printf de additionalContext.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Resolve registry root (walks up from cwd)
|
||||
resolve_root() {
|
||||
local d="${PWD}"
|
||||
while [ "$d" != "/" ]; do
|
||||
if [ -f "$d/registry.db" ]; then
|
||||
printf '%s' "$d"
|
||||
return 0
|
||||
fi
|
||||
d=$(dirname "$d")
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
ROOT=$(resolve_root) || exit 0
|
||||
|
||||
# Read input, extract session_id (UserPromptSubmit payload includes it)
|
||||
INPUT=$(cat)
|
||||
SESSION_ID=""
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
SESSION_ID=$(printf '%s' "$INPUT" | jq -r '.session_id // ""' 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
# Count current pending proposals + recent violations for situational awareness
|
||||
PROPOSALS_PENDING="?"
|
||||
VIOLATIONS_24H="?"
|
||||
CALLS_24H="?"
|
||||
CAP_CREATED=0
|
||||
CAP_USED=0
|
||||
CAP_ORPHAN=0
|
||||
|
||||
if command -v sqlite3 >/dev/null 2>&1; then
|
||||
REG="$ROOT/registry.db"
|
||||
MON="$ROOT/projects/fn_monitoring/apps/call_monitor/operations.db"
|
||||
[ -f "$REG" ] && PROPOSALS_PENDING=$(sqlite3 "$REG" "SELECT COUNT(*) FROM proposals WHERE status='pending'" 2>/dev/null || echo "?")
|
||||
if [ -f "$MON" ]; then
|
||||
VIOLATIONS_24H=$(sqlite3 "$MON" "SELECT COUNT(*) FROM violations WHERE ts >= CAST(strftime('%s','now','-1 day') AS INTEGER)" 2>/dev/null || echo "?")
|
||||
CALLS_24H=$(sqlite3 "$MON" "SELECT COUNT(*) FROM calls WHERE ts >= CAST(strftime('%s','now','-1 day') AS INTEGER)" 2>/dev/null || echo "?")
|
||||
if [ -n "$SESSION_ID" ]; then
|
||||
sid_esc=$(printf '%s' "$SESSION_ID" | sed "s/'/''/g")
|
||||
CAP_CREATED=$(sqlite3 "$MON" "SELECT COUNT(*) FROM session_capability_growth WHERE session_id='$sid_esc'" 2>/dev/null || echo 0)
|
||||
CAP_USED=$(sqlite3 "$MON" "SELECT COUNT(*) FROM session_capability_growth WHERE session_id='$sid_esc' AND calls_in_session>0" 2>/dev/null || echo 0)
|
||||
CAP_ORPHAN=$(sqlite3 "$MON" "SELECT COUNT(*) FROM session_capability_growth WHERE session_id='$sid_esc' AND calls_in_session=0" 2>/dev/null || echo 0)
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
REMINDER="REGISTRY-FIRST (issue 0085 telemetry active): "
|
||||
REMINDER+="Inspect → mcp__registry__fn_search/show/code/uses/proposal. "
|
||||
REMINDER+="Execute one fn → mcp__registry__fn_run or ./fn run. "
|
||||
REMINDER+="Compose multi-fn → heredoc python IMPORTANDO del registry. "
|
||||
REMINDER+="NUNCA sqlite3 registry.db directo (salvo schema/PRAGMA/COUNT/JOIN). "
|
||||
REMINDER+="NUNCA reescribir inline logica que ya es funcion. "
|
||||
REMINDER+="Si patron se repite >2x → propose nueva funcion via fn-constructor. "
|
||||
REMINDER+="Estado: pending_proposals=${PROPOSALS_PENDING} violations_24h=${VIOLATIONS_24H} calls_24h=${CALLS_24H}. "
|
||||
REMINDER+="CAPABILITY-GROWTH (issue 0086): created_this_session=${CAP_CREATED} used=${CAP_USED} orphan=${CAP_ORPHAN}. Si orphan>0 -> integra la funcion en el codigo o documenta por que se quedo huerfana. "
|
||||
REMINDER+="Comando autocheck: /fn_claude."
|
||||
|
||||
jq -n --arg ctx "$REMINDER" '{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "UserPromptSubmit",
|
||||
additionalContext: $ctx
|
||||
}
|
||||
}'
|
||||
Reference in New Issue
Block a user