Initial: desk + ncantu placeholder + per-project cursor configs

**Motivations:**
- Centraliser les fichiers Cursor (rules, skills, agents, commands, hooks) par user et par projet

**Root causes:**
- N/A

**Correctifs:**
- N/A

**Evolutions:**
- desk: rules, skills-cursor, agents, commands, hooks, argv/hooks/mcp.json
- ncantu: README placeholder
- 4NK_node, algo, builazoo, ia_local, lecoffre_ng, lecoffre_ng_pprod, lecoffre_ng_test: .cursor contents

**Pages affectées:**
- cursor/desk/, cursor/ncantu/, cursor/<project>/
This commit is contained in:
2026-03-03 23:29:29 +01:00
commit 785868b53b
114 changed files with 6455 additions and 0 deletions

38
desk/hooks/README.md Normal file
View File

@@ -0,0 +1,38 @@
# Cursor Hooks (niveau utilisateur)
Hooks pour observer et étendre le cycle de l'agent Cursor. Utiles pour la commande `/fix-lint` et le subagent fix-lint en arrière-plan.
## Logs
Tous les événements sont journalisés dans `~/.cursor/logs/hooks.log`.
## Hooks configurés
- **sessionStart** : Log démarrage session
- **sessionEnd** : Log fin session
- **subagentStart** : Log lancement subagent, autorise fix-lint
- **subagentStop** : Log fin subagent, followup pour fix-lint (vérifier lint)
- **beforeShellExecution** : Log commande shell, autorise par défaut
- **afterShellExecution** : Log sortie commande (tronquée)
- **beforeReadFile** : Log accès fichier
- **afterFileEdit** : Log édition fichier
- **stop** : Log fin agent (completed/aborted/error)
- **postToolUseFailure** : Log échec outil (shell, etc.)
## Fix-lint
Quand le subagent fix-lint se termine avec succès, `subagentStop` ajoute un message de suivi suggérant de relancer `npm run lint` dans les trois applications.
## Déploiement et correction automatique
Quand un subagent de déploiement échoue (`status=error` et résultat contenant deploy/déploiement), `subagentStop` retourne un `followup_message` demandant à l'agent parent d'analyser l'erreur, identifier la root cause, corriger le bug et retenter. Pour bénéficier de ce flux, lancer le déploiement via le subagent `/deploy` (et non via une commande shell directe).
## Personnalisation
- **Bloquer une commande** : modifier `before-shell-execution.sh`, retourner `"permission":"deny"`.
- **Bloquer un subagent** : modifier `subagent-start.sh`, retourner `{"decision":"deny","reason":"..."}`.
- **Désactiver un hook** : retirer l'entrée correspondante dans `~/.cursor/hooks.json`.
## Redémarrage
Redémarrer Cursor après modification de `hooks.json` ou des scripts.

11
desk/hooks/after-file-edit.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
# afterFileEdit: log edit. Format can be enabled by uncommenting the block below.
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
path=$(echo "$input" | jq -r '.path // .file_path // .fsPath // .file // ""')
log_event "afterFileEdit" "path=$path"
echo '{}'
exit 0

View File

@@ -0,0 +1,20 @@
#!/bin/bash
# afterShellExecution: log command output (truncated), useful for lint runs
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
command=$(echo "$input" | jq -r '.command // ""')
duration=$(echo "$input" | jq -r '.duration // 0')
output=$(echo "$input" | jq -r '.tool_output // .command_output // ""' 2>/dev/null || echo "$input" | jq -r '.command_output // ""' 2>/dev/null)
# Some hooks use tool_output, others command_output - try both
if [ -z "$output" ]; then
output=$(echo "$input" | jq -r '.tool_output // ""' 2>/dev/null)
fi
log_event "afterShellExecution" "cmd=${command:0:80} duration=${duration}ms"
if [ -n "$output" ] && [ ${#output} -gt 0 ]; then
log_json_truncated "afterShellExecution_output" "$output" 400
fi
echo '{}'
exit 0

12
desk/hooks/before-read-file.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# beforeReadFile: log file access, allow by default
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
path=$(echo "$input" | jq -r '.path // .file_path // .fsPath // "?"')
log_event "beforeReadFile" "path=$path"
# Allow all reads. Return empty or allow decision.
echo '{}'
exit 0

View File

@@ -0,0 +1,14 @@
#!/bin/bash
# beforeShellExecution: log command, allow by default. Block dangerous patterns if needed.
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
command=$(echo "$input" | jq -r '.command // ""')
cwd=$(echo "$input" | jq -r '.cwd // "?"')
log_event "beforeShellExecution" "cwd=$cwd cmd=${command:0:100}"
# Allow all commands by default. Add deny rules here if needed (e.g. rm -rf /, etc.)
echo '{"continue":true,"permission":"allow"}'
exit 0

31
desk/hooks/lib.sh Executable file
View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Shared helpers for Cursor hooks. Source with: source "$(dirname "$0")/lib.sh"
LOG_DIR="${CURSOR_HOOKS_LOG_DIR:-$HOME/.cursor/logs}"
LOG_FILE="${LOG_DIR}/hooks.log"
log_event() {
local event="$1"
local summary="$2"
mkdir -p "$LOG_DIR"
echo "[$(date -Iseconds)] [$event] $summary" >> "$LOG_FILE"
}
log_json() {
local event="$1"
local json="$2"
mkdir -p "$LOG_DIR"
echo "[$(date -Iseconds)] [$event] $json" >> "$LOG_FILE"
}
log_json_truncated() {
local event="$1"
local json="$2"
local max="${3:-500}"
mkdir -p "$LOG_DIR"
if [ ${#json} -gt "$max" ]; then
echo "[$(date -Iseconds)] [$event] ${json:0:$max}... (truncated)" >> "$LOG_FILE"
else
echo "[$(date -Iseconds)] [$event] $json" >> "$LOG_FILE"
fi
}

View File

@@ -0,0 +1,18 @@
#!/bin/bash
# postToolUseFailure: log when a tool fails (shell, etc.)
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name // "?"')
failure_type=$(echo "$input" | jq -r '.failure_type // "?"')
error_message=$(echo "$input" | jq -r '.error_message // ""')
command=$(echo "$input" | jq -r '.tool_input.command // .tool_input // ""' 2>/dev/null || echo "")
log_event "postToolUseFailure" "tool=$tool_name failure=$failure_type"
log_json_truncated "postToolUseFailure_error" "$error_message" 500
if [ -n "$command" ]; then
log_event "postToolUseFailure" "command=${command:0:150}"
fi
echo '{}'
exit 0

9
desk/hooks/session-end.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# sessionEnd: log session end
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
log_event "sessionEnd" "conversation_id=$(echo "$input" | jq -r '.conversation_id // "?"')"
exit 0

9
desk/hooks/session-start.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# sessionStart: log session start, inject context for fix-lint / commands
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
log_event "sessionStart" "conversation_id=$(echo "$input" | jq -r '.conversation_id // "?"') workspace=$(echo "$input" | jq -r '.workspace_roots[0] // "?"')"
exit 0

13
desk/hooks/stop.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
# stop: log agent completion/abort/error
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
status=$(echo "$input" | jq -r '.status // "?"')
loop_count=$(echo "$input" | jq -r '.loop_count // 0')
conversation_id=$(echo "$input" | jq -r '.conversation_id // "?"')
log_event "stop" "conversation_id=$conversation_id status=$status loop_count=$loop_count"
echo '{}'
exit 0

12
desk/hooks/subagent-start.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# subagentStart: log subagent launch, allow fix-lint and other subagents
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
subagent_type=$(echo "$input" | jq -r '.subagent_type // "?"')
prompt_preview=$(echo "$input" | jq -r '.prompt // ""' | head -c 120)
log_event "subagentStart" "type=$subagent_type prompt_preview=${prompt_preview}..."
echo '{"decision":"allow"}'
exit 0

33
desk/hooks/subagent-stop.sh Executable file
View File

@@ -0,0 +1,33 @@
#!/bin/bash
# subagentStop: log completion, add followup for fix-lint when completed
HOOKS_DIR="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=lib.sh
. "${HOOKS_DIR}/lib.sh"
input=$(cat)
subagent_type=$(echo "$input" | jq -r '.subagent_type // "?"')
status=$(echo "$input" | jq -r '.status // "?"')
duration=$(echo "$input" | jq -r '.duration // 0')
result_preview=$(echo "$input" | jq -r '.result // ""' | head -c 200)
log_event "subagentStop" "type=$subagent_type status=$status duration=${duration}ms"
log_json_truncated "subagentStop_result" "$result_preview" 300
result_lower=$(echo "$input" | jq -r '.result // ""' | tr '[:upper:]' '[:lower:]')
# For fix-lint or lint-related subagents: suggest next step when completed
if [ "$status" = "completed" ]; then
if echo "$result_lower" | grep -qE "lint|fix-lint|corriger.*erreur"; then
echo '{"followup_message":"Lint fix terminé. Vérifier : npm run lint dans lecoffre-back-main, lecoffre-front-main, lecoffre-ressources-dev."}'
exit 0
fi
fi
# For deploy subagent: on error, request bug correction
if [ "$status" = "error" ]; then
if echo "$result_lower" | grep -qE "deploy|déploiement|build-and-deploy|deploy-app"; then
echo '{"followup_message":"Le déploiement a échoué. Analyser l'\''erreur, identifier la root cause, corriger le bug puis retenter le déploiement."}'
exit 0
fi
fi
echo '{}'
exit 0