2026-05-13 17:05:59 +03:00
<!DOCTYPE html>
< html lang = "ru" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Система автоматизации Лидерры< / title >
< script src = "https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js" > < / script >
< style >
* { box-sizing : border-box ; margin : 0 ; padding : 0 ; }
body { background : #0d0d1a ; color : #fdf6e3 ; font-family : 'Segoe UI' , system-ui , sans-serif ; height : 100 vh ; display : flex ; flex-direction : column ; overflow : hidden ; }
/* ── Toolbar ── */
# toolbar { background : #073642 ; border-bottom : 1 px solid #586e75 ; padding : 8 px 12 px ; display : flex ; align-items : center ; gap : 10 px ; flex-shrink : 0 ; }
# toolbar h1 { font-size : 14 px ; color : #93a1a1 ; white-space : nowrap ; margin-right : 6 px ; }
# search { background : #002b36 ; border : 1 px solid #586e75 ; color : #fdf6e3 ; border-radius : 5 px ; padding : 5 px 10 px ; font-size : 13 px ; width : 220 px ; outline : none ; }
# search : focus { border-color : #268bd2 ; }
# search :: placeholder { color : #586e75 ; }
. btn { background : #073642 ; border : 1 px solid #586e75 ; color : #93a1a1 ; border-radius : 5 px ; padding : 5 px 12 px ; font-size : 12 px ; cursor : pointer ; transition : all 0.15 s ; }
. btn : hover { background : #0d4a5a ; color : #fdf6e3 ; border-color : #839496 ; }
# btn-clear { margin-left : auto ; }
/* ── Main area ── */
# main { flex : 1 ; display : flex ; overflow : hidden ; }
/* ── Graph canvas ── */
# network { flex : 1 ; background : #1e1e2e ; }
2026-05-14 09:09:01 +03:00
# network canvas { background : #1e1e2e ; }
2026-05-13 17:05:59 +03:00
/* ── Legend panel ── */
2026-05-14 12:17:08 +03:00
# legend-panel { width : 300 px ; min-width : 300 px ; background : #002b36 ; border-left : 1 px solid #586e75 ; overflow-y : auto ; padding : 16 px ; display : none ; flex-direction : column ; gap : 14 px ; position : relative ; }
# legend-handle {
position : absolute ; left : 0 ; top : 0 ;
width : 6 px ; height : 100 % ;
cursor : col-resize ;
background : transparent ;
transition : background 0.15 s ;
z-index : 10 ;
}
# legend-handle : hover , # legend-handle . dragging { background : #0d4a5a ; }
2026-05-13 17:05:59 +03:00
# legend-panel . visible { display : flex ; }
# legend-close { align-self : flex-end ; background : none ; border : none ; color : #586e75 ; font-size : 18 px ; cursor : pointer ; line-height : 1 ; padding : 0 ; }
# legend-close : hover { color : #fdf6e3 ; }
# legend-title { font-size : 15 px ; font-weight : 600 ; color : #fdf6e3 ; overflow-wrap : break-word ; }
2026-05-14 13:14:20 +03:00
# legend-edge-title { font-size : 15 px ; font-weight : 600 ; color : #fdf6e3 ; overflow-wrap : break-word ; line-height : 1.4 ; }
2026-05-13 17:05:59 +03:00
# legend-category { font-size : 11 px ; text-transform : uppercase ; letter-spacing : 0.08 em ; }
. legend-section { background : #073642 ; border-radius : 6 px ; padding : 10 px 12 px ; }
. legend-section h4 { font-size : 11 px ; text-transform : uppercase ; letter-spacing : 0.06 em ; color : #839496 ; margin-bottom : 6 px ; }
. legend-section p { font-size : 13 px ; color : #eee8d5 ; line-height : 1.5 ; }
. legend-section ul { list-style : none ; display : flex ; flex-direction : column ; gap : 4 px ; }
. legend-section li { font-size : 12 px ; color : #eee8d5 ; line-height : 1.4 ; }
. legend-section li span . cond { color : #839496 ; font-style : italic ; }
2026-05-14 13:16:56 +03:00
. conflict-item { border-radius : 4 px ; padding : 6 px 8 px ; margin-top : 4 px ; }
. conflict-item . cname { font-weight : 600 ; font-size : 12 px ; }
. conflict-item . cdesc { color : #eee8d5 ; font-size : 11 px ; margin-top : 2 px ; line-height : 1.4 ; }
2026-05-13 17:05:59 +03:00
# legend-no-conflicts { font-size : 12 px ; color : #586e75 ; }
/* ── Category legend footer ── */
# cat-legend { background : #073642 ; border-top : 1 px solid #586e75 ; padding : 7 px 14 px ; display : flex ; flex-wrap : wrap ; gap : 12 px ; flex-shrink : 0 ; }
. cat-item { display : flex ; align-items : center ; gap : 5 px ; font-size : 11 px ; color : #839496 ; }
. cat-dot { width : 10 px ; height : 10 px ; border-radius : 50 % ; flex-shrink : 0 ; }
2026-05-15 06:04:27 +03:00
/* .cat-item interactive states (Task 1) — separate from layout rule above to keep concerns split */
2026-05-15 06:00:14 +03:00
. cat-item {
cursor : pointer ;
padding : 2 px 6 px ;
border-radius : 4 px ;
transition : background 0.12 s , box-shadow 0.12 s ;
user-select : none ;
}
. cat-item : hover { background : rgba ( 255 , 255 , 255 , 0.05 ) ; }
. cat-item . active {
background : rgba ( 253 , 246 , 227 , 0.12 ) ;
box-shadow : inset 0 0 0 1 px rgba ( 253 , 246 , 227 , 0.4 ) ;
color : #fdf6e3 ;
}
2026-05-13 17:05:59 +03:00
< / style >
< / head >
< body >
< div id = "toolbar" >
< h1 > 🗺 Лидерра — карта автоматизации< / h1 >
< input id = "search" type = "text" placeholder = "Поиск узла…" autocomplete = "off" >
< button class = "btn" id = "btn-freeze" > ❄ Зафиксировать< / button >
< button class = "btn" id = "btn-unfreeze" > ▶ Расшевелить< / button >
< button class = "btn" id = "btn-reset" > ⊙ Сбросить вид< / button >
< button class = "btn" id = "btn-clear" > ✕ Снять выделение< / button >
< / div >
< div id = "main" >
< div id = "network" > < / div >
< div id = "legend-panel" >
2026-05-14 12:17:08 +03:00
< div id = "legend-handle" title = "Перетащи, чтобы изменить ширину" > < / div >
2026-05-13 17:05:59 +03:00
< button id = "legend-close" > × < / button >
2026-05-14 13:14:20 +03:00
< div id = "legend-node-content" >
< div id = "legend-title" > —< / div >
< div id = "legend-category" > < / div >
< div class = "legend-section" > < h4 > Что делает< / h4 > < p id = "ld-desc" > —< / p > < / div >
< div class = "legend-section" > < h4 > Когда используется< / h4 > < p id = "ld-when" > —< / p > < / div >
< div class = "legend-section" > < h4 > Ограничения< / h4 > < p id = "ld-limits" > —< / p > < / div >
< div class = "legend-section" > < h4 > Кому подчиняется< / h4 > < ul id = "ld-reports" > < / ul > < / div >
< div class = "legend-section" > < h4 > Кто подчиняется ему< / h4 > < ul id = "ld-manages" > < / ul > < / div >
< div class = "legend-section" > < h4 > С кем работает одновременно< / h4 > < ul id = "ld-together" > < / ul > < / div >
< div class = "legend-section" id = "conflicts-section" >
< h4 > ⚡ Конфликты< / h4 >
< div id = "ld-conflicts" > < / div >
< / div >
< / div >
< div id = "legend-edge-content" style = "display:none;" >
< div id = "legend-edge-title" > —< / div >
< div class = "legend-section" > < h4 > Источник запроса< / h4 > < p id = "le-from" > —< / p > < / div >
< div class = "legend-section" > < h4 > Конечный получатель< / h4 > < p id = "le-to" > —< / p > < / div >
< div class = "legend-section" > < h4 > Тип связи< / h4 > < p id = "le-type" > —< / p > < / div >
< div class = "legend-section" > < h4 > Когда срабатывает< / h4 > < p id = "le-when" > —< / p > < / div >
< div class = "legend-section" > < h4 > Что передаёт< / h4 > < p id = "le-transfers" > —< / p > < / div >
< div class = "legend-section" > < h4 > Обязательность< / h4 > < p id = "le-mandatory" > —< / p > < / div >
< div class = "legend-section" > < h4 > Регламент< / h4 > < p id = "le-rule" > —< / p > < / div >
2026-05-13 17:05:59 +03:00
< / div >
< / div >
< / div >
2026-05-15 06:12:43 +03:00
<!-- data - filter - key атрибуты потребляются SECTION 8 (interactive highlighting) -->
2026-05-13 17:05:59 +03:00
< div id = "cat-legend" >
2026-05-15 06:07:16 +03:00
< div class = "cat-item" data-filter-key = "group:rules" > < div class = "cat-dot" style = "background:#268bd2" > < / div > Правила< / div >
< div class = "cat-item" data-filter-key = "group:plugins" > < div class = "cat-dot" style = "background:#859900" > < / div > Плагины< / div >
< div class = "cat-item" data-filter-key = "group:skills_sp" > < div class = "cat-dot" style = "background:#6c71c4" > < / div > Скилы Superpowers< / div >
< div class = "cat-item" data-filter-key = "group:skills_proj" > < div class = "cat-dot" style = "background:#d33682" > < / div > Скилы проекта< / div >
< div class = "cat-item" data-filter-key = "group:hooks" > < div class = "cat-dot" style = "background:#2aa198" > < / div > Хуки< / div >
< div class = "cat-item" data-filter-key = "group:agents" > < div class = "cat-dot" style = "background:#b58900" > < / div > Агенты< / div >
< div class = "cat-item" data-filter-key = "group:mcp" > < div class = "cat-dot" style = "background:#cb4b16" > < / div > MCP-серверы< / div >
< div class = "cat-item" data-filter-key = "group:lefthook" > < div class = "cat-dot" style = "background:#dc322f" > < / div > Lefthook jobs< / div >
< div class = "cat-item" data-filter-key = "group:memory" > < div class = "cat-dot" style = "background:#586e75" > < / div > Memory files< / div >
< div class = "cat-item" data-filter-key = "conflict:RED" > < div class = "cat-dot" style = "background:#ff5f57; border:1px dashed #ff5f57" > < / div > 🔴 Не закрыт правилом< / div >
< div class = "cat-item" data-filter-key = "conflict:BLACK" > < div class = "cat-dot" style = "background:#888888; border:1px dashed #888888" > < / div > ⚫ Возник на практике< / div >
< div class = "cat-item" data-filter-key = "conflict:GREEN" > < div class = "cat-dot" style = "background:#859900; border:1px dashed #859900" > < / div > 🟢 Закрыт правилом< / div >
2026-05-13 17:05:59 +03:00
< / div >
< script >
// ════════════════════════════════════════════════════
// SECTION 1: NODES
// ════════════════════════════════════════════════════
2026-05-14 09:22:45 +03:00
// Радиально-секторная компоновка.
// Сектора (по 90°): N=workflow (0– 90), E=UI (90– 180), S=infra (180– 270), W=data/RLS (270– 360).
const RADII = [ 0 , 220 , 400 , 600 , 800 , 1000 , 1180 ] ;
function pos ( ring , angleDeg ) {
const r = RADII [ ring ] ;
const a = angleDeg * Math . PI / 180 ;
return { x : Math . round ( r * Math . cos ( a ) ) , y : Math . round ( r * Math . sin ( a ) ) } ;
}
2026-05-13 17:05:59 +03:00
const NODES = [
2026-05-14 09:22:45 +03:00
// ── ПРАВИЛА (4) ── центр + первое кольцо ───────
{ id : 'pravila' , label : 'Pravila v1.13' , group : 'rules' , size : 38 , ring : 0 , ... pos ( 0 , 0 ) } ,
{ id : 'claude_md' , label : 'CLAUDE.md v1.92' , group : 'rules' , size : 34 , ring : 1 , ... pos ( 1 , 30 ) } ,
{ id : 'psr_v1' , label : 'PSR_v1 v2.1' , group : 'rules' , size : 32 , ring : 1 , ... pos ( 1 , 150 ) } ,
{ id : 'tooling' , label : 'Tooling v1.17' , group : 'rules' , size : 30 , ring : 1 , ... pos ( 1 , 270 ) } ,
// ── ПЛАГИНЫ (5) ── второе кольцо ───────────────
{ id : 'superpowers' , label : 'Superpowers v5.1' , group : 'plugins' , size : 30 , ring : 2 , ... pos ( 2 , 45 ) } ,
{ id : 'fd_plugin' , label : 'Frontend Design' , group : 'plugins' , size : 26 , ring : 2 , ... pos ( 2 , 135 ) } ,
{ id : 'upm' , label : 'UI UX Pro Max' , group : 'plugins' , size : 22 , ring : 2 , ... pos ( 2 , 165 ) } ,
{ id : 'claude_md_mgmt' , label : 'claude-md-mgmt' , group : 'plugins' , size : 22 , ring : 2 , ... pos ( 2 , 225 ) } ,
{ id : 'hookify_plugin' , label : 'hookify (плагин)' , group : 'plugins' , size : 22 , ring : 2 , ... pos ( 2 , 200 ) } ,
// ── СКИЛЫ SUPERPOWERS (14) — N sector (0– 90) ────
{ id : 'sk_brainstorm' , label : 'brainstorming' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 5 ) } ,
{ id : 'sk_wplans' , label : 'writing-plans' , group : 'skills_sp' , size : 20 , ring : 3 , ... pos ( 3 , 11 ) } ,
{ id : 'sk_eplans' , label : 'executing-plans' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 17 ) } ,
{ id : 'sk_subagent' , label : 'subagent-driven' , group : 'skills_sp' , size : 20 , ring : 3 , ... pos ( 3 , 23 ) } ,
{ id : 'sk_tdd' , label : 'TDD' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 29 ) } ,
{ id : 'sk_verify' , label : 'verification-before-completion' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 36 ) } ,
{ id : 'sk_debug' , label : 'systematic-debugging' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 43 ) } ,
{ id : 'sk_parallel' , label : 'parallel-work' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 50 ) } ,
{ id : 'sk_worktree' , label : 'worktree' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 57 ) } ,
{ id : 'sk_pr' , label : 'finishing-pr' , group : 'skills_sp' , size : 18 , ring : 3 , ... pos ( 3 , 64 ) } ,
{ id : 'sk_coderev' , label : 'code-review' , group : 'skills_sp' , size : 16 , ring : 3 , ... pos ( 3 , 71 ) } ,
{ id : 'sk_spreview' , label : 'spec-review' , group : 'skills_sp' , size : 16 , ring : 3 , ... pos ( 3 , 78 ) } ,
{ id : 'sk_wskills' , label : 'writing-skills' , group : 'skills_sp' , size : 16 , ring : 3 , ... pos ( 3 , 85 ) } ,
{ id : 'sk_elements' , label : 'elements-of-style' , group : 'skills_sp' , size : 16 , ring : 3 , ... pos ( 3 , 92 ) } ,
// ── СКИЛЫ ПРОЕКТА (2) — W sector (RLS) ─────────
{ id : 'sk_rls' , label : 'rls-check' , group : 'skills_proj' , size : 20 , ring : 3 , ... pos ( 3 , 305 ) } ,
{ id : 'sk_qitem' , label : 'q-item-add' , group : 'skills_proj' , size : 20 , ring : 3 , ... pos ( 3 , 220 ) } ,
// ── ХУКИ (5) — S+infra ────────────────────────
{ id : 'hk_session' , label : 'SessionStart:\ncontext-inject' , group : 'hooks' , size : 24 , ring : 4 , ... pos ( 4 , 100 ) } ,
{ id : 'hk_economy' , label : 'UserPromptSubmit:\neconomy-mode' , group : 'hooks' , size : 22 , ring : 4 , ... pos ( 4 , 95 ) } ,
{ id : 'hk_pre_claude' , label : 'PreToolUse:\nCLAUDE.md-warn' , group : 'hooks' , size : 22 , ring : 4 , ... pos ( 4 , 215 ) } ,
{ id : 'hk_post_md' , label : 'PostToolUse:\nmarkdownlint' , group : 'hooks' , size : 20 , ring : 4 , ... pos ( 4 , 195 ) } ,
{ id : 'hk_post_schema' , label : 'PostToolUse:\nschema-changelog' , group : 'hooks' , size : 20 , ring : 4 , ... pos ( 4 , 300 ) } ,
// ── АГЕНТЫ (11) — N (workflow) + W (RLS) ──────
{ id : 'ag_explore' , label : 'Explore' , group : 'agents' , size : 20 , ring : 4 , ... pos ( 4 , 10 ) } ,
{ id : 'ag_general' , label : 'general-purpose' , group : 'agents' , size : 20 , ring : 4 , ... pos ( 4 , 25 ) } ,
{ id : 'ag_plan' , label : 'Plan' , group : 'agents' , size : 20 , ring : 4 , ... pos ( 4 , 40 ) } ,
{ id : 'ag_pest' , label : 'pest-parallel-debugger' , group : 'agents' , size : 24 , ring : 4 , ... pos ( 4 , 55 ) } ,
{ id : 'ag_guide' , label : 'claude-code-guide' , group : 'agents' , size : 18 , ring : 4 , ... pos ( 4 , 70 ) } ,
{ id : 'ag_statusline' , label : 'statusline-setup' , group : 'agents' , size : 18 , ring : 4 , ... pos ( 4 , 85 ) } ,
{ id : 'ag_hookify' , label : 'hookify:\nconversation-analyzer' , group : 'agents' , size : 18 , ring : 4 , ... pos ( 4 , 230 ) } ,
{ id : 'ag_pcreator' , label : 'plugin-dev:\nagent-creator' , group : 'agents' , size : 16 , ring : 4 , ... pos ( 4 , 245 ) } ,
{ id : 'ag_pvalid' , label : 'plugin-dev:\nplugin-validator' , group : 'agents' , size : 16 , ring : 4 , ... pos ( 4 , 260 ) } ,
{ id : 'ag_skreview' , label : 'plugin-dev:\nskill-reviewer' , group : 'agents' , size : 16 , ring : 4 , ... pos ( 4 , 275 ) } ,
{ id : 'ag_rls' , label : 'rls-reviewer' , group : 'agents' , size : 22 , ring : 4 , ... pos ( 4 , 315 ) } ,
// ── MCP-СЕРВЕРЫ (7) — E (UI) + W (data) ───────
{ id : 'mcp_21st' , label : 'MCP: 21st.dev Magic' , group : 'mcp' , size : 20 , ring : 5 , ... pos ( 5 , 130 ) } ,
{ id : 'mcp_pw' , label : 'MCP: playwright' , group : 'mcp' , size : 22 , ring : 5 , ... pos ( 5 , 110 ) } ,
{ id : 'mcp_gh' , label : 'MCP: github' , group : 'mcp' , size : 22 , ring : 5 , ... pos ( 5 , 75 ) } ,
{ id : 'mcp_boost' , label : 'MCP: laravel-boost' , group : 'mcp' , size : 24 , ring : 5 , ... pos ( 5 , 290 ) } ,
{ id : 'mcp_redis' , label : 'MCP: redis' , group : 'mcp' , size : 22 , ring : 5 , ... pos ( 5 , 310 ) } ,
{ id : 'mcp_sentry' , label : 'MCP: sentry' , group : 'mcp' , size : 22 , ring : 5 , ... pos ( 5 , 330 ) } ,
{ id : 'mcp_semgrep' , label : 'MCP: semgrep' , group : 'mcp' , size : 20 , ring : 5 , ... pos ( 5 , 350 ) } ,
// ── LEFTHOOK JOBS (10) — S+W (infra/data) ─────
{ id : 'lh_mdlint' , label : 'lefthook:\nmarkdownlint' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 185 ) } ,
{ id : 'lh_cspell' , label : 'lefthook:\ncspell' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 200 ) } ,
{ id : 'lh_stylelint' , label : 'lefthook:\nstylelint' , group : 'lefthook' , size : 16 , ring : 5 , ... pos ( 5 , 215 ) } ,
{ id : 'lh_eslint' , label : 'lefthook:\neslint-vue' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 230 ) } ,
{ id : 'lh_lychee' , label : 'lefthook:\nlychee-links' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 245 ) } ,
{ id : 'lh_gitleaks' , label : 'lefthook:\ngitleaks' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 260 ) } ,
{ id : 'lh_gitleaks2' , label : 'lefthook:\ngitleaks pre-push' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 275 ) } ,
{ id : 'lh_pint' , label : 'lefthook:\npint' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 25 ) } ,
{ id : 'lh_larastan' , label : 'lefthook:\nlarastan' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 50 ) } ,
{ id : 'lh_squawk' , label : 'lefthook:\nsquawk' , group : 'lefthook' , size : 18 , ring : 5 , ... pos ( 5 , 320 ) } ,
// ── MEMORY FILES (15) — внешнее кольцо ──────────
{ id : 'mem_user' , label : 'memory:\nuser_profile' , group : 'memory' , size : 16 , ring : 6 , ... pos ( 6 , 0 ) } ,
{ id : 'mem_comm' , label : 'memory:\nfeedback_comm' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 24 ) } ,
{ id : 'mem_env' , label : 'memory:\nfeedback_env' , group : 'memory' , size : 16 , ring : 6 , ... pos ( 6 , 48 ) } ,
{ id : 'mem_sp' , label : 'memory:\nfeedback_superpowers' , group : 'memory' , size : 16 , ring : 6 , ... pos ( 6 , 72 ) } ,
{ id : 'mem_plugins' , label : 'memory:\nfeedback_plugins' , group : 'memory' , size : 16 , ring : 6 , ... pos ( 6 , 96 ) } ,
{ id : 'mem_handoff' , label : 'memory:\nreference_handoff' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 120 ) } ,
{ id : 'mem_redesign' , label : 'memory:\nportal_redesign' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 144 ) } ,
{ id : 'mem_devindices' , label : 'memory:\ndev_indices' , group : 'memory' , size : 12 , ring : 6 , ... pos ( 6 , 168 ) } ,
{ id : 'mem_phase1' , label : 'memory:\nphase1_strategy' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 192 ) } ,
{ id : 'mem_state' , label : 'memory:\nproject_state' , group : 'memory' , size : 16 , ring : 6 , ... pos ( 6 , 216 ) } ,
{ id : 'mem_brain' , label : 'memory:\nclaude_brain' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 240 ) } ,
{ id : 'mem_supplier' , label : 'memory:\nsupplier_integration' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 264 ) } ,
{ id : 'mem_audit' , label : 'memory:\naudit_2026-05-13' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 288 ) } ,
{ id : 'mem_archive' , label : 'memory:\nreference_archive' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 312 ) } ,
{ id : 'mem_github' , label : 'memory:\nreference_github' , group : 'memory' , size : 14 , ring : 6 , ... pos ( 6 , 336 ) } ,
2026-05-13 17:05:59 +03:00
] ;
// ════════════════════════════════════════════════════
// SECTION 2: EDGES
// ════════════════════════════════════════════════════
2026-05-14 12:59:25 +03:00
const CONFLICT _TYPES = {
RED : { color : '#ff5f57' , bg : '#2d0000' , emoji : '🔴' , label : 'Не закрыт правилом' , rank : 1 } ,
BLACK : { color : '#888888' , bg : '#1a1a1a' , emoji : '⚫' , label : 'Возник на практике' , rank : 2 } ,
GREEN : { color : '#859900' , bg : '#0e1a00' , emoji : '🟢' , label : 'Закрыт правилом' , rank : 3 } ,
} ;
2026-05-13 17:05:59 +03:00
const E = ( from , to , label ) => ( {
2026-05-14 09:09:42 +03:00
from , to ,
title : label ,
2026-05-13 17:05:59 +03:00
color : { color : '#586e75' , highlight : '#93a1a1' , hover : '#93a1a1' } ,
arrows : { to : { enabled : true , scaleFactor : 0.6 } } ,
2026-05-14 09:09:42 +03:00
smooth : { type : 'continuous' , roundness : 0.5 }
2026-05-13 17:05:59 +03:00
} ) ;
2026-05-14 12:59:25 +03:00
const CONFLICT = ( from , to , label , type = 'RED' ) => ( {
2026-05-14 09:09:42 +03:00
from , to ,
title : label ,
2026-05-14 12:59:25 +03:00
label : CONFLICT _TYPES [ type ] . emoji ,
2026-05-13 17:05:59 +03:00
dashes : true ,
width : 2 ,
2026-05-14 12:59:25 +03:00
color : { color : CONFLICT _TYPES [ type ] . color , highlight : '#ff8880' , hover : '#ff8880' } ,
2026-05-13 17:05:59 +03:00
arrows : { to : { enabled : true , scaleFactor : 0.7 } , from : { enabled : true , scaleFactor : 0.7 } } ,
2026-05-14 12:59:25 +03:00
font : { color : CONFLICT _TYPES [ type ] . color , size : 14 , align : 'middle' , strokeWidth : 3 , strokeColor : '#1e1e2e' } ,
2026-05-13 17:05:59 +03:00
smooth : { type : 'curvedCW' , roundness : 0.35 }
} ) ;
const EDGES = [
// ── ПРАВИЛА — иерархия ──────────────────────────
E ( 'pravila' , 'claude_md' , 'подчиняет\n(уровень 1→2a)' ) ,
E ( 'pravila' , 'psr_v1' , 'подчиняет\n(уровень 1→3)' ) ,
E ( 'claude_md' , 'tooling' , 'ссылается\nна реестр' ) ,
E ( 'pravila' , 'superpowers' , '§12: обязывает\nинвокировать 1-м' ) ,
// ── PSR_v1 координирует плагины ─────────────────
E ( 'psr_v1' , 'superpowers' , 'R5: координирует\nпарный стек' ) ,
E ( 'psr_v1' , 'fd_plugin' , 'R5: координирует\nпарный стек' ) ,
E ( 'psr_v1' , 'upm' , 'R14.3: активирует\nтолько через pipeline' ) ,
E ( 'psr_v1' , 'mcp_21st' , 'R14.4: активирует\nтолько через pipeline' ) ,
E ( 'psr_v1' , 'claude_md_mgmt' , 'R10.1 блок 1:\nинфраструктурный' ) ,
// ── CLAUDE.md ────────────────────────────────────
E ( 'claude_md' , 'mcp_boost' , 'описывает §3.2' ) ,
E ( 'claude_md' , 'mcp_sentry' , 'описывает §4.8' ) ,
E ( 'claude_md' , 'mcp_redis' , 'описывает §4.9' ) ,
E ( 'claude_md' , 'claude_md_mgmt' , '§5п.10:\nединственный канал' ) ,
E ( 'claude_md' , 'ag_pest' , 'описывает\nкогда вызывать' ) ,
E ( 'claude_md' , 'ag_rls' , 'описывает\nкогда вызывать' ) ,
// ── ХУКИ ────────────────────────────────────────
E ( 'hk_pre_claude' , 'claude_md' , 'проверяет\nпри Edit/Write' ) ,
E ( 'hk_post_md' , 'lh_mdlint' , 'дублирует задачу\n(локально)' ) ,
E ( 'hk_post_schema' , 'claude_md' , 'напоминает про\nCHANGELOG_schema' ) ,
E ( 'hk_session' , 'mem_user' , 'читает\nпри старте' ) ,
E ( 'hk_session' , 'mem_env' , 'читает\nпри старте' ) ,
E ( 'hk_session' , 'mem_sp' , 'читает\nпри старте' ) ,
E ( 'hk_session' , 'mem_plugins' , 'читает\nпри старте' ) ,
E ( 'hk_session' , 'mem_state' , 'читает\nпри старте' ) ,
E ( 'hk_economy' , 'superpowers' , 'парсит уровень\nэкономии' ) ,
// ── SUPERPOWERS содержит скилы ──────────────────
E ( 'superpowers' , 'sk_brainstorm' , 'содержит' ) ,
E ( 'superpowers' , 'sk_tdd' , 'содержит' ) ,
E ( 'superpowers' , 'sk_debug' , 'содержит' ) ,
E ( 'superpowers' , 'sk_wplans' , 'содержит' ) ,
E ( 'superpowers' , 'sk_eplans' , 'содержит' ) ,
E ( 'superpowers' , 'sk_verify' , 'содержит' ) ,
E ( 'superpowers' , 'sk_parallel' , 'содержит' ) ,
E ( 'superpowers' , 'sk_worktree' , 'содержит' ) ,
E ( 'superpowers' , 'sk_pr' , 'содержит' ) ,
E ( 'superpowers' , 'sk_subagent' , 'содержит' ) ,
E ( 'superpowers' , 'sk_wskills' , 'содержит' ) ,
E ( 'superpowers' , 'sk_spreview' , 'содержит' ) ,
E ( 'superpowers' , 'sk_coderev' , 'содержит' ) ,
E ( 'superpowers' , 'sk_elements' , 'содержит' ) ,
// ── СКИЛЫ вызывают друг друга ───────────────────
E ( 'sk_brainstorm' , 'sk_wplans' , 'вызывает\nпосле дизайна' ) ,
E ( 'sk_wplans' , 'sk_eplans' , 'вызывает\nдля выполнения' ) ,
E ( 'sk_wplans' , 'sk_subagent' , 'альтернатива\nexecuting-plans' ) ,
E ( 'sk_subagent' , 'ag_explore' , 'запускает\nдля поиска' ) ,
E ( 'sk_subagent' , 'ag_general' , 'запускает\nдля задач' ) ,
E ( 'sk_subagent' , 'ag_plan' , 'запускает\nдля архитектуры' ) ,
E ( 'sk_parallel' , 'sk_worktree' , 'использует\nдля изоляции' ) ,
// ── СКИЛЫ ПРОЕКТА ───────────────────────────────
E ( 'sk_rls' , 'tooling' , 'использует\nsquawk + grep §3.2' ) ,
E ( 'sk_rls' , 'mcp_boost' , 'SQL запросы\nк схеме' ) ,
E ( 'sk_qitem' , 'claude_md_mgmt' , 'делегирует\nправку CLAUDE.md' ) ,
// ── CLAUDE-MD-MGMT ──────────────────────────────
E ( 'claude_md_mgmt' , 'claude_md' , 'единственный\nканал правок' ) ,
// ── HOOKIFY ─────────────────────────────────────
E ( 'ag_hookify' , 'hookify_plugin' , 'передаёт\nанализ' ) ,
E ( 'hookify_plugin' , 'hk_pre_claude' , 'может создавать\nновые хуки' ) ,
E ( 'hookify_plugin' , 'hk_economy' , 'может создавать\nновые хуки' ) ,
// ── АГЕНТЫ используют MCP ───────────────────────
E ( 'ag_pest' , 'mcp_redis' , 'читает\nочереди/кэш' ) ,
E ( 'ag_rls' , 'mcp_boost' , 'SQL запросы\nк БД' ) ,
E ( 'ag_guide' , 'mcp_gh' , 'ищет\nв репозитории' ) ,
// ── LEFTHOOK вызывается git ──────────────────────
E ( 'lh_gitleaks' , 'mem_plugins' , 'блокирует коммит\nпри ПДн в staged' ) ,
E ( 'lh_larastan' , 'mcp_boost' , 'Boost даёт\nконтекст типов' ) ,
E ( 'lh_squawk' , 'tooling' , 'соответствует\n§3.2 #15' ) ,
E ( 'lh_gitleaks2' , 'lh_gitleaks' , 'строже:\nвся история' ) ,
E ( 'lh_lychee' , 'claude_md' , 'проверяет\nссылки в .md' ) ,
// ── MEMORY читается Claude ──────────────────────
E ( 'mem_env' , 'ag_pest' , 'квирки 72/77\nиспользует агент' ) ,
E ( 'mem_plugins' , 'psr_v1' , 'отражает\nтекущие версии' ) ,
E ( 'mem_archive' , 'claude_md' , 'синхронизирует\nверсии доков' ) ,
// ── MCP ─────────────────────────────────────────
E ( 'mcp_pw' , 'hk_session' , 'используется\nдля a11y smoke' ) ,
E ( 'mcp_gh' , 'sk_pr' , 'PR, issues\nпри finishing-pr' ) ,
E ( 'mcp_boost' , 'ag_rls' , 'схема БД\nдля RLS-review' ) ,
// ══════════════════════════════════════════════════
2026-05-14 12:59:25 +03:00
// КОНФЛИКТЫ — 3-color classification (iter2 §4)
// 🔴 не закрыт правилом / ⚫ возник на практике / 🟢 закрыт правилом
2026-05-13 17:05:59 +03:00
// ══════════════════════════════════════════════════
2026-05-14 12:59:25 +03:00
CONFLICT ( 'sk_rls' , 'ag_rls' , 'RLS compliance: оба покрывают, нет регламента' , 'RED' ) ,
CONFLICT ( 'hookify_plugin' , 'hk_pre_claude' , 'hookify может перезаписать существующий хук' , 'RED' ) ,
CONFLICT ( 'mcp_pw' , 'sk_parallel' , 'Browser is already in use (квирк #2)' , 'BLACK' ) ,
2026-05-14 17:15:39 +03:00
CONFLICT ( 'ag_pest' , 'mcp_redis' , 'Гонка в Redis при Pest --parallel из подкаталога (квирк 72)' , 'BLACK' ) ,
2026-05-14 12:59:25 +03:00
CONFLICT ( 'psr_v1' , 'claude_md' , 'Закрыто §5п.10 CLAUDE.md + хук CLAUDE.md-warn' , 'GREEN' ) ,
CONFLICT ( 'upm' , 'fd_plugin' , 'PSR_v1 R14.5: не параллельно' , 'GREEN' ) ,
CONFLICT ( 'mcp_21st' , 'fd_plugin' , 'PSR_v1 R14.5: не параллельно' , 'GREEN' ) ,
CONFLICT ( 'hk_economy' , 'superpowers' , 'Pravila §12.4: §12 неотменяем' , 'GREEN' ) ,
2026-05-13 17:05:59 +03:00
] ;
// ════════════════════════════════════════════════════
// SECTION 3: NODE DETAILS
// ════════════════════════════════════════════════════
const CATEGORY _LABELS = {
rules : 'Правило' , plugins : 'Плагин' , skills _sp : 'Скил Superpowers' ,
skills _proj : 'Скил проекта' , hooks : 'Хук .claude' , agents : 'Агент' ,
mcp : 'MCP-сервер' , lefthook : 'Lefthook job' , memory : 'Memory-файл'
} ;
2026-05-14 09:10:57 +03:00
function nd ( desc , when , limits , reportsTo , manages , together , conflicts ) {
// Backward-compat: old 5-arg signature was nd(desc, reportsTo, manages, together, conflicts).
// If 2nd arg is an array, treat as old-style call and shift args.
if ( Array . isArray ( when ) ) {
return {
desc ,
when : '' ,
limits : '' ,
reportsTo : when ,
manages : limits ,
together : reportsTo ,
conflicts : ( manages || [ ] ) ,
} ;
}
return { desc , when : when || '' , limits : limits || '' , reportsTo , manages , together , conflicts : conflicts || [ ] } ;
2026-05-13 17:05:59 +03:00
}
const NODE _DETAILS = {
// ── ПРАВИЛА ──────────────────────────────────────
pravila : nd (
2026-05-14 12:29:21 +03:00
'Главный свод правил работы Клода — кто чем командует, что запрещено, какие обязательные действия.' ,
'Действует всегда — Клод читает его при старте каждой сессии.' ,
'Правило §12 (обязательные скилы) нельзя отменить — даже режимом экономии или просьбой «не используй сейчас». Расходимость с другими документами — нарушение §7.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[
2026-05-14 12:29:21 +03:00
{ name : 'CLAUDE.md' , cond : 'подчинён, уровень 2a в цепочке приоритетов' } ,
{ name : 'PSR_v1' , cond : 'подчинён, уровень 3 в цепочке приоритетов' } ,
2026-05-14 17:15:39 +03:00
{ name : 'плагин Superpowers' , cond : '§12 обязывает запускать скил первым' } ,
2026-05-13 17:05:59 +03:00
{ name : 'Все компоненты' , cond : 'через цепочку приоритетов §1' }
] ,
[ { name : 'CLAUDE.md' , cond : 'оба читаются при старте сессии' } ]
) ,
claude _md : nd (
2026-05-14 17:15:39 +03:00
'Оперативная карта проекта — список технологий, команд, фаз (нулевая фаза — документация и прототипы; первая — старт backend; вторая — старт frontend; третья — перед запуском в боевую среду), ссылок на документы.' ,
2026-05-14 12:29:21 +03:00
'Читается при старте каждой сессии; обновляется при новом инструменте или новой фазе.' ,
'Править можно только через скил `/claude-md-management:claude-md-improver` или `:revise-claude-md` (правило §5 п.10). Прямые Edit/Write блокируются хуком предупреждения.' ,
[ { name : 'Pravila' , cond : 'всегда подчинён (уровень 2a)' } ] ,
2026-05-13 17:05:59 +03:00
[
{ name : 'Tooling v1.17' , cond : 'ссылается как на реестр инструментов' } ,
2026-05-14 17:15:39 +03:00
{ name : 'плагин claude-md-management' , cond : 'правило §5 п.10 — единственный канал правок' }
2026-05-13 17:05:59 +03:00
] ,
[
2026-05-14 12:29:21 +03:00
{ name : 'Pravila' , cond : 'оба читаются при старте сессии' } ,
{ name : 'Tooling' , cond : 'оба — оперативные карты уровня 2' }
2026-05-13 17:05:59 +03:00
] ,
2026-05-14 12:59:25 +03:00
[ { name : 'PSR_v1' , desc : 'Правило §5 п.10 запрещает прямые правки, но PSR_v1 это явно не повторяет — есть риск Edit без скила' , type : 'GREEN' } ]
2026-05-13 17:05:59 +03:00
) ,
psr _v1 : nd (
2026-05-14 12:29:21 +03:00
'Правила совместной работы плагинов — кто с кем работает, какая процедура обязательна.' ,
2026-05-14 17:15:39 +03:00
'При выборе UI-инструмента (плагин Frontend Design против плагина UI UX Pro Max против MCP-сервера 21st Magic), при координации парных плагинов, при включении дополнительного MCP (внешнего сервиса-инструмента Claude) вне основных фаз.' ,
'Обязательное правило R14.5: плагины UI UX Pro Max, 21st Magic, Frontend Design — нельзя использовать одновременно. Обязательное правило R6.0 (фильтр стека) и R6.1 (палитра Forest) — нужно соблюдать при UI-выводе плагинов.' ,
2026-05-14 12:29:21 +03:00
[ { name : 'Pravila' , cond : 'подчинён, уровень 3 в цепочке' } ] ,
2026-05-13 17:05:59 +03:00
[
2026-05-14 17:15:39 +03:00
{ name : 'плагин Superpowers + плагин Frontend Design' , cond : 'координирует как пару плагинов' } ,
{ name : 'плагин UI UX Pro Max' , cond : 'R14.3: включается только через процедуру' } ,
{ name : 'MCP-сервер 21st Magic' , cond : 'R14.4: включается только через процедуру' }
2026-05-13 17:05:59 +03:00
] ,
2026-05-14 12:29:21 +03:00
[ { name : 'CLAUDE.md' , cond : 'обе — оперативные карты, правятся согласованно' } ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'CLAUDE.md' , desc : 'CLAUDE.md §5 п.10 требует править только через скил claude-md-management, а PSR_v1 это ограничение не повторяет — риск прямых Edit' , type : 'GREEN' } ]
2026-05-13 17:05:59 +03:00
) ,
tooling : nd (
'Реестр 35 инструментов — когда что использовать, команды установки, конфликты.' ,
2026-05-14 17:15:39 +03:00
'При выборе инструмента для фазы (нулевая документация / первая backend / вторая frontend / третья перед запуском в боевую среду), при добавлении нового инструмента, при обновлении версий.' ,
2026-05-14 12:29:21 +03:00
'При прямом конфликте с CLAUDE.md побеждает CLAUDE.md (оперативная карта уровня 2a). Любая правка требует синхронизации с CLAUDE.md §3.' ,
2026-05-13 17:05:59 +03:00
[
2026-05-14 12:29:21 +03:00
{ name : 'Pravila' , cond : 'уровень 2b — оперативная карта рядом с CLAUDE.md' } ,
{ name : 'CLAUDE.md' , cond : 'при прямом конфликте побеждает CLAUDE.md' }
2026-05-13 17:05:59 +03:00
] ,
[ ] ,
2026-05-14 12:29:21 +03:00
[ { name : 'CLAUDE.md' , cond : 'обе — оперативные карты, правятся синхронно' } ]
2026-05-13 17:05:59 +03:00
) ,
// ── ПЛАГИНЫ ──────────────────────────────────────
superpowers : nd (
2026-05-14 12:29:21 +03:00
'Плагин поведения Клода — 14 скилов для тестов, отладки, планирования, параллельной работы.' ,
2026-05-14 17:15:39 +03:00
'При творческих, отладочных, тестовых и многошаговых задачах: скил brainstorming (продумать варианты) / скил TDD (разработка через тесты — failing test first) / скил systematic-debugging / скил verification-before-completion (обязательная проверка готовности) / скил writing-plans / скил parallel-work / скил worktree / скил finishing-PR (запрос на слияние кода) / скил subagent-driven-development / скил writing-skills (карта типов в §12.2 Pravila).' ,
2026-05-14 12:29:21 +03:00
'Обязательное правило §12: единственная отмена — явная просьба заказчика «не используй superpowers сейчас» на текущее действие. Раздел §9 «Отступления» к §12 не применяется.' ,
2026-05-13 17:05:59 +03:00
[
2026-05-14 12:29:21 +03:00
{ name : 'Pravila §12' , cond : 'обязательное правило: скил запускается первым' } ,
2026-05-14 17:15:39 +03:00
{ name : 'PSR_v1' , cond : 'координирует как пару с плагином Frontend Design' }
2026-05-13 17:05:59 +03:00
] ,
2026-05-14 17:15:39 +03:00
[ { name : 'Все 14 скилов Superpowers' , cond : 'содержит' } ] ,
[ { name : 'плагин Frontend Design' , cond : 'пара — работают вместе при UI-задачах' } ] ,
[ { name : 'хук economy-mode' , desc : 'Режим экономии 100% теоретически может «сэкономить» запуск скила и нарушить обязательное правило §12 (§12 нельзя отменить)' , type : 'GREEN' } ]
2026-05-13 17:05:59 +03:00
) ,
fd _plugin : nd (
2026-05-14 17:15:39 +03:00
'Плагин знаний о UI — Vue, Vuetify, доступность (accessibility), паттерны компонентов для Лидерры.' ,
'При UI/UX задачах — компоненты, экраны, паттерны взаимодействия; в паре с плагином Superpowers (даёт процесс).' ,
2026-05-14 12:29:21 +03:00
'Фильтр стека R6.0: срезать React/Tailwind/shadcn/JSX в Vue 3 + Vuetify 3. Обязательное правило палитры Forest R6.1 для цветов, шрифтов и иконок.' ,
[ { name : 'PSR_v1' , cond : 'R5: подчинён как часть пары плагинов' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'пара — Frontend Design даёт UI-знания, Superpowers даёт процесс' } ] ,
2026-05-13 17:05:59 +03:00
[
2026-05-14 17:15:39 +03:00
{ name : 'плагин UI UX Pro Max' , desc : 'Правило PSR_v1 R14.5: нельзя одновременно — оба включены в настройках, но должны чередоваться' , type : 'GREEN' } ,
{ name : 'MCP-сервер 21st Magic' , desc : 'Правило PSR_v1 R14.5: нельзя одновременно с Frontend Design — оба потенциально доступны' , type : 'GREEN' }
2026-05-13 17:05:59 +03:00
]
) ,
upm : nd (
2026-05-14 12:29:21 +03:00
'Резервная библиотека UI — 50+ стилей, 161 палитра, 99 правил-подсказок UX. Только по процедуре.' ,
2026-05-14 17:15:39 +03:00
'Только по процедуре PSR_v1 R14.3: запасной вариант к плагину Frontend Design ИЛИ «третий вариант» в архитектурном решении.' ,
'R14.5: нельзя одновременно с плагином Frontend Design / MCP-сервером 21st Magic. Фильтр стека R6.0 и обязательное правило палитры Forest R6.1 — обязательны. Проверка доступности Pa11y (автопроверка accessibility — доступности) перед выкаткой.' ,
2026-05-14 12:29:21 +03:00
[ { name : 'PSR_v1' , cond : 'R14.3: включается только через процедуру, не сам по себе' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Frontend Design' , desc : 'Правило PSR_v1 R14.5: нельзя одновременно — UI UX Pro Max как материал, Frontend Design как решатель; риск смешать роли' , type : 'GREEN' } ]
2026-05-13 17:05:59 +03:00
) ,
claude _md _mgmt : nd (
2026-05-14 12:29:21 +03:00
'Единственный разрешённый способ править CLAUDE.md — через скил claude-md-improver или revise-claude-md.' ,
'При любой правке CLAUDE.md (новая фаза, новый инструмент, смена версии, новые особенности — всё через скил).' ,
'Правило PSR_v1 R10.1 блок 1 (инфраструктурная категория). Внутри процедуры продолжают действовать §4 правил Клода (синхронизация Pravila + Tooling).' ,
2026-05-13 17:05:59 +03:00
[ { name : 'PSR_v1' , cond : 'R10.1 Блок 1: инфраструктурная категория' } ] ,
2026-05-14 12:29:21 +03:00
[ { name : 'CLAUDE.md' , cond : 'правило §5 п.10: единственный канал правок' } ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил q-item-add' , cond : 'скил делегирует правки CLAUDE.md через этот плагин' } ]
2026-05-13 17:05:59 +03:00
) ,
hookify _plugin : nd (
2026-05-14 12:29:21 +03:00
'Плагин создания хуков — анализирует разговоры и предлагает новые автоматизации в виде хуков.' ,
'При запросе «давай повесим хук на это поведение» или после серии повторяющихся ошибок — анализ через агента conversation-analyzer.' ,
'Правило PSR_v1 R10.1. Новые хуки могут конфликтовать с существующими (см. конфликты ниже) — обязательная проверка файла настроек до создания.' ,
2026-05-13 17:05:59 +03:00
[ { name : 'PSR_v1' , cond : 'R10.1: формализован' } ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'агент hookify:conversation-analyzer' , cond : 'запускает анализ разговоров' } ] ,
[ { name : 'агент hookify:conversation-analyzer' , cond : 'плагин и агент работают в паре' } ] ,
2026-05-13 17:05:59 +03:00
[
2026-05-14 17:15:39 +03:00
{ name : 'хук pre-claude-warn' , desc : 'плагин hookify создаёт новые хуки PreToolUse на лету — может перезаписать или конкурировать с этим хуком' , type : 'RED' }
2026-05-13 17:05:59 +03:00
]
) ,
// ── СКИЛЫ SUPERPOWERS ────────────────────────────
sk _brainstorm : nd (
'Продумывает задачу вместе с заказчиком, формулирует варианты A/B/C и согласует дизайн до написания кода.' ,
2026-05-14 12:42:32 +03:00
'При творческой задаче — новая фича, непростой рефакторинг, дизайн-решение; ДО любого кода или плана.' ,
2026-05-14 17:15:39 +03:00
'Обязательно явное «Approved» от заказчика до перехода к скилу writing-plans. Запрет: нельзя запускать скилы реализации до утверждения дизайна.' ,
[ { name : 'плагин Superpowers' , cond : 'содержит' } , { name : 'Pravila §12' , cond : 'обязательное правило (см. §12) для творческих задач' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил writing-plans' , cond : 'вызывается сразу после brainstorming для создания плана' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _tdd : nd (
2026-05-14 17:15:39 +03:00
'Ведёт разработку через написание падающего теста до кода: сначала RED (тест провален), потом GREEN (тест проходит). TDD (разработка через тесты — failing test first).' ,
2026-05-14 12:42:32 +03:00
'При любом новом боевом коде — backend (Pest) и frontend (Vitest).' ,
'Падающий тест пишется ДО реализации; формулировка «код должен работать» без проверенного теста — нарушение правила §12.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } , { name : 'Pravila §12' , cond : 'обязательное правило (см. §12) для любого нового кода' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил executing-plans' , cond : 'TDD встроен в каждый шаг плана выполнения' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _debug : nd (
2026-05-14 12:42:32 +03:00
'Системная отладка: минимум 3 гипотезы, опровержение каждой реальной проверкой до того, как править код — никаких «должно работать».' ,
'При неожиданном поведении, падении теста, ошибке во время работы, неожиданном выводе.' ,
'Минимум 3 гипотезы. Опровержение через реальные команды и тесты, а не «логика подсказывает». Никаких «попробую исправить» без подтверждённой причины.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } , { name : 'Pravila §12' , cond : 'обязательное правило (см. §12) при неожиданном поведении' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер redis' , cond : 'используется для отладки очередей Redis' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _wplans : nd (
'Создаёт детальный план реализации с полным кодом, командами и шагами по 2-5 минут.' ,
2026-05-14 17:15:39 +03:00
'После скила brainstorming (творческая задача) или сразу для многошаговой (≥3 шагов) технической задачи.' ,
2026-05-14 12:42:32 +03:00
'Никаких заглушек (TBD, TODO, «add validation»). Каждый шаг — реальный код или команда. Покрытие спецификации обязательно.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } , { name : 'Pravila §12' , cond : 'обязательное правило (см. §12) для многошаговых задач (≥3 шагов)' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил executing-plans или скил subagent-driven' , cond : 'план передаётся в один из них для выполнения' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _eplans : nd (
'Выполняет готовый план шаг за шагом, отмечает чекбоксы, делает коммиты после каждого шага.' ,
2026-05-14 17:15:39 +03:00
'После скила writing-plans (если выбрано выполнение в текущей сессии); альтернатива — скил subagent-driven-development в той же сессии.' ,
2026-05-14 12:42:32 +03:00
'Каждый шаг отмечается галочкой, коммиты не объединяются — атомарно по одному за шаг (Pravila §4.2).' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил writing-plans' , cond : 'получает план от writing-plans' } , { name : 'скил subagent-driven-development' , cond : 'альтернатива — зависит от выбора пользователя' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _verify : nd (
2026-05-14 12:42:32 +03:00
'Обязательная проверка перед заявлением «готово»: запускает тесты, видит реальный вывод, никаких предположений.' ,
'Перед любым заявлением «готово»/«passed»/«closed»/«merged» — обязательно (правило §12 + экономия 0% как жёсткое требование).' ,
'Реальный запуск, не «должно пройти». Вывод тестов виден полностью. Выборочные результаты запрещены («tests pass» = ровно столько, сколько действительно прошло).' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } , { name : 'Pravila §12' , cond : 'обязательное правило (см. §12) перед любым заявлением «готово»' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер laravel-boost' , cond : 'запросы к БД для проверки данных' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _parallel : nd (
2026-05-14 12:42:32 +03:00
'Разбивает независимые задачи на параллельные потоки с изоляцией через git worktrees (отдельные рабочие копии репозитория).' ,
'При нескольких независимых рабочих фронтах одновременно (CTO-задача + Plan-задача + audit-fix).' ,
'Изоляция через worktree обязательна — никакого «работаю в одной директории на 3 ветках сразу». Иначе риск конфликта файлов и веток.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил worktree' , cond : 'parallel-work использует worktree для изоляции' } ] ,
[ { name : 'MCP-сервер playwright' , desc : 'Браузер уже занят (Browser is already in use) при одновременном запуске нескольких сессий через worktree' , type : 'BLACK' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _worktree : nd (
2026-05-14 12:42:32 +03:00
'Создаёт изолированную копию репозитория (worktree) для рискованной или параллельной работы.' ,
'При параллельной работе нескольких задач или при рискованной работе (рефакторинг с возможным откатом, ветка экспериментов).' ,
'Очистка через ExitWorktree обязательна. Не оставлять забытые worktree — захламляют файловую систему.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил parallel-work' , cond : 'worktree — инструмент для parallel-work' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _pr : nd (
2026-05-14 17:15:39 +03:00
'Чеклист финальной готовности PR (запроса на слияние кода): тесты, документация, чистота кода, проверки перед push.' ,
2026-05-14 12:42:32 +03:00
'Перед `gh pr create` или `git push` в общую ветку — обязательная проверка готовности.' ,
2026-05-14 17:15:39 +03:00
'Проверки перед push (job gitleaks в lefthook по всей истории + job lychee) — не обходить через `--no-verify`. Pravila §4.2.' ,
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер github' , cond : 'создаёт PR (запрос на слияние кода) через GitHub' } , { name : 'lefthook (job-набор перед push)' , cond : 'запускает job gitleaks + job lychee' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _subagent : nd (
'Запускает суб-агентов для крупных задач — каждый в отдельном контексте без накопленного шума.' ,
2026-05-14 12:42:32 +03:00
'При выполнении плана в той же сессии ИЛИ при делегировании поиска/анализа большого объёма.' ,
'Суб-агент в режиме экономии 0%: запрашивать полный сырой вывод, а не сводку — решения принимать самому.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[
2026-05-14 17:15:39 +03:00
{ name : 'агент Explore' , cond : 'запускает для поиска по кодовой базе' } ,
{ name : 'агент general-purpose' , cond : 'запускает для сложных задач' } ,
{ name : 'агент Plan' , cond : 'запускает для архитектурного планирования' }
2026-05-13 17:05:59 +03:00
] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил writing-plans' , cond : 'subagent-driven — основной способ выполнения планов' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _wskills : nd (
2026-05-14 12:42:32 +03:00
'Создаёт новые скилы по стандартному шаблону: файл SKILL.md, заголовочный блок (frontmatter), описание процесса.' ,
'При формализации повторяющегося процесса в переиспользуемый скил (после 2-3 примеров одинаковой работы).' ,
'Шаблон SKILL.md, заголовочный блок (name, description, when_to_use, allowed-tools), описание процесса с DOT-диаграммой.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'агент plugin-dev:skill-reviewer' , cond : 'агент проверяет созданный скил' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _spreview : nd (
2026-05-14 12:42:32 +03:00
'Проверяет документ-спецификацию на полноту, противоречия, заглушки и объём работ.' ,
2026-05-14 17:15:39 +03:00
'После скила writing-plans на отдельном этапе ДО реализации — для крупных планов с несколькими задачами.' ,
'Самопроверка прямо в скиле brainstorming достаточна для небольшой спецификации; для крупных — отдельный запуск этого скила.' ,
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил brainstorming' , cond : 'spec-review вызывается в конце brainstorming после записи спека' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _coderev : nd (
2026-05-14 12:42:32 +03:00
'Систематический разбор кода — безопасность, тесты, архитектура, соответствие правилам.' ,
2026-05-14 17:15:39 +03:00
'Перед слиянием PR (запроса на слияние кода); после крупной серии коммитов; при подготовке к релизу; при подозрении на регрессию (возврат к старому багу).' ,
'Без выборочности: разбор всех изменений, а не только подозрительных. SAST (статический анализ кода на уязвимости, через MCP-сервер semgrep) включается обязательно.' ,
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер semgrep' , cond : 'SAST-проверка при ревью кода' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _elements : nd (
'Улучшает написание текстов и документации — ясность, лаконичность, без воды.' ,
2026-05-14 17:15:39 +03:00
'При написании спецификации/плана/CHANGELOG/описания PR (запроса на слияние кода) — для общения с командой.' ,
2026-05-14 12:42:32 +03:00
'Без воды. Без «легко», «просто», «всего лишь». Каждое утверждение измеримо.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers' , cond : 'содержит' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
// ── СКИЛЫ ПРОЕКТА ────────────────────────────────
sk _rls : nd (
2026-05-14 17:15:39 +03:00
'7-шаговый чеклист RLS (защита строк по тенанту) для новой таблицы: tenant_id, ENABLE RLS (включение защиты строк), политики, права для 5 ролей, CHANGELOG, проверка через squawk, дымовой тест (быстрая проверка функциональности).' ,
'При создании новой таблицы в db/schema.sql ИЛИ при правках существующих политик RLS (защиты строк по тенанту).' ,
'Права для 5 ролей обязательны (crm_app_user / crm_app_admin / crm_supplier_worker BYPASSRLS (право обходить защиту строк — для системных задач) / crm_readonly / crm_migrator). Запись в CHANGELOG_schema.md обязательна.' ,
2026-05-13 17:05:59 +03:00
[ { name : 'Tooling §3.2' , cond : 'использует squawk (#15) и команды grep' } ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер laravel-boost' , cond : 'SQL запросы к schema.sql для проверки' } ] ,
[ { name : 'агент rls-reviewer' , desc : 'оба проверяют соответствие политик RLS (защиты строк по тенанту) — скил для ручной проверки таблицы, агент для разбора PR (запроса на слияние кода) и diff; нет чёткой границы когда какой' , type : 'RED' } ]
2026-05-13 17:05:59 +03:00
) ,
sk _qitem : nd (
'Добавляет новый открытый вопрос в реестр Открытые_вопросы_v8_3.md с обновлением счётчиков §0 и версии.' ,
2026-05-14 12:42:32 +03:00
'При появлении нового открытого вопроса (Биз-/CTO-/Ю-/Диз-/DO-/OPEN-) — формальная запись в реестр.' ,
'Категория (Биз-/CTO-/...) обязательна. Связанные документы (CLAUDE.md/Pravila/PSR_v1/Tooling) — синхронизируются.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин claude-md-management' , cond : 'скил делегирует правку CLAUDE.md через плагин (правило §5п.10 в нормативке)' } ]
2026-05-13 17:05:59 +03:00
) ,
// ── ХУКИ ─────────────────────────────────────────
hk _pre _claude : nd (
'Блокирует прямое редактирование CLAUDE.md — срабатывает на Edit/Write по этому файлу.' ,
2026-05-14 17:15:39 +03:00
'PreToolUse (перед каждым вызовом инструмента) — перед каждым Edit/Write, фильтр путей нацелен на CLAUDE.md.' ,
'Обход запрещён. Единственный способ редактировать — плагин claude-md-management (правило §5п.10 в нормативке).' ,
2026-05-14 12:42:32 +03:00
[ { name : '.claude/settings.json' , cond : 'описан как хук PreToolUse' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин hookify' , desc : 'плагин hookify динамически создаёт новые хуки PreToolUse — может перезаписать или конкурировать с этим хуком' , type : 'RED' } ]
2026-05-13 17:05:59 +03:00
) ,
hk _post _md : nd (
2026-05-14 12:42:32 +03:00
'После каждого Edit .md-файла запускает markdownlint --fix автоматически.' ,
2026-05-14 17:15:39 +03:00
'PostToolUse (после каждого вызова инструмента) — после Edit/Write на *.md (кроме корневого CLAUDE.md, чтобы не зациклить).' ,
2026-05-14 12:42:32 +03:00
'Не правит CLAUDE.md (исключён из фильтра путей). При неисправимой ошибке (например, битая ссылка) — предупреждение, не блокировка.' ,
[ { name : '.claude/settings.json' , cond : 'описан как хук PostToolUse' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'job markdownlint в lefthook' , cond : 'дублируют задачу: хук — в сессии, lefthook — при коммите' } ]
2026-05-13 17:05:59 +03:00
) ,
hk _post _schema : nd (
'После правки db/schema.sql напоминает обновить db/CHANGELOG_schema.md.' ,
2026-05-14 17:15:39 +03:00
'PostToolUse (после каждого вызова инструмента) — после Edit/Write на `db/schema.sql`.' ,
2026-05-14 12:42:32 +03:00
'Напоминание, не блокировка. Дисциплина ведения CHANGELOG_schema — на разработчике (§4.2 Pravila).' ,
[ { name : '.claude/settings.json' , cond : 'описан как хук PostToolUse' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'job squawk в lefthook' , cond : 'оба реагируют на изменения SQL' } ]
2026-05-13 17:05:59 +03:00
) ,
hk _session : nd (
2026-05-14 12:42:32 +03:00
'При старте каждой сессии подгружает CLAUDE.md, Pravila и ключевые memory-файлы в контекст.' ,
2026-05-14 17:15:39 +03:00
'SessionStart (при старте сессии) — единожды при инициализации сессии Claude Code.' ,
2026-05-14 12:42:32 +03:00
'Список memory-файлов фиксированный — для расширения править настройку хука. Не читает 80+ квирков целиком — выборочно по релевантности.' ,
[ { name : '.claude/settings.json' , cond : 'описан как хук SessionStart' } ] ,
2026-05-13 17:05:59 +03:00
[
2026-05-14 17:15:39 +03:00
{ name : 'память user_profile' , cond : 'читает' } ,
{ name : 'память feedback_environment' , cond : 'читает' } ,
{ name : 'память project_state' , cond : 'читает' } ,
{ name : 'память feedback_superpowers_hard_rule' , cond : 'читает' } ,
{ name : 'память feedback_plugin_paired_stack' , cond : 'читает' }
2026-05-13 17:05:59 +03:00
] ,
[ ]
) ,
hk _economy : nd (
2026-05-14 12:42:32 +03:00
'Перед каждым промптом разбирает «экономия X%» и выставляет режим строгости (0% = максимальное качество, 100% = по умолчанию).' ,
2026-05-14 17:15:39 +03:00
'UserPromptSubmit (перед отправкой промпта пользователя) — ищет шаблон /экономия\\s*(\\d+)%/.' ,
2026-05-14 12:42:32 +03:00
'Правило §12 **НЕ** отменяется этим режимом ни на каком уровне. Действует только на текущую задачу — следующий промпт разбирается заново.' ,
[ { name : '.claude/settings.json' , cond : 'описан как хук UserPromptSubmit' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин Superpowers (§12)' , desc : 'Экономия=100% теоретически может «сэкономить» вызов скила, нарушая обязательное правило (см. §12) — §12 неотменяем, экономия его не отменяет' , type : 'GREEN' } ]
2026-05-13 17:05:59 +03:00
) ,
// ── АГЕНТЫ ───────────────────────────────────────
ag _pest : nd (
2026-05-14 12:47:10 +03:00
'Разбирает падения тестов Pest --parallel: отличает настоящую ошибку от одного из пяти известных квирков (72/73/77...).' ,
2026-05-14 17:15:39 +03:00
'При падении Pest --parallel ИЛИ при дымовом тесте (быстрой проверке функциональности) только из подкаталога (как в аудите Phase 3 SyncSupplierProjectsJobTest).' ,
'READ-ONLY (только чтение — только читает код, ничего не правит). Каждую гипотезу подтверждает реальным запуском, а не «похоже на квирк».' ,
2026-05-13 17:05:59 +03:00
[ { name : 'CLAUDE.md §6' , cond : 'описывает когда вызывать' } ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер redis' , cond : 'читает Redis для отладки квирка 72 (гонка supplier:session)' } ] ,
[ { name : 'MCP-сервер redis' , desc : 'Pest --parallel — гонка (race condition) с кэшем Redis при запуске из подкаталога (квирк 72)' , type : 'BLACK' } ]
2026-05-13 17:05:59 +03:00
) ,
ag _rls : nd (
2026-05-14 12:47:10 +03:00
'Проверяет миграции на соответствие RLS (защите строк по тенанту) — 7 пунктов чеклиста с реальными командами, только чтение.' ,
2026-05-14 17:15:39 +03:00
'При создании/правке миграции в db/migrations/ ИЛИ при правке db/schema.sql ИЛИ при ревью PR (запроса на слияние кода) с изменениями БД.' ,
'READ-ONLY (только чтение — только Read/Grep/Glob/Bash) — код не пишет. Не замена скилу rls-check — у них разные сценарии.' ,
2026-05-14 12:47:10 +03:00
[ { name : 'CLAUDE.md' , cond : 'описывает в §6 и в директории агентов' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер laravel-boost' , cond : 'SQL запросы к db/schema.sql' } ] ,
[ { name : 'скил rls-check' , desc : 'оба покрывают соответствие RLS (защиты строк по тенанту), чёткой границы нет: агент — для PR (запроса на слияние кода) и diff, скил — для ручной проверки таблицы' , type : 'RED' } ]
2026-05-13 17:05:59 +03:00
) ,
ag _statusline : nd (
2026-05-14 12:47:10 +03:00
'Настраивает строку состояния Claude Code через правку файла настроек.' ,
2026-05-14 17:15:39 +03:00
'При запросе пользователя «настрой строку состояния» — редкая разовая задача.' ,
2026-05-14 12:47:10 +03:00
'Правит только секцию statusline в settings.json, другие части файла не трогает.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
ag _guide : nd (
2026-05-14 17:15:39 +03:00
'Отвечает на вопросы про API Claude Code, SDK, MCP-серверы (внешние сервисы-инструменты Claude), хуки, slash-команды.' ,
2026-05-14 09:17:37 +03:00
'При вопросе про возможности Claude Code/SDK/API — «Can Claude...», «How do I...», «Does Claude...».' ,
2026-05-14 17:15:39 +03:00
'READ-ONLY (только чтение): ищет в документации, код не правит. Не для отладки кода — только вопросы о платформе.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'MCP-сервер github' , cond : 'при необходимости ищет примеры в репозитории' } ]
2026-05-13 17:05:59 +03:00
) ,
ag _explore : nd (
2026-05-14 12:47:10 +03:00
'Быстрый поиск файлов по шаблону имени или по символу — только чтение, без анализа.' ,
'При точечном поиске — найти файл по имени или сделать grep по символу/ключевому слову.' ,
'Не для свободного исследования. Не для ревью/аудита (читает отрывки, теряет контекст за пределами окна чтения).' ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил subagent-driven-development' , cond : 'запускается для задач поиска' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
ag _general : nd (
2026-05-14 12:47:10 +03:00
'Универсальный агент для сложных многошаговых исследований — с полным набором инструментов.' ,
2026-05-14 17:15:39 +03:00
'При сложных многошаговых задачах исследования или написания кода, когда агента Explore не хватает (нужен анализ, не только поиск).' ,
2026-05-14 12:47:10 +03:00
'Полный набор инструментов — может писать код. Дороже Explore по токенам.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил subagent-driven-development' , cond : 'запускается для основных задач' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
ag _plan : nd (
2026-05-14 12:47:10 +03:00
'Архитектор: разрабатывает планы реализации, находит критичные файлы, разбирает компромиссы.' ,
2026-05-14 17:15:39 +03:00
'При архитектурном планировании задачи из нескольких компонентов (не для мелкого рефакторинга).' ,
'READ-ONLY (только чтение — без Edit/Write/NotebookEdit). Возвращает план, сам его не реализует.' ,
[ { name : 'скил subagent-driven-development' , cond : 'запускается для архитектурных задач' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил writing-plans' , cond : 'агент Plan и скил writing-plans решают похожую задачу в разных контекстах' } ]
2026-05-13 17:05:59 +03:00
) ,
ag _hookify : nd (
2026-05-14 12:47:10 +03:00
'Разбирает транскрипты диалогов и ищет поведение, которое стоит предотвратить хуком.' ,
2026-05-14 09:17:37 +03:00
'При триггере /hookify без аргументов ИЛИ при запросе «look back at this conversation, what mistakes to prevent».' ,
2026-05-14 17:15:39 +03:00
'READ-ONLY (только чтение — только Read+Grep). Рекомендует хуки, сам их не создаёт — передаёт в плагин hookify.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'плагин hookify' , cond : 'агент передаёт рекомендации в плагин' } ]
2026-05-13 17:05:59 +03:00
) ,
ag _pcreator : nd (
2026-05-14 12:47:10 +03:00
'Создаёт настройку новых агентов по описанию от пользователя.' ,
2026-05-14 09:17:37 +03:00
'При запросе «create an agent that...» — генерация agent.md по описанию функциональности.' ,
2026-05-14 17:15:39 +03:00
'Только инструменты Write/Read. Созданного агента сам не проверяет — передаёт в агент plugin-validator.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'агент plugin-dev:plugin-validator' , cond : 'валидатор проверяет созданного агента' } ]
2026-05-13 17:05:59 +03:00
) ,
ag _pvalid : nd (
2026-05-14 12:47:10 +03:00
'Проверяет структуру плагина на корректность — plugin.json, инструменты, манифест.' ,
'После создания/правки plugin.json или компонентов плагина — превентивная проверка.' ,
2026-05-14 17:15:39 +03:00
'READ-ONLY (только чтение — Read/Grep/Glob/Bash). Сам не правит — выдаёт список нарушений.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'агент plugin-dev:agent-creator' , cond : 'валидатор и создатель работают в паре' } ]
2026-05-13 17:05:59 +03:00
) ,
ag _skreview : nd (
2026-05-14 12:47:10 +03:00
'Оценивает качество написанного скила — описание, ход работы, примеры, лучшие практики.' ,
2026-05-14 17:15:39 +03:00
'После создания/правки скила через скил writing-skills — превентивное ревью.' ,
'READ-ONLY (только чтение — Read/Grep/Glob). Не правит — выдаёт рекомендации.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил writing-skills' , cond : 'skill-reviewer проверяет то, что создал writing-skills' } ]
2026-05-13 17:05:59 +03:00
) ,
// ── MCP-СЕРВЕРЫ ──────────────────────────────────
mcp _pw : nd (
2026-05-14 12:47:10 +03:00
'Управляет браузером — снимает скриншоты, кликает, заполняет формы для smoke- и a11y-тестов.' ,
2026-05-14 09:18:45 +03:00
'При визуальной проверке прототипов (фаза 0), при a11y smoke (axe-core), при UI integration smoke.' ,
2026-05-14 12:47:10 +03:00
'Не для боевых пользователей. На сессию один общий браузер — при parallel-work возможны столкновения (см. квирк #2 в memory).' ,
2026-05-13 17:05:59 +03:00
[ { name : 'CLAUDE.md §3.1 #2' , cond : 'активен с фазы 0' } ] ,
[ ] ,
2026-05-14 12:59:25 +03:00
[ { name : 'SessionStart хук' , cond : 'используется для визуальной проверки прототипов' } ] ,
[ { name : 'parallel-work скил' , desc : 'Один shared browser на сессию — конкуренция при параллельной работе через worktrees (memory квирк #2)' , type : 'BLACK' } ]
2026-05-13 17:05:59 +03:00
) ,
mcp _gh : nd (
2026-05-14 12:47:10 +03:00
'GitHub API — читает/создаёт PR, issues, коммиты, ветки в репозитории CoralMinister/lidpotok.' ,
'При работе с PR/issues, при поиске в репозитории, при создании PR через скил finishing-pr.' ,
'Не делать push в main без явного одобрения. Pravila §4: атомарные коммиты, не объединять их через MCP.' ,
2026-05-13 17:05:59 +03:00
[ { name : 'CLAUDE.md §3.1 #3' , cond : 'активен с фазы 0' } ] ,
[ ] ,
[ { name : 'finishing-pr скил' , cond : 'создаёт PR через этот MCP' } ]
) ,
mcp _boost : nd (
2026-05-14 12:47:10 +03:00
'Laravel Boost — SQL-запросы к dev-БД, схема таблиц, журналы ошибок, поиск по документации Laravel.' ,
2026-05-14 17:15:39 +03:00
'С первой фазы (старт backend) и далее — при SQL-запросах, поиске в документации Laravel, работе с моделями Eloquent.' ,
2026-05-14 12:47:10 +03:00
'**READ-ONLY к prod** — `.env.production` не должен попадать в локальный конфиг. Не использовать правила-подсказки Inertia/Livewire/Tailwind/Filament.' ,
[ { name : 'CLAUDE.md §3.2 #10' , cond : 'активен с фазы 1, доступ к prod только на чтение' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ { name : 'ag_rls агент' , cond : 'rls-reviewer использует Boost для SQL' } , { name : 'sk_rls скил' , cond : 'rls-check использует Boost' } ]
) ,
mcp _semgrep : nd (
2026-05-14 12:47:10 +03:00
'SAST (статический анализ кода на уязвимости) — ищет уязвимости, XSS (внедрение JS), SQLi (внедрение SQL), нарушения правил по шаблонам.' ,
2026-05-14 17:15:39 +03:00
'На третьей фазе проекта (перед запуском в боевую среду) — при просмотре кода через скил code-review и при автозапуске проверок CI (continuous integration) перед выпуском новой версии.' ,
2026-05-14 12:47:10 +03:00
'Настройка в .semgrep.yml. Ложные срабатывания документируются прямо в коде.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'CLAUDE.md §3.4 #25' , cond : 'третья фаза — перед запуском в боевую среду' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'скил code-review' , cond : 'MCP-сервер semgrep используется при разборе кода' } ]
2026-05-13 17:05:59 +03:00
) ,
mcp _sentry : nd (
2026-05-14 12:47:10 +03:00
'Читает ошибки из self-hosted Sentry в Yandex Cloud — события, стектрейсы, метрики. READ-ONLY (только чтение).' ,
'При расследовании ошибок боевой среды во время работы (после развёртывания Б-1).' ,
'**READ-ONLY** (org:read/project:read/event:read). Ждёт развёртывания инстанса Sentry по Б-1 (зависит от регистрации ООО).' ,
2026-05-14 17:15:39 +03:00
[ { name : 'CLAUDE.md §3.3 #34' , cond : 'вне основных фаз (для отладки во время работы); ждёт развёртывания Sentry по Б-1' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
mcp _redis : nd (
2026-05-14 12:47:10 +03:00
'Читает Redis/Memurai — ключи, очереди, кэш для отладки гонок (race conditions). СТРОГО READ-ONLY.' ,
'При отладке очередей Redis (Pest --parallel квирк 72), при анализе инвалидации кэша.' ,
'**СТРОГО READ-ONLY** — никаких DEL/FLUSHDB/SET/LPUSH из Claude (только GET/KEYS/LIST). Источник Anthropic устарел — миграция post-MVP.' ,
2026-05-14 17:15:39 +03:00
[ { name : 'CLAUDE.md §3.3 #35' , cond : 'вне основных фаз (для отладки во время работы); PSR_v1 R10.1 блок 3' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 12:59:25 +03:00
[ { name : 'pest-parallel-debugger агент' , cond : 'агент использует для квирка 72 (гонка в Redis)' } ] ,
2026-05-14 17:15:39 +03:00
[ { name : 'агент pest-parallel-debugger' , desc : 'Гонка в Redis при Pest --parallel при запуске из подкаталога (квирк 72)' , type : 'BLACK' } ]
2026-05-13 17:05:59 +03:00
) ,
mcp _21st : nd (
2026-05-14 12:47:10 +03:00
'Генератор стартовых шаблонов UI-компонентов через LLM. Активация только через процедуру R14.4.' ,
'Только через процедуру PSR_v1 R14.4: предпроверка из 9 условий (брендовый App*? есть аналог в Vuetify? есть уже существующий компонент?).' ,
'R14.5: не запускать параллельно с FD/UPM. Обязательны: JSX→Vue, Tailwind→utility, shadcn→Vuetify. Pa11y a11y на готовом виде.' ,
[ { name : 'PSR_v1 R14.4' , cond : 'строгая предпроверка: 9 условий перед активацией' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 12:59:25 +03:00
[ { name : 'Frontend Design' , desc : 'PSR_v1 R14.5: нельзя параллельно — 21st как генератор материала, FD как решатель; риск смешать роли и нарушить R6 (фильтр стека)' , type : 'GREEN' } ]
2026-05-13 17:05:59 +03:00
) ,
// ── LEFTHOOK JOBS ─────────────────────────────────
lh _gitleaks : nd (
2026-05-14 12:53:35 +03:00
'Ищет ПДн (персональные данные), токены и API-ключи в файлах, готовых к коммиту. Если находит — коммит блокируется.' ,
'Перед каждым коммитом — проверяет только те файлы, что добавлены через `git add`.' ,
'Обход через `--no-verify` запрещён (правило §4.2 Pravila). Находка = блокирующая ошибка, не предупреждение. Известные ложные срабатывания — в файл `.gitleaksignore`.' ,
[ { name : 'lefthook.yml' , cond : 'задача №1 в наборе перед коммитом, без параллельного запуска' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'lefthook:gitleaks pre-push' , cond : 'версия для push сканирует всю историю' } ]
2026-05-13 17:05:59 +03:00
) ,
lh _mdlint : nd (
2026-05-14 12:53:35 +03:00
'Проверяет и авто-исправляет стиль файлов Markdown перед коммитом.' ,
'Перед каждым коммитом, когда в нём есть файлы `.md`.' ,
'Настройки в `.markdownlint-cli2.cjs`. Авто-исправление включено (исправленные файлы авто-сохраняются обратно в коммит).' ,
[ { name : 'lefthook.yml' , cond : 'задача №2 в наборе перед коммитом' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'PostToolUse:markdownlint-fix хук' , cond : 'делают одно и то же — хук сразу после правки, lefthook при коммите' } ]
2026-05-13 17:05:59 +03:00
) ,
lh _cspell : nd (
2026-05-14 12:53:35 +03:00
'Проверяет орфографию в `.md` файлах по словарю `cspell-words.txt`.' ,
'Перед каждым коммитом, когда в нём есть файлы `.md`.' ,
'Словарь: `cspell-words.txt`. Кириллица — в нижнем регистре. Не обходить через `--no-verify` — добавлять валидные слова в словарь.' ,
[ { name : 'lefthook.yml' , cond : 'задача №3 в наборе перед коммитом' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
lh _stylelint : nd (
2026-05-14 12:53:35 +03:00
'Линтует CSS в HTML-прототипах (`web/v8/*.html`).' ,
'Перед каждым коммитом, когда в нём есть файлы `.html`/`.css`.' ,
'Настройки Stylelint в `.stylelintrc`. Устаревшие свойства (например `word-break: break-word`) блокируют коммит.' ,
[ { name : 'lefthook.yml' , cond : 'задача №4 в наборе перед коммитом' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
lh _pint : nd (
2026-05-14 12:53:35 +03:00
'Авто-форматирует код PHP по PSR-стандарту (стиль кода PHP). Исправленные файлы авто-сохраняются обратно в коммит.' ,
'Перед каждым коммитом — на каждый файл `.php` в директории `app/`.' ,
'Авто-исправление включено. Настройки в `app/pint.json`.' ,
[ { name : 'lefthook.yml' , cond : 'задача №5 в наборе перед коммитом, корень `app/`' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
lh _larastan : nd (
2026-05-14 12:53:35 +03:00
'Статический анализ PHP (Larastan, уровень L9) — находит ошибки типов выше базового уровня.' ,
'Перед каждым коммитом, когда в нём есть файлы `.php` в `app/`.' ,
'Базовый уровень `phpstan-baseline.neon` зафиксирован — новые ошибки блокируют коммит. Не повышать базовый уровень без обоснования.' ,
[ { name : 'lefthook.yml' , cond : 'задача №6 в наборе перед коммитом, корень `app/`' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'MCP: laravel-boost' , cond : 'Boost даёт контекст типов через IDE-подсказки' } ]
2026-05-13 17:05:59 +03:00
) ,
lh _squawk : nd (
2026-05-14 12:53:35 +03:00
'Линтер SQL-миграций — проверяет безопасные шаблоны (без блокировки таблиц, параллельный CREATE INDEX и т.п.).' ,
'Перед каждым коммитом, когда в нём есть файлы миграций (`database/migrations/*.php` или `db/*.sql`).' ,
'Настройки в `squawk.toml`. Небезопасные миграции (`ALTER TABLE ADD COLUMN NOT NULL DEFAULT`) запрещены без явной метки `-- squawk-ignore`.' ,
[ { name : 'lefthook.yml' , cond : 'задача №7 в наборе перед коммитом, фильтр путей `*.sql`' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'Tooling #15 squawk' , cond : 'соответствует записи §3.2 в Tooling' } ]
2026-05-13 17:05:59 +03:00
) ,
lh _eslint : nd (
2026-05-14 12:53:35 +03:00
'Линтует файлы Vue/TypeScript в `app/resources/js/`.' ,
'Перед каждым коммитом, когда в нём есть файлы `.vue`/`.ts`/`.tsx` в `app/`.' ,
'Flat-config ESLint 10 + plugin-vue 10. Обход через `--no-verify` запрещён. Ошибки блокируют коммит, предупреждения допустимы.' ,
[ { name : 'lefthook.yml' , cond : 'задача №8 в наборе перед коммитом, корень `app/`' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ]
) ,
lh _gitleaks2 : nd (
2026-05-14 12:53:35 +03:00
'Полный скан всей истории коммитов на секреты — строже задачи перед коммитом.' ,
'pre-push (перед `git push` на удалённый репозиторий) — сканирует историю новых коммитов целиком.' ,
'Обход через `--no-verify` запрещён (правило §4.2 Pravila). На больших push (200+ коммитов) занимает 30+ секунд.' ,
[ { name : 'lefthook.yml' , cond : 'задача в наборе перед push' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'lefthook:gitleaks' , cond : 'версия для push строже: проверяет всю историю, а не только staged' } ]
2026-05-13 17:05:59 +03:00
) ,
lh _lychee : nd (
2026-05-14 12:53:35 +03:00
'Проверяет все ссылки в `.md` файлах на битые (`docs/**/*.md`, `db/**/*.md`, корневые `*.md`).' ,
'pre-push (перед `git push`) — проверяет ссылки во всех `.md` файлах репозитория.' ,
'Внешние ссылки проверяются с таймаутом 10 секунд; при отсутствии интернета — ошибка. Настройки в `lychee.toml`. Обход через `--no-verify` запрещён.' ,
[ { name : 'lefthook.yml' , cond : 'задача в наборе перед push' } ] ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ { name : 'CLAUDE.md' , cond : 'проверяет ссылки в CLAUDE.md в том числе' } ]
) ,
// ── MEMORY FILES ─────────────────────────────────
mem _user : nd (
'Профиль заказчика: Дмитрий, Windows Server 2022, VSCode, русский язык, путь к проекту.' ,
2026-05-14 12:53:35 +03:00
'Читается при старте каждой сессии через хук SessionStart — для языка и предпочтений.' ,
2026-05-14 09:20:54 +03:00
'Не содержит секретов. При смене заказчика — переписать полностью.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ { name : 'SessionStart хук' , cond : 'читается при старте каждой сессии' } ]
) ,
mem _comm : nd (
2026-05-14 12:53:35 +03:00
'Стиль общения: короткие команды («а», «б», «делай»), варианты A/B/C, явная фиксация переоткрытий.' ,
'Читается при работе с заказчиком — чтобы соответствовать его стилю общения.' ,
'Это не код, а правила коммуникации. Корректировки — только через явный отзыв заказчика.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'memory:user_profile' , cond : 'дополняет профиль заказчика' } ]
2026-05-13 17:05:59 +03:00
) ,
mem _env : nd (
2026-05-14 12:53:35 +03:00
'80+ квирков (особенностей) окружения: специфика Windows Server, баги инструментов, обходные пути.' ,
'При неожиданном поведении — сначала проверить memory:env на известный квирк.' ,
'Не дублировать — добавлять только новые квирки. Счётчик квирков актуализируется в `project_state.md`.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[
{ name : 'pest-parallel-debugger агент' , cond : 'квирки 72/77 используются агентом' } ,
{ name : 'SessionStart хук' , cond : 'читается при старте' }
]
) ,
mem _sp : nd (
2026-05-14 12:53:35 +03:00
'Обязательное правило §12 + архитектура хука economy из 6 компонентов — дисциплина вызова скилов.' ,
'При работе со скилами — для соответствия обязательному правилу §12 Pravila.' ,
'Описывает архитектуру хука economy (6 компонентов) — менять только при изменении самого хука.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'economy-mode хук' , cond : 'memory описывает архитектуру хука' } ]
2026-05-13 17:05:59 +03:00
) ,
mem _plugins : nd (
2026-05-14 12:53:35 +03:00
'Правила парного стека плагинов, MCP-серверы для отладки, уровневая структура PSR_v1.' ,
'При работе с плагинами FD/UPM/21st/Sentry/Redis MCP — для уровневого разделения по PSR_v1.' ,
2026-05-14 09:20:54 +03:00
'Синхронизируется с PSR_v1 — изменения в memory только если изменился сам PSR_v1.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ { name : 'PSR_v1' , cond : 'memory отражает актуальные версии PSR_v1' } ]
) ,
mem _state : nd (
2026-05-14 12:53:35 +03:00
'Текущее состояние проекта: ветка, тесты (Pest/Vitest), последние коммиты, активные задачи.' ,
2026-05-14 09:20:54 +03:00
'Читается при старте сессии — для быстрого контекста; обновляется после крупных вех.' ,
2026-05-14 12:53:35 +03:00
'Может устаревать — перечитать при сомнении. Не доверять данным старше 2-3 дней без проверки.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ { name : 'SessionStart хук' , cond : 'читается при старте для быстрого контекста' } ]
) ,
mem _phase1 : nd (
2026-05-14 12:53:35 +03:00
'Стратегия фазы 1: нативный стек Windows без Docker, расширение pg_partman заменено Artisan-задачей в cron.' ,
'При работе с инфраструктурой фазы 1 (PG/Redis/PHP-CLI нативно на Windows).' ,
'Стратегия зафиксирована до закрытия блокера Б-1 (Managed PG в Yandex Cloud) или 6 месяцев — пересмотр указан в файле.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
mem _archive : nd (
'Карта источников истины: версии всех 13+ ключевых документов проекта.' ,
2026-05-14 09:20:54 +03:00
'При вопросах «какая версия документа X» или «где источник истины для Y».' ,
2026-05-14 12:53:35 +03:00
'Синхронизируется с §0 CLAUDE.md. Изменения в memory — только если изменился §0 CLAUDE.md.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
2026-05-14 12:53:35 +03:00
[ { name : 'CLAUDE.md' , cond : 'memory синхронизирует версии с §0 CLAUDE.md' } ]
2026-05-13 17:05:59 +03:00
) ,
mem _github : nd (
2026-05-14 12:53:35 +03:00
'Репозиторий GitHub CoralMinister/lidpotok: HEAD, хуки, правила push.' ,
'При работе с GitHub — push, PR, операции с ветками.' ,
'Не делать `push --force` на main (предупреждение в Pravila). Хуки перед push обязательны.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ { name : 'MCP: github' , cond : 'MCP и memory дополняют друг друга для работы с GitHub' } ]
) ,
mem _handoff : nd (
2026-05-14 12:53:35 +03:00
'Дизайн-передача от Платона: что из `liderra_v8_handoff/` используем, что нет.' ,
'При UI/дизайн-задачах — для разделения «брендбук используем» vs «состав фич — по ТЗ v8.5».' ,
'Передача — только дизайн/токены/компоненты. Функционал и состав экранов — НЕ из передачи (берём из ТЗ).' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
mem _audit : nd (
2026-05-14 12:53:35 +03:00
'Полный аудит портала 13.05.2026: 38 находок, вердикт жёлтый, 10 отложенных вопросов закрыты.' ,
'При вопросах про аудит или его последствия (Q.DEFER, распределение P0/P1/P2).' ,
'Это снимок состояния — не редактировать при последующих аудитах, создавать новые memory-файлы.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
mem _supplier : nd (
2026-05-14 12:53:35 +03:00
'Прогресс интеграции с поставщиком лидов (планы 1-5): задачи, коммиты, блокеры.' ,
'При работе с интеграцией поставщика (планы 1-5) — для текущего состояния и блокеров.' ,
'Блокеры (Б-1, доступы) — внешние, не разрешаются силами Claude. Только отслеживание.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
mem _brain : nd (
2026-05-14 12:53:35 +03:00
'Claude Brain v1.0 — отдельный репозиторий «мозга», тег `brain-v1.0`, скрипт `install.sh`.' ,
'При работе с репозиторием brain или скриптом `install.sh` для синхронизации потребителей.' ,
'Push на репозиторий brain в GitHub заблокирован (вопрос 8.2). Не пытаться push без разрешения.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
mem _redesign : nd (
2026-05-14 12:53:35 +03:00
'Редизайн Quiet Luxury: 20 коммитов, базовый CSS + composables + переписанный AppSidebar.' ,
'При работе с редизайном портала (frontend, AppSidebar, базовый CSS).' ,
'Бэклог итерации I2 — в §15 спека, 10 пунктов отложены. Не реализовывать пункты I2 без явного запроса.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
mem _devindices : nd (
2026-05-14 12:53:35 +03:00
'Dev Element Indices — временная фича обратной связи для разработки; к удалению в продакшене.' ,
'При работе с dev-фидбеком (например «1030 измени цвет») — для соответствия номер → элемент.' ,
'**ВРЕМЕННАЯ** — заказчик прямо сказал «уберём в конечном релизе». Не вкладываться в долгосрочную инфраструктуру.' ,
2026-05-13 17:05:59 +03:00
[ ] ,
[ ] ,
[ ]
) ,
} ;
2026-05-14 13:11:12 +03:00
// ════════════════════════════════════════════════════
// SECTION 3.5: EDGE DETAILS (iter2 §5)
// ════════════════════════════════════════════════════
const edgeKey = ( from , to ) => from + '->' + to ;
const EDGE _DETAILS = {
// ── ПРАВИЛА — иерархия ──────────────────────────
'pravila->claude_md' : { type : 'подчиняет' , when : 'всегда — CLAUDE.md уровень ниже Pravila' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §1 (уровень 1→2a)' } ,
'pravila->psr_v1' : { type : 'подчиняет' , when : 'всегда — PSR_v1 уровень 3 ниже Pravila' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §1 (уровень 1→3)' } ,
'claude_md->tooling' : { type : 'документирует' , when : 'при правке реестра инструментов' , transfers : 'документация' , mandatory : 'обязательно' , rule : 'CLAUDE.md §0, §3 (ссылка на Прил. Н)' } ,
'pravila->superpowers' : { type : 'подчиняет' , when : 'задача попадает под карту §12.2 (14 типов)' , transfers : 'контроль' , mandatory : 'hard-block' , rule : 'Pravila §12 (hard rule, §9 не применяется)' } ,
// ── PSR_v1 координирует плагины ─────────────────
'psr_v1->superpowers' : { type : 'координирует' , when : 'paired-stack: процесс/решатель' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'PSR_v1 R5 (paired stack ядро)' } ,
'psr_v1->fd_plugin' : { type : 'координирует' , when : 'paired-stack: процесс/решатель' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'PSR_v1 R5 (paired stack ядро)' } ,
2026-05-14 17:15:39 +03:00
'psr_v1->upm' : { type : 'координирует' , when : 'вне основных фаз — активация через процедуру R14.3' , transfers : 'контроль' , mandatory : 'опционально' , rule : 'PSR_v1 R10.1, R11.5, R14.3' } ,
'psr_v1->mcp_21st' : { type : 'координирует' , when : 'вне основных фаз — активация через процедуру R14.4' , transfers : 'контроль' , mandatory : 'опционально' , rule : 'PSR_v1 R10.1, R14.4' } ,
2026-05-14 13:11:12 +03:00
'psr_v1->claude_md_mgmt' : { type : 'координирует' , when : 'инфраструктурный плагин для CLAUDE.md edits' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'PSR_v1 R10.1 блок 1' } ,
// ── CLAUDE.md — документирует ──────────────────
'claude_md->mcp_boost' : { type : 'документирует' , when : 'фаза 1+ Laravel SQL/Eloquent/docs' , transfers : 'документация' , mandatory : 'рекомендуется' , rule : 'CLAUDE.md §3.2 #10' } ,
2026-05-14 17:15:39 +03:00
'claude_md->mcp_sentry' : { type : 'документирует' , when : 'вне основных фаз — для отладки во время работы, ждёт Б-1' , transfers : 'документация' , mandatory : 'опционально' , rule : 'CLAUDE.md §3.3 #34' } ,
'claude_md->mcp_redis' : { type : 'документирует' , when : 'вне основных фаз — Memurai только на чтение' , transfers : 'документация' , mandatory : 'опционально' , rule : 'CLAUDE.md §3.3 #35' } ,
2026-05-14 13:11:12 +03:00
'claude_md->claude_md_mgmt' : { type : 'документирует' , when : 'единственный канал правок CLAUDE.md' , transfers : 'документация' , mandatory : 'hard-block' , rule : 'CLAUDE.md §5 п.10' } ,
'claude_md->ag_pest' : { type : 'документирует' , when : 'агент для Pest TDD задач' , transfers : 'документация' , mandatory : 'рекомендуется' , rule : 'CLAUDE.md §3 (агенты)' } ,
'claude_md->ag_rls' : { type : 'документирует' , when : 'агент для RLS-аудита и smoke-тестов' , transfers : 'документация' , mandatory : 'рекомендуется' , rule : 'CLAUDE.md §3 (агенты)' } ,
// ── HOOKS — триггеры ────────────────────────────
'hk_pre_claude->claude_md' : { type : 'триггерит' , when : 'PreToolUse Edit/Write CLAUDE.md' , transfers : 'проверка' , mandatory : 'hard-block' , rule : 'settings.json hooks (claude-md-management канал)' } ,
'hk_post_md->lh_mdlint' : { type : 'триггерит' , when : 'PostToolUse Edit *.md → markdownlint --fix' , transfers : 'триггер' , mandatory : 'обязательно' , rule : 'settings.json hooks + lefthook' } ,
'hk_post_schema->claude_md' : { type : 'триггерит' , when : 'PostToolUse Edit db/schema.sql → напоминание sync' , transfers : 'триггер' , mandatory : 'обязательно' , rule : 'settings.json hooks (§4.2 правил Claude)' } ,
'hk_session->mem_user' : { type : 'триггерит' , when : 'SessionStart инжектит memory-блок' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'settings.json hooks (memory inject)' } ,
'hk_session->mem_env' : { type : 'триггерит' , when : 'SessionStart инжектит memory-блок' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'settings.json hooks (memory inject)' } ,
'hk_session->mem_sp' : { type : 'триггерит' , when : 'SessionStart инжектит memory-блок' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'settings.json hooks (memory inject)' } ,
'hk_session->mem_plugins' : { type : 'триггерит' , when : 'SessionStart инжектит memory-блок' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'settings.json hooks (memory inject)' } ,
'hk_session->mem_state' : { type : 'триггерит' , when : 'SessionStart инжектит memory-блок' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'settings.json hooks (memory inject)' } ,
// ── SUPERPOWERS — содержит skills ───────────────
'superpowers->sk_brainstorm' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_tdd' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_debug' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_wplans' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_eplans' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_verify' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_parallel' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_worktree' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_pr' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_subagent' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_wskills' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_spreview' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_coderev' : { type : 'содержит' , when : 'plugin содержит skill' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12.2 (карта 14 типов)' } ,
'superpowers->sk_elements' : { type : 'содержит' , when : 'plugin содержит skill (using-superpowers)' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12 (старт сессии)' } ,
// ── SKILLS — последовательности ─────────────────
'sk_brainstorm->sk_wplans' : { type : 'запускает' , when : 'после brainstorming → writing-plans' , transfers : 'триггер' , mandatory : 'рекомендуется' , rule : 'PSR_v1 R5 (paired-stack flow)' } ,
'sk_wplans->sk_eplans' : { type : 'запускает' , when : 'plan готов → executing-plans' , transfers : 'триггер' , mandatory : 'рекомендуется' , rule : 'Superpowers process chain' } ,
'sk_wplans->sk_subagent' : { type : 'альтернатива' , when : 'если в текущей сессии — subagent-driven-development вместо executing' , transfers : 'триггер' , mandatory : 'опционально' , rule : 'Superpowers process chain (in-session alt)' } ,
'sk_subagent->ag_explore' : { type : 'запускает' , when : 'параллельный поиск/исследование' , transfers : 'триггер' , mandatory : 'опционально' , rule : 'subagent-driven-development (Task agent)' } ,
'sk_subagent->ag_general' : { type : 'запускает' , when : 'general-purpose subagent для независимых задач' , transfers : 'триггер' , mandatory : 'опционально' , rule : 'subagent-driven-development (Task agent)' } ,
'sk_subagent->ag_plan' : { type : 'запускает' , when : 'агент планирования больших задач' , transfers : 'триггер' , mandatory : 'опционально' , rule : 'subagent-driven-development (Task agent)' } ,
'sk_parallel->sk_worktree' : { type : 'запускает' , when : 'parallel agents требует изоляцию worktree' , transfers : 'триггер' , mandatory : 'рекомендуется' , rule : 'PSR_v1 R5 (dispatching-parallel-agents)' } ,
'sk_rls->tooling' : { type : 'читает' , when : 'RLS-аудит сверяется с реестром инструментов' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'Tooling Прил. Н (squawk/Boost)' } ,
'sk_rls->mcp_boost' : { type : 'запускает' , when : 'RLS-смоук читает БД через Boost MCP' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'CLAUDE.md §3.2 #10 (Boost)' } ,
'sk_qitem->claude_md_mgmt' : { type : 'запускает' , when : 'правки реестра открытых вопросов → бамп CLAUDE.md' , transfers : 'триггер' , mandatory : 'обязательно' , rule : 'CLAUDE.md §5 п.10 (единственный канал)' } ,
'claude_md_mgmt->claude_md' : { type : 'запускает' , when : 'skills claude-md-improver / revise-claude-md правят файл' , transfers : 'контроль' , mandatory : 'hard-block' , rule : 'CLAUDE.md §5 п.10' } ,
// ── HOOKIFY — генерирует hooks ──────────────────
'ag_hookify->hookify_plugin' : { type : 'запускает' , when : 'агент hookify создаёт правила через plugin' , transfers : 'триггер' , mandatory : 'опционально' , rule : 'hookify plugin docs' } ,
'hookify_plugin->hk_economy' : { type : 'содержит' , when : 'hookify сгенерировал economy hook' , transfers : 'контроль' , mandatory : 'обязательно' , rule : 'Pravila §12 economy hook architecture' } ,
// ── АГЕНТЫ → MCP/инструменты ────────────────────
'ag_rls->mcp_boost' : { type : 'запускает' , when : 'RLS-аудит через Boost MCP' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'CLAUDE.md §3.2 #10' } ,
'ag_guide->mcp_gh' : { type : 'запускает' , when : 'агент гайдов работает с GitHub issues/PR' , transfers : 'триггер' , mandatory : 'опционально' , rule : 'CLAUDE.md §3.1 #3 (GitHub MCP)' } ,
// ── LEFTHOOK jobs ───────────────────────────────
'lh_gitleaks->mem_plugins' : { type : 'документирует' , when : 'gitleaks-job описан в memory о плагинах' , transfers : 'документация' , mandatory : 'рекомендуется' , rule : 'Tooling Прил. Н §8 (lefthook.yml)' } ,
'lh_larastan->mcp_boost' : { type : 'запускает' , when : 'Larastan job использует Boost для контекста' , transfers : 'проверка' , mandatory : 'обязательно' , rule : 'lefthook.yml job + Boost' } ,
'lh_squawk->tooling' : { type : 'читает' , when : 'squawk-конфиг описан в реестре инструментов' , transfers : 'документация' , mandatory : 'обязательно' , rule : 'Tooling Прил. Н #15' } ,
'lh_gitleaks2->lh_gitleaks' : { type : 'альтернатива' , when : 'второй gitleaks-job (pre-push) вариант pre-commit' , transfers : 'проверка' , mandatory : 'опционально' , rule : 'lefthook.yml (дубль pre-push)' } ,
'lh_lychee->claude_md' : { type : 'читает' , when : 'lychee валидирует ссылки в CLAUDE.md и docs' , transfers : 'проверка' , mandatory : 'обязательно' , rule : 'CLAUDE.md §4 + lefthook' } ,
// ── MEMORY → плагины/правила ────────────────────
'mem_env->ag_pest' : { type : 'документирует' , when : 'memory о квирках окружения нужен для Pest agent' , transfers : 'данные' , mandatory : 'рекомендуется' , rule : 'memory/feedback_environment.md' } ,
'mem_plugins->psr_v1' : { type : 'документирует' , when : 'memory о парном стеке отражает PSR_v1' , transfers : 'данные' , mandatory : 'рекомендуется' , rule : 'memory/feedback_plugin_paired_stack.md' } ,
'mem_archive->claude_md' : { type : 'документирует' , when : 'memory об архиве содержит refs на CLAUDE.md' , transfers : 'данные' , mandatory : 'рекомендуется' , rule : 'memory/reference_archive.md' } ,
// ── MCP → агенты/skills ─────────────────────────
'mcp_pw->hk_session' : { type : 'триггерит' , when : 'Playwright MCP вызывается из SessionStart hook' , transfers : 'триггер' , mandatory : 'опционально' , rule : 'settings.json hooks + Playwright' } ,
'mcp_gh->sk_pr' : { type : 'запускает' , when : 'finishing-a-development-branch использует gh-команды' , transfers : 'триггер' , mandatory : 'обязательно' , rule : 'Superpowers finishing-a-development-branch' } ,
'mcp_boost->ag_rls' : { type : 'запускает' , when : 'Boost MCP отдаёт данные RLS-агенту' , transfers : 'данные' , mandatory : 'обязательно' , rule : 'CLAUDE.md §3.2 #10' } ,
// ── КОНФЛИКТЫ (8 рёбер; 3 из них имеют ту же пару from/to, что и обычные — здесь объединены под одним ключом) ─
'sk_rls->ag_rls' : { type : 'конфликт' , when : 'skill и agent оба претендуют на RLS-аудит' , transfers : 'coverage' , mandatory : 'опционально' , rule : 'нет регламента (двойное покрытие)' } ,
'hookify_plugin->hk_pre_claude' : { type : 'конфликт' , when : 'hookify plugin генерирует hook — двойное owner-ship vs settings.json' , transfers : 'coverage' , mandatory : 'опционально' , rule : 'нет регламента (plugin vs settings.json)' } ,
'mcp_pw->sk_parallel' : { type : 'конфликт' , when : 'Playwright и parallel-agents оба требуют изоляцию' , transfers : 'coverage' , mandatory : 'опционально' , rule : 'нет регламента (изоляция worktree vs MCP)' } ,
'ag_pest->mcp_redis' : { type : 'конфликт' , when : 'Pest --parallel race на Redis cache (quirk 72/77)' , transfers : 'coverage' , mandatory : 'опционально' , rule : 'CLAUDE.md §3.3 #35 (Redis MCP) — race остаётся вне регламента' } ,
'psr_v1->claude_md' : { type : 'конфликт' , when : 'PSR_v1 уровень 3 vs CLAUDE.md 2a — приоритет CLAUDE.md' , transfers : 'контроль' , mandatory : 'hard-block' , rule : 'CLAUDE.md §1 (priority chain)' } ,
'upm->fd_plugin' : { type : 'конфликт' , when : 'UPM и FD оба претендуют на UI-решения' , transfers : 'coverage' , mandatory : 'hard-block' , rule : 'PSR_v1 R14.5 (не параллельно)' } ,
'mcp_21st->fd_plugin' : { type : 'конфликт' , when : '21st Magic и FD оба генераторы UI' , transfers : 'coverage' , mandatory : 'hard-block' , rule : 'PSR_v1 R14.5 (не параллельно)' } ,
'hk_economy->superpowers' : { type : 'конфликт' , when : 'economy hook блокирует skill в обход Pravila §12' , transfers : 'контроль' , mandatory : 'hard-block' , rule : 'Pravila §12.4 (только явный «не используй»)' } ,
} ;
2026-05-13 17:05:59 +03:00
// ════════════════════════════════════════════════════
// SECTION 4: VIS INIT
// ════════════════════════════════════════════════════
const GROUPS = {
rules : { color : { background : '#073642' , border : '#268bd2' , highlight : { border : '#93a1a1' , background : '#0d4a5a' } } , font : { color : '#fdf6e3' , size : 13 , bold : true } } ,
plugins : { color : { background : '#001a00' , border : '#859900' , highlight : { border : '#b8cc00' , background : '#002600' } } , font : { color : '#fdf6e3' , size : 12 } } ,
skills _sp : { color : { background : '#1a0033' , border : '#6c71c4' , highlight : { border : '#9b9fea' , background : '#250047' } } , font : { color : '#fdf6e3' , size : 11 } } ,
skills _proj : { color : { background : '#2d0020' , border : '#d33682' , highlight : { border : '#e869a8' , background : '#3d0028' } } , font : { color : '#fdf6e3' , size : 12 } } ,
hooks : { color : { background : '#002233' , border : '#2aa198' , highlight : { border : '#4dd7ce' , background : '#003344' } } , font : { color : '#fdf6e3' , size : 11 } } ,
agents : { color : { background : '#1a1200' , border : '#b58900' , highlight : { border : '#e0ad00' , background : '#261a00' } } , font : { color : '#fdf6e3' , size : 11 } } ,
mcp : { color : { background : '#2d1200' , border : '#cb4b16' , highlight : { border : '#ff6b30' , background : '#3d1900' } } , font : { color : '#fdf6e3' , size : 11 } } ,
lefthook : { color : { background : '#2d0000' , border : '#dc322f' , highlight : { border : '#ff5f5c' , background : '#3d0000' } } , font : { color : '#fdf6e3' , size : 10 } } ,
memory : { color : { background : '#112233' , border : '#586e75' , highlight : { border : '#839496' , background : '#1a2f40' } } , font : { color : '#eee8d5' , size : 10 } } ,
} ;
const nodesDS = new vis . DataSet ( NODES ) ;
const edgesDS = new vis . DataSet ( EDGES ) ;
const network = new vis . Network (
document . getElementById ( 'network' ) ,
{ nodes : nodesDS , edges : edgesDS } ,
{
groups : GROUPS ,
nodes : {
shape : 'dot' ,
borderWidth : 2 ,
borderWidthSelected : 3 ,
font : { multi : 'html' } ,
} ,
edges : {
2026-05-14 09:23:46 +03:00
smooth : { type : 'continuous' , roundness : 0.5 } ,
2026-05-13 17:05:59 +03:00
selectionWidth : 2 ,
} ,
physics : {
2026-05-14 09:23:46 +03:00
enabled : false ,
2026-05-13 17:05:59 +03:00
} ,
interaction : {
hover : true ,
tooltipDelay : 400 ,
multiselect : false ,
} ,
}
) ;
2026-05-14 09:23:46 +03:00
network . once ( 'afterDrawing' , ( ) => {
network . fit ( { animation : { duration : 600 , easingFunction : 'easeInOutQuad' } } ) ;
2026-05-13 17:05:59 +03:00
} ) ;
// ════════════════════════════════════════════════════
// SECTION 5: LEGEND PANEL
// ════════════════════════════════════════════════════
function renderLegendItem ( item ) {
return ` <li> ${ item . name } ${ item . cond ? ` <span class="cond">— ${ item . cond } </span> ` : '' } </li> ` ;
}
2026-05-14 13:14:20 +03:00
function showNodeLegend ( nodeId ) {
document . getElementById ( 'legend-node-content' ) . style . display = '' ;
document . getElementById ( 'legend-edge-content' ) . style . display = 'none' ;
2026-05-13 17:05:59 +03:00
const node = NODES . find ( n => n . id === nodeId ) ;
const details = NODE _DETAILS [ nodeId ] ;
const panel = document . getElementById ( 'legend-panel' ) ;
if ( ! node || ! details ) { panel . classList . remove ( 'visible' ) ; return ; }
document . getElementById ( 'legend-title' ) . textContent = node . label . replace ( /\n/g , ' ' ) ;
const catLabel = CATEGORY _LABELS [ node . group ] || node . group ;
const catColor = GROUPS [ node . group ] && GROUPS [ node . group ] . color ? GROUPS [ node . group ] . color . border : '#839496' ;
document . getElementById ( 'legend-category' ) . innerHTML =
` <span style="color: ${ catColor } ; font-weight:600;">● ${ catLabel } </span> ` ;
document . getElementById ( 'ld-desc' ) . textContent = details . desc ;
2026-05-14 09:10:57 +03:00
document . getElementById ( 'ld-when' ) . textContent = details . when || '—' ;
document . getElementById ( 'ld-limits' ) . textContent = details . limits || 'без особых ограничений' ;
2026-05-13 17:05:59 +03:00
const ldReports = document . getElementById ( 'ld-reports' ) ;
ldReports . innerHTML = details . reportsTo . length
? details . reportsTo . map ( renderLegendItem ) . join ( '' )
: '<li style="color:#586e75; font-style:italic;">Не подчиняется никому</li>' ;
const ldManages = document . getElementById ( 'ld-manages' ) ;
ldManages . innerHTML = details . manages . length
? details . manages . map ( renderLegendItem ) . join ( '' )
: '<li style="color:#586e75; font-style:italic;">Никто не подчиняется</li>' ;
const ldTogether = document . getElementById ( 'ld-together' ) ;
ldTogether . innerHTML = details . together . length
? details . together . map ( renderLegendItem ) . join ( '' )
: '<li style="color:#586e75; font-style:italic;">Нет особых одновременных связей</li>' ;
const ldConflicts = document . getElementById ( 'ld-conflicts' ) ;
if ( details . conflicts && details . conflicts . length ) {
2026-05-14 13:16:56 +03:00
const sorted = [ ... details . conflicts ] . sort ( ( a , b ) =>
( CONFLICT _TYPES [ a . type ] ? CONFLICT _TYPES [ a . type ] . rank : 999 ) -
( CONFLICT _TYPES [ b . type ] ? CONFLICT _TYPES [ b . type ] . rank : 999 )
) ;
ldConflicts . innerHTML = sorted . map ( c => {
const t = CONFLICT _TYPES [ c . type ] || CONFLICT _TYPES . RED ;
return ` <div class="conflict-item" style="background: ${ t . bg } "> ` +
` <div class="cname" style="color: ${ t . color } "> ${ t . emoji } ${ c . name } </div> ` +
` <div class="cdesc"> ${ c . desc } </div> ` +
` </div> ` ;
} ) . join ( '' ) ;
2026-05-13 17:05:59 +03:00
} else {
ldConflicts . innerHTML = '<div id="legend-no-conflicts" style="font-size:12px;color:#586e75;">Конфликтов не выявлено</div>' ;
}
document . getElementById ( 'conflicts-section' ) . style . display = '' ;
panel . classList . add ( 'visible' ) ;
}
2026-05-14 13:14:20 +03:00
function showEdgeLegend ( edgeId ) {
const edge = edgesDS . get ( edgeId ) ;
if ( ! edge ) return ;
const panel = document . getElementById ( 'legend-panel' ) ;
document . getElementById ( 'legend-node-content' ) . style . display = 'none' ;
document . getElementById ( 'legend-edge-content' ) . style . display = '' ;
const fromNode = NODES . find ( n => n . id === edge . from ) ;
const toNode = NODES . find ( n => n . id === edge . to ) ;
if ( ! fromNode || ! toNode ) return ;
const fromCat = CATEGORY _LABELS [ fromNode . group ] || fromNode . group ;
const toCat = CATEGORY _LABELS [ toNode . group ] || toNode . group ;
const fromColor = ( GROUPS [ fromNode . group ] && GROUPS [ fromNode . group ] . color ) ? GROUPS [ fromNode . group ] . color . border : '#839496' ;
const toColor = ( GROUPS [ toNode . group ] && GROUPS [ toNode . group ] . color ) ? GROUPS [ toNode . group ] . color . border : '#839496' ;
const edgeColor = ( edge . color && edge . color . color ) ? edge . color . color : '#586e75' ;
document . getElementById ( 'legend-edge-title' ) . innerHTML =
'<span style="color:' + fromColor + '">' + fromNode . label . replace ( /\n/g , ' ' ) + '</span>' +
' <span style="color:' + edgeColor + '">→</span> ' +
'<span style="color:' + toColor + '">' + toNode . label . replace ( /\n/g , ' ' ) + '</span>' ;
document . getElementById ( 'le-from' ) . innerHTML =
fromNode . label . replace ( /\n/g , ' ' ) +
' <span style="color:' + fromColor + ';font-size:11px;text-transform:uppercase;">(' + fromCat + ')</span>' ;
document . getElementById ( 'le-to' ) . innerHTML =
toNode . label . replace ( /\n/g , ' ' ) +
' <span style="color:' + toColor + ';font-size:11px;text-transform:uppercase;">(' + toCat + ')</span>' ;
const details = EDGE _DETAILS [ edgeKey ( edge . from , edge . to ) ] ;
if ( details ) {
document . getElementById ( 'le-type' ) . textContent = details . type ;
document . getElementById ( 'le-when' ) . textContent = details . when ;
document . getElementById ( 'le-transfers' ) . textContent = details . transfers ;
document . getElementById ( 'le-mandatory' ) . textContent = details . mandatory ;
document . getElementById ( 'le-rule' ) . textContent = details . rule ;
} else {
[ 'le-type' , 'le-when' , 'le-transfers' , 'le-mandatory' ] . forEach ( id =>
document . getElementById ( id ) . textContent = '—' ) ;
document . getElementById ( 'le-rule' ) . textContent = 'Регламент не задокументирован' ;
}
panel . classList . add ( 'visible' ) ;
}
2026-05-13 17:05:59 +03:00
network . on ( 'click' , params => {
if ( params . nodes . length === 1 ) {
2026-05-15 06:24:06 +03:00
const id = params . nodes [ 0 ] ;
HIGHLIGHT . setSelectedNode ( id ) ;
HIGHLIGHT . applyHighlight ( ) ;
// Right panel still shows details of the clicked node (last-clicked, even after toggle-off)
showNodeLegend ( id ) ;
2026-05-14 13:14:20 +03:00
} else if ( params . edges . length === 1 ) {
showEdgeLegend ( params . edges [ 0 ] ) ;
2026-05-13 17:05:59 +03:00
} else if ( params . nodes . length === 0 && params . edges . length === 0 ) {
2026-05-15 06:24:06 +03:00
HIGHLIGHT . state . selectedNode = null ;
HIGHLIGHT . applyHighlight ( ) ;
2026-05-13 17:05:59 +03:00
document . getElementById ( 'legend-panel' ) . classList . remove ( 'visible' ) ;
}
} ) ;
document . getElementById ( 'legend-close' ) . addEventListener ( 'click' , ( ) => {
document . getElementById ( 'legend-panel' ) . classList . remove ( 'visible' ) ;
} ) ;
// ════════════════════════════════════════════════════
// SECTION 6: TOOLBAR
// ════════════════════════════════════════════════════
let highlightedNode = null ;
document . getElementById ( 'search' ) . addEventListener ( 'input' , function ( ) {
const q = this . value . trim ( ) . toLowerCase ( ) ;
if ( ! q ) {
nodesDS . update ( NODES . map ( n => ( { id : n . id , borderWidth : 2 , opacity : 1.0 } ) ) ) ;
highlightedNode = null ;
return ;
}
const matches = NODES . filter ( n => n . label . toLowerCase ( ) . includes ( q ) ) ;
const updates = NODES . map ( n => {
const match = matches . some ( m => m . id === n . id ) ;
return {
id : n . id ,
borderWidth : match ? 5 : 1 ,
opacity : match ? 1.0 : 0.25 ,
} ;
} ) ;
nodesDS . update ( updates ) ;
if ( matches . length === 1 ) {
network . focus ( matches [ 0 ] . id , { scale : 1.4 , animation : { duration : 500 , easingFunction : 'easeInOutQuad' } } ) ;
2026-05-14 13:14:20 +03:00
showNodeLegend ( matches [ 0 ] . id ) ;
2026-05-13 17:05:59 +03:00
highlightedNode = matches [ 0 ] . id ;
}
} ) ;
document . getElementById ( 'btn-freeze' ) . addEventListener ( 'click' , ( ) => {
network . setOptions ( { physics : { enabled : false } } ) ;
} ) ;
document . getElementById ( 'btn-unfreeze' ) . addEventListener ( 'click' , ( ) => {
2026-05-14 09:23:46 +03:00
network . setOptions ( {
physics : {
enabled : true ,
solver : 'forceAtlas2Based' ,
forceAtlas2Based : {
gravitationalConstant : - 50 ,
centralGravity : 0.0 ,
springLength : 100 ,
springConstant : 0.02 ,
damping : 0.6 ,
avoidOverlap : 0.4 ,
} ,
} ,
} ) ;
2026-05-13 17:05:59 +03:00
} ) ;
document . getElementById ( 'btn-reset' ) . addEventListener ( 'click' , ( ) => {
2026-05-14 09:23:46 +03:00
// (a) Restore radial positions
nodesDS . update ( NODES . map ( n => ( { id : n . id , x : n . x , y : n . y } ) ) ) ;
// (b) Refit camera
2026-05-13 17:05:59 +03:00
network . fit ( { animation : { duration : 600 , easingFunction : 'easeInOutQuad' } } ) ;
} ) ;
document . getElementById ( 'btn-clear' ) . addEventListener ( 'click' , ( ) => {
document . getElementById ( 'search' ) . value = '' ;
nodesDS . update ( NODES . map ( n => ( { id : n . id , borderWidth : 2 , opacity : 1.0 } ) ) ) ;
document . getElementById ( 'legend-panel' ) . classList . remove ( 'visible' ) ;
highlightedNode = null ;
} ) ;
2026-05-14 12:17:08 +03:00
// ════════════════════════════════════════════════════
// SECTION 7: RESIZE HANDLE + LOCALSTORAGE
// ════════════════════════════════════════════════════
const LEGEND _STORAGE _KEY = 'liderra-map-legend-width' ;
const LEGEND _MIN _W = 300 , LEGEND _MAX _W = 900 ;
2026-05-14 12:22:52 +03:00
let redrawScheduled = false ;
2026-05-14 12:17:08 +03:00
function applyLegendWidth ( w ) {
const clamped = Math . max ( LEGEND _MIN _W , Math . min ( LEGEND _MAX _W , w ) ) ;
const panel = document . getElementById ( 'legend-panel' ) ;
panel . style . width = clamped + 'px' ;
panel . style . minWidth = clamped + 'px' ;
2026-05-14 12:22:52 +03:00
if ( typeof network !== 'undefined' && network && ! redrawScheduled ) {
redrawScheduled = true ;
requestAnimationFrame ( ( ) => {
redrawScheduled = false ;
network . redraw ( ) ;
} ) ;
}
2026-05-14 12:17:08 +03:00
}
function restoreLegendWidth ( ) {
2026-05-14 12:22:52 +03:00
let saved = 300 ;
try {
saved = parseInt ( localStorage . getItem ( LEGEND _STORAGE _KEY ) || '300' , 10 ) ;
} catch ( e ) { /* private mode or quota — keep default */ }
2026-05-14 12:17:08 +03:00
applyLegendWidth ( saved ) ;
}
( function setupResizeHandle ( ) {
const handle = document . getElementById ( 'legend-handle' ) ;
if ( ! handle ) return ;
let dragging = false ;
handle . addEventListener ( 'mousedown' , e => {
dragging = true ;
handle . classList . add ( 'dragging' ) ;
document . body . style . userSelect = 'none' ;
e . preventDefault ( ) ;
} ) ;
document . addEventListener ( 'mousemove' , e => {
if ( ! dragging ) return ;
const w = window . innerWidth - e . clientX ;
applyLegendWidth ( w ) ;
} ) ;
document . addEventListener ( 'mouseup' , ( ) => {
if ( ! dragging ) return ;
dragging = false ;
handle . classList . remove ( 'dragging' ) ;
document . body . style . userSelect = '' ;
const w = parseInt ( document . getElementById ( 'legend-panel' ) . style . width , 10 ) ;
2026-05-14 12:22:52 +03:00
try { localStorage . setItem ( LEGEND _STORAGE _KEY , String ( w ) ) ; } catch ( e ) { /* private mode or quota */ }
2026-05-14 12:17:08 +03:00
} ) ;
} ) ( ) ;
2026-05-15 06:12:43 +03:00
// ════════════════════════════════════════════════════
// SECTION 8: HIGHLIGHTING (legend filter + node focus)
// ════════════════════════════════════════════════════
const HIGHLIGHT = ( function setupHighlight ( ) {
const FILTER _GROUP _PREFIX = 'group:' ;
const FILTER _CONFLICT _PREFIX = 'conflict:' ;
const OPACITY _FOCUS = 1.0 ;
const OPACITY _FILTER = 0.55 ;
const OPACITY _DIM = 0.15 ;
const CONFLICT _EDGE _MIN _OPACITY = 0.85 ;
const state = {
selectedNode : null ,
legendFilter : new Set ( ) ,
} ;
// ── Pre-computed indices ──────────────────────────
const NODES _BY _ID = new Map ( ) ;
const NEIGHBOURS = new Map ( ) ;
const CONFLICT _ENDPOINTS = { RED : new Set ( ) , BLACK : new Set ( ) , GREEN : new Set ( ) } ;
const CONFLICT _EDGE _TYPE = new Map ( ) ;
NODES . forEach ( n => {
NODES _BY _ID . set ( n . id , n ) ;
if ( ! NEIGHBOURS . has ( n . id ) ) NEIGHBOURS . set ( n . id , new Set ( ) ) ;
} ) ;
edgesDS . get ( ) . forEach ( edge => {
// Both directions — Q5; conflict edges count as connections — Q6
if ( NEIGHBOURS . has ( edge . from ) ) NEIGHBOURS . get ( edge . from ) . add ( edge . to ) ;
if ( NEIGHBOURS . has ( edge . to ) ) NEIGHBOURS . get ( edge . to ) . add ( edge . from ) ;
if ( edge . dashes && edge . color && edge . color . color ) {
for ( const t of [ 'RED' , 'BLACK' , 'GREEN' ] ) {
if ( CONFLICT _TYPES [ t ] . color === edge . color . color ) {
CONFLICT _ENDPOINTS [ t ] . add ( edge . from ) ;
CONFLICT _ENDPOINTS [ t ] . add ( edge . to ) ;
CONFLICT _EDGE _TYPE . set ( edge . id , t ) ;
break ;
}
}
}
} ) ;
// ── Opacity computations ──────────────────────────
function computeNodeOpacity ( nodeId ) {
// Row 1: focus
if ( state . selectedNode !== null ) {
if ( state . selectedNode === nodeId ) return OPACITY _FOCUS ;
const neigh = NEIGHBOURS . get ( state . selectedNode ) ;
if ( neigh && neigh . has ( nodeId ) ) return OPACITY _FOCUS ;
}
// Row 2: idle
if ( state . legendFilter . size === 0 && state . selectedNode === null ) return OPACITY _FOCUS ;
// Row 3: in filter?
const node = NODES _BY _ID . get ( nodeId ) ;
let inFilter = false ;
if ( node && state . legendFilter . has ( FILTER _GROUP _PREFIX + node . group ) ) inFilter = true ;
if ( ! inFilter ) {
for ( const t of [ 'RED' , 'BLACK' , 'GREEN' ] ) {
if ( state . legendFilter . has ( FILTER _CONFLICT _PREFIX + t ) && CONFLICT _ENDPOINTS [ t ] . has ( nodeId ) ) {
inFilter = true ;
break ;
}
}
}
if ( inFilter ) return state . selectedNode ? OPACITY _FILTER : OPACITY _FOCUS ;
// Row 4: everything else
return OPACITY _DIM ;
}
function computeEdgeOpacity ( edge ) {
const fromO = computeNodeOpacity ( edge . from ) ;
const toO = computeNodeOpacity ( edge . to ) ;
const baseline = Math . min ( fromO , toO ) ;
// Conflict edge directly selected via 🔴/⚫/🟢 in filter — boost to ≥0.85
const ctype = CONFLICT _EDGE _TYPE . get ( edge . id ) ;
if ( ctype && state . legendFilter . has ( FILTER _CONFLICT _PREFIX + ctype ) ) {
return Math . max ( CONFLICT _EDGE _MIN _OPACITY , baseline ) ;
}
return baseline ;
}
function applyHighlight ( ) {
const nodeUpdates = NODES . map ( n => ( {
id : n . id ,
opacity : computeNodeOpacity ( n . id ) ,
} ) ) ;
const edgeUpdates = edgesDS . get ( ) . map ( e => ( {
id : e . id ,
color : Object . assign ( { } , e . color || { } , { opacity : computeEdgeOpacity ( e ) } ) ,
} ) ) ;
nodesDS . update ( nodeUpdates ) ;
edgesDS . update ( edgeUpdates ) ;
}
// ── State manipulators ────────────────────────────
function toggleFilter ( key ) {
if ( state . legendFilter . has ( key ) ) state . legendFilter . delete ( key ) ;
else state . legendFilter . add ( key ) ;
}
function setSelectedNode ( id ) {
if ( state . selectedNode === id ) state . selectedNode = null ; // toggle on repeat
else state . selectedNode = id ;
}
function clearAll ( ) {
state . selectedNode = null ;
state . legendFilter . clear ( ) ;
}
function updateLegendVisuals ( ) {
document . querySelectorAll ( '#cat-legend .cat-item' ) . forEach ( item => {
const key = item . dataset . filterKey ;
if ( ! key ) return ;
if ( state . legendFilter . has ( key ) ) item . classList . add ( 'active' ) ;
else item . classList . remove ( 'active' ) ;
} ) ;
}
2026-05-15 06:20:22 +03:00
// ── Legend click delegation ───────────────────────
document . getElementById ( 'cat-legend' ) . addEventListener ( 'click' , e => {
const item = e . target . closest ( '.cat-item' ) ;
if ( ! item || ! item . dataset . filterKey ) return ;
toggleFilter ( item . dataset . filterKey ) ;
applyHighlight ( ) ;
updateLegendVisuals ( ) ;
} ) ;
2026-05-15 06:12:43 +03:00
// Expose API (closes over state)
return {
applyHighlight ,
toggleFilter ,
setSelectedNode ,
clearAll ,
updateLegendVisuals ,
state , // exposed for debug only
} ;
} ) ( ) ;
2026-05-14 12:17:08 +03:00
window . addEventListener ( 'DOMContentLoaded' , restoreLegendWidth ) ;
2026-05-13 17:05:59 +03:00
< / script >
< / body >
< / html >