Files
brain/project-files/docs/visualizations/hooks-skills-plugins-map.html
T
Дмитрий ed9bade863 feat: extract brain artifacts from Liderra + ~/.claude/
project-files/:
- CLAUDE.md.template (266 lines)
- docs/Pravila_raboty_Claude.template.md (720 lines)
- docs/Plugin_stack_rules.template.md (916 lines)
- docs/Tooling.template.md (613 lines)
- docs/CHANGELOG_claude_md.template.md
- docs/visualizations/hooks-skills-plugins-map.html (3122 lines)
- .mcp.json.template (universal: playwright/github/semgrep; laravel-boost dropped)

user-level-files/:
- hooks/ (10 Python files: skill-marker, skill-check, economy-* x8)
- settings-fragment.json (enabledPlugins + permissions + hooks only)
- marketplaces.json (3 sources)
- plugins-manifest.json (4 plugins pinned with gitCommitSha)
- mcp-user.template.json (magic with <<MAGIC_API_KEY>> placeholder)

Gitleaks scan: 0 findings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 00:46:51 +03:00

3123 lines
124 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Карта работы — Hooks · Skills · Plugins</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT,WONK@0,9..144,200..900,0..100,0..1;1,9..144,200..900,0..100,0..1&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js" defer></script>
<style>
/* ============================================================
COLOR SYSTEM — Vintage Engineering Manual
============================================================ */
:root {
--paper: #F2EBDC;
--paper-shade: #E8DFC8;
--paper-deep: #D9CFB5;
--ink: #0E2235;
--ink-soft: #2B3D52;
--ink-fade: #5E6F82;
--blueprint: #1A6E8E;
--blueprint-deep: #0F4D67;
--amber: #C48B26;
--amber-deep: #9A6A14;
--rust: #8B3A1B;
--rust-deep: #6A2A12;
--sage: #5E7A4F;
--sage-deep: #3E5333;
--grid: rgba(14, 34, 53, 0.06);
--rule: rgba(14, 34, 53, 0.15);
--shadow-paper: 0 1px 0 rgba(0,0,0,0.04), 0 4px 24px rgba(14, 34, 53, 0.08);
}
/* ============================================================
RESET + BASE
============================================================ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
scroll-behavior: smooth;
font-size: 16px;
}
body {
font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
font-weight: 400;
font-size: 1rem;
line-height: 1.6;
color: var(--ink);
background: var(--paper);
background-image:
linear-gradient(var(--grid) 1px, transparent 1px),
linear-gradient(90deg, var(--grid) 1px, transparent 1px);
background-size: 32px 32px;
background-attachment: fixed;
min-height: 100vh;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}
/* Subtle paper grain overlay */
body::before {
content: '';
position: fixed;
inset: 0;
pointer-events: none;
z-index: 1;
opacity: 0.4;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' /%3E%3CfeColorMatrix values='0 0 0 0 0.05 0 0 0 0 0.13 0 0 0 0 0.21 0 0 0 0.18 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
main { position: relative; z-index: 2; }
/* ============================================================
TYPOGRAPHY
============================================================ */
.display {
font-family: 'Fraunces', serif;
font-variation-settings: 'opsz' 144, 'SOFT' 50, 'WONK' 1;
font-weight: 400;
letter-spacing: -0.02em;
line-height: 0.95;
}
.display-i {
font-family: 'Fraunces', serif;
font-style: italic;
font-variation-settings: 'opsz' 144, 'SOFT' 100, 'WONK' 1;
font-weight: 300;
}
.mono {
font-family: 'JetBrains Mono', ui-monospace, monospace;
font-weight: 400;
}
.caps {
font-family: 'Plus Jakarta Sans', sans-serif;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.18em;
font-size: 0.72rem;
}
/* ============================================================
PAGE FRAME
============================================================ */
.frame {
max-width: 1240px;
margin: 0 auto;
padding: 0 32px;
}
@media (max-width: 720px) {
.frame { padding: 0 20px; }
}
/* ============================================================
HERO
============================================================ */
.hero {
padding: 96px 0 64px;
position: relative;
}
.hero-meta {
display: flex;
align-items: baseline;
gap: 24px;
margin-bottom: 48px;
color: var(--ink-fade);
}
.hero-meta-divider {
flex: 1;
height: 1px;
background: var(--rule);
position: relative;
top: -4px;
}
.hero-title {
font-size: clamp(3rem, 9vw, 7rem);
margin-bottom: 32px;
}
.hero-title em {
font-style: italic;
color: var(--blueprint);
font-variation-settings: 'opsz' 144, 'SOFT' 100, 'WONK' 1;
}
.hero-sub {
font-size: 1.25rem;
max-width: 720px;
color: var(--ink-soft);
line-height: 1.55;
}
.hero-sub strong {
font-weight: 600;
color: var(--ink);
background: linear-gradient(transparent 60%, rgba(196, 139, 38, 0.35) 60%);
padding: 0 2px;
}
.hero-stamp {
position: absolute;
top: 80px;
right: 32px;
display: flex;
flex-direction: column;
align-items: flex-end;
text-align: right;
color: var(--rust);
border: 2px solid var(--rust);
padding: 14px 18px;
transform: rotate(4deg);
background: rgba(242, 235, 220, 0.6);
}
.hero-stamp .num { font-family: 'Fraunces', serif; font-size: 2rem; font-weight: 400; line-height: 1; }
.hero-stamp .lbl { font-size: 0.65rem; letter-spacing: 0.2em; text-transform: uppercase; margin-top: 4px; }
@media (max-width: 720px) {
.hero { padding: 60px 0 40px; }
.hero-stamp { display: none; }
}
/* ============================================================
SECTION
============================================================ */
.section {
padding: 64px 0;
border-top: 1px solid var(--rule);
}
.section-num {
font-family: 'Fraunces', serif;
font-style: italic;
font-variation-settings: 'opsz' 144, 'SOFT' 100;
font-weight: 300;
font-size: 4rem;
color: var(--blueprint);
line-height: 1;
margin-bottom: 8px;
}
.section-title {
font-family: 'Fraunces', serif;
font-weight: 500;
font-variation-settings: 'opsz' 60, 'SOFT' 30;
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.1;
margin-bottom: 12px;
letter-spacing: -0.01em;
}
.section-lede {
font-size: 1.15rem;
color: var(--ink-soft);
max-width: 680px;
margin-bottom: 48px;
line-height: 1.55;
}
/* ============================================================
THE THREE CONCEPTS
============================================================ */
.triad {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0;
border: 1px solid var(--rule);
background: var(--paper);
}
@media (max-width: 900px) {
.triad { grid-template-columns: 1fr; }
}
.triad-cell {
padding: 36px 32px 40px;
position: relative;
background: var(--paper);
transition: background 250ms;
}
.triad-cell:not(:last-child) {
border-right: 1px solid var(--rule);
}
@media (max-width: 900px) {
.triad-cell:not(:last-child) {
border-right: none;
border-bottom: 1px solid var(--rule);
}
}
.triad-cell:hover { background: var(--paper-shade); }
.triad-tag {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--blueprint);
margin-bottom: 16px;
}
.triad-name {
font-family: 'Fraunces', serif;
font-weight: 400;
font-variation-settings: 'opsz' 100, 'SOFT' 50;
font-size: 2.4rem;
line-height: 1;
margin-bottom: 8px;
color: var(--ink);
}
.triad-name em {
font-style: italic;
color: var(--rust);
font-variation-settings: 'opsz' 144, 'SOFT' 100, 'WONK' 1;
}
.triad-eq {
font-family: 'Fraunces', serif;
font-style: italic;
color: var(--ink-fade);
font-size: 1.05rem;
margin-bottom: 24px;
}
.triad-body {
font-size: 0.95rem;
line-height: 1.6;
color: var(--ink-soft);
}
.triad-key {
margin-top: 24px;
padding-top: 16px;
border-top: 1px dashed var(--rule);
font-size: 0.85rem;
}
.triad-key b {
font-weight: 600;
color: var(--ink);
}
.icon-shape {
width: 56px;
height: 56px;
margin-bottom: 24px;
display: flex;
align-items: center;
justify-content: center;
}
/* ============================================================
TIMELINE
============================================================ */
.timeline {
margin-top: 32px;
position: relative;
background: var(--paper-shade);
border: 1px solid var(--rule);
padding: 48px 32px 40px;
border-radius: 2px;
overflow-x: auto;
}
.timeline-rail {
position: relative;
min-width: 1000px;
padding: 16px 0 48px;
}
.timeline-line {
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 2px;
background: var(--ink);
background-image: linear-gradient(to right, var(--ink) 0, var(--ink) 6px, transparent 6px, transparent 12px);
background-size: 12px 2px;
z-index: 1;
}
.timeline-steps {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 16px;
position: relative;
z-index: 2;
}
.t-step {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
position: relative;
}
.t-step-dot {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--paper);
border: 2px solid var(--ink);
margin-bottom: 16px;
position: relative;
}
.t-step-dot.user { background: var(--ink); }
.t-step-dot.fire { background: var(--amber); border-color: var(--amber-deep); }
.t-step-dot.danger { background: var(--rust); border-color: var(--rust-deep); }
.t-step-dot.done { background: var(--sage); border-color: var(--sage-deep); }
.t-step-label {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--blueprint);
margin-bottom: 8px;
}
.t-step-title {
font-family: 'Fraunces', serif;
font-weight: 500;
font-variation-settings: 'opsz' 36, 'SOFT' 30;
font-size: 1.05rem;
line-height: 1.15;
margin-bottom: 8px;
}
.t-step-desc {
font-size: 0.82rem;
color: var(--ink-soft);
line-height: 1.45;
max-width: 200px;
}
.t-step-script {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
color: var(--rust);
margin-top: 8px;
padding: 4px 8px;
background: rgba(139, 58, 27, 0.08);
border-radius: 2px;
display: inline-block;
}
/* Mid-turn micro-section (between UserPromptSubmit and Stop) */
.timeline-inner {
margin: 56px 24px 0;
padding: 24px;
border: 1px dashed var(--blueprint);
background: rgba(26, 110, 142, 0.05);
position: relative;
}
.timeline-inner-tag {
position: absolute;
top: -10px;
left: 24px;
background: var(--paper-shade);
padding: 0 12px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: var(--blueprint);
}
.timeline-inner-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
@media (max-width: 900px) {
.timeline-inner-grid { grid-template-columns: 1fr; }
}
.t-inner-step {
padding: 16px;
background: var(--paper);
border-left: 3px solid var(--blueprint);
}
.t-inner-step.special {
border-left-color: var(--rust);
}
.t-inner-title {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 1rem;
margin-bottom: 4px;
line-height: 1.2;
}
.t-inner-trigger {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
color: var(--ink-fade);
margin-bottom: 8px;
}
.t-inner-desc {
font-size: 0.85rem;
color: var(--ink-soft);
line-height: 1.5;
}
/* Parallel hooks (SessionStart, PostCompact) */
.timeline-parallel {
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid var(--rule);
}
.timeline-parallel-title {
font-family: 'Plus Jakarta Sans', sans-serif;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.15em;
font-size: 0.75rem;
color: var(--ink-fade);
margin-bottom: 16px;
}
.timeline-parallel-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
@media (max-width: 720px) {
.timeline-parallel-list { grid-template-columns: 1fr; }
}
.tp-item {
display: flex;
gap: 16px;
}
.tp-item-when {
font-family: 'Fraunces', serif;
font-style: italic;
color: var(--blueprint);
font-weight: 400;
white-space: nowrap;
min-width: 150px;
font-size: 0.95rem;
}
.tp-item-what {
font-size: 0.88rem;
color: var(--ink-soft);
}
/* ============================================================
HOOK ROSTER — table-card hybrid
============================================================ */
.roster {
margin-top: 32px;
border: 1px solid var(--rule);
}
.roster-row {
display: grid;
grid-template-columns: 180px 220px 1fr 120px;
gap: 24px;
padding: 20px 28px;
border-bottom: 1px solid var(--rule);
align-items: center;
background: var(--paper);
transition: background 200ms;
}
.roster-row:last-child { border-bottom: none; }
.roster-row:hover { background: var(--paper-shade); }
.roster-row.header {
background: var(--ink);
color: var(--paper);
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.roster-row.header:hover { background: var(--ink); }
@media (max-width: 900px) {
.roster-row {
grid-template-columns: 1fr;
gap: 8px;
}
.roster-row.header { display: none; }
}
.r-event {
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
font-weight: 500;
color: var(--blueprint-deep);
}
.r-matcher {
font-family: 'JetBrains Mono', monospace;
font-size: 0.78rem;
color: var(--ink-soft);
}
.r-script {
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
color: var(--rust);
}
.r-desc {
font-size: 0.92rem;
color: var(--ink-soft);
line-height: 1.5;
}
.r-type {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
text-align: center;
padding: 4px 10px;
border: 1px solid;
width: max-content;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.r-type.cmd { color: var(--sage-deep); border-color: var(--sage); background: rgba(94, 122, 79, 0.1); }
.r-type.agent { color: var(--rust); border-color: var(--rust); background: rgba(139, 58, 27, 0.1); }
/* ============================================================
PERMISSIONS BLOCK
============================================================ */
.perms {
margin-top: 32px;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
@media (max-width: 900px) {
.perms { grid-template-columns: 1fr; }
}
.perm-card {
padding: 28px 24px;
border: 1px solid var(--rule);
background: var(--paper);
position: relative;
}
.perm-card-num {
position: absolute;
top: 16px;
right: 20px;
font-family: 'Fraunces', serif;
font-style: italic;
font-variation-settings: 'opsz' 144, 'SOFT' 100, 'WONK' 1;
font-weight: 300;
font-size: 3.5rem;
line-height: 1;
}
.perm-card.allow { border-top: 4px solid var(--sage); }
.perm-card.allow .perm-card-num { color: var(--sage); }
.perm-card.deny { border-top: 4px solid var(--rust); }
.perm-card.deny .perm-card-num { color: var(--rust); }
.perm-card.ask { border-top: 4px solid var(--amber); }
.perm-card.ask .perm-card-num { color: var(--amber-deep); }
.perm-card-label {
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
letter-spacing: 0.18em;
text-transform: uppercase;
margin-bottom: 12px;
color: var(--ink-fade);
}
.perm-card-title {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 1.6rem;
margin-bottom: 12px;
line-height: 1.1;
}
.perm-card-body {
font-size: 0.88rem;
color: var(--ink-soft);
line-height: 1.5;
}
/* ============================================================
PLUGINS GRID
============================================================ */
.plugins {
margin-top: 32px;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
@media (max-width: 900px) {
.plugins { grid-template-columns: 1fr; }
}
.plugin-card {
border: 1px solid var(--rule);
background: var(--paper);
padding: 28px 28px 24px;
position: relative;
}
.plugin-card-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px dashed var(--rule);
}
.plugin-card-name {
font-family: 'Fraunces', serif;
font-weight: 500;
font-variation-settings: 'opsz' 60, 'SOFT' 30;
font-size: 1.5rem;
line-height: 1.1;
}
.plugin-card-scope {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 4px 8px;
background: var(--ink);
color: var(--paper);
}
.plugin-card-desc {
font-size: 0.92rem;
color: var(--ink-soft);
line-height: 1.55;
margin-bottom: 16px;
}
.plugin-skills-list {
font-family: 'JetBrains Mono', monospace;
font-size: 0.78rem;
color: var(--blueprint-deep);
line-height: 1.7;
}
/* ============================================================
SKILLS BY PURPOSE
============================================================ */
.skills-purpose {
margin-top: 32px;
}
.sp-row {
display: grid;
grid-template-columns: 280px 1fr;
gap: 32px;
padding: 24px 0;
border-bottom: 1px solid var(--rule);
}
.sp-row:last-child { border-bottom: none; }
@media (max-width: 900px) {
.sp-row { grid-template-columns: 1fr; gap: 12px; }
}
.sp-cat {
display: flex;
flex-direction: column;
gap: 6px;
}
.sp-cat-symbol {
font-family: 'Fraunces', serif;
font-variation-settings: 'opsz' 144, 'WONK' 1, 'SOFT' 100;
font-size: 3rem;
font-style: italic;
font-weight: 300;
color: var(--rust);
line-height: 1;
}
.sp-cat-name {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 1.3rem;
line-height: 1.2;
}
.sp-cat-meta {
font-family: 'JetBrains Mono', monospace;
font-size: 0.75rem;
color: var(--ink-fade);
}
.sp-skills {
font-family: 'JetBrains Mono', monospace;
font-size: 0.85rem;
line-height: 1.85;
color: var(--ink-soft);
}
.sp-skill {
display: inline-block;
padding: 2px 8px;
margin: 2px 4px 2px 0;
background: var(--paper-shade);
border: 1px solid var(--rule);
color: var(--ink);
font-size: 0.78rem;
white-space: nowrap;
}
/* ============================================================
FILESYSTEM TREE
============================================================ */
.fs {
margin-top: 32px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.85rem;
line-height: 1.85;
background: var(--ink);
color: var(--paper);
padding: 36px 32px;
border-radius: 2px;
overflow-x: auto;
}
.fs-line {
display: flex;
align-items: center;
gap: 12px;
white-space: nowrap;
}
.fs-path { color: var(--paper); }
.fs-path b { color: var(--amber); font-weight: 600; }
.fs-comment { color: var(--ink-fade); font-style: italic; }
.fs-comment::before { content: '◆ '; color: var(--amber); }
.fs-section-tag {
display: inline-block;
padding: 2px 8px;
margin-right: 8px;
background: rgba(196, 139, 38, 0.2);
color: var(--amber);
font-size: 0.7rem;
letter-spacing: 0.08em;
text-transform: uppercase;
border: 1px solid rgba(196, 139, 38, 0.4);
}
/* ============================================================
MENTAL MODEL
============================================================ */
.mental {
margin-top: 32px;
border: 1px solid var(--ink);
padding: 48px 40px;
background: var(--paper);
position: relative;
}
.mental-stage {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
}
@media (max-width: 900px) {
.mental-stage { grid-template-columns: 1fr; gap: 32px; }
}
.mental-actor {
position: relative;
padding: 24px 20px;
border: 1px solid var(--rule);
background: var(--paper-shade);
}
.mental-actor.you { border-left: 4px solid var(--sage); }
.mental-actor.runtime { border-left: 4px solid var(--blueprint); }
.mental-actor.claude { border-left: 4px solid var(--amber); }
.mental-actor.verifier { border-left: 4px solid var(--rust); }
.mental-tag {
font-family: 'JetBrains Mono', monospace;
font-size: 0.68rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--ink-fade);
margin-bottom: 12px;
}
.mental-name {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 1.2rem;
line-height: 1.1;
margin-bottom: 16px;
}
.mental-actor.you .mental-name { color: var(--sage-deep); }
.mental-actor.runtime .mental-name { color: var(--blueprint-deep); }
.mental-actor.claude .mental-name { color: var(--amber-deep); }
.mental-actor.verifier .mental-name { color: var(--rust); }
.mental-list {
list-style: none;
font-size: 0.85rem;
color: var(--ink-soft);
line-height: 1.55;
}
.mental-list li {
position: relative;
padding-left: 14px;
margin-bottom: 8px;
}
.mental-list li::before {
content: '→';
position: absolute;
left: 0;
color: var(--ink-fade);
}
/* ============================================================
PRACTICAL ACTIONS
============================================================ */
.actions {
margin-top: 32px;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
@media (max-width: 900px) {
.actions { grid-template-columns: 1fr; }
}
.action {
padding: 24px;
background: var(--paper);
border: 1px solid var(--rule);
position: relative;
}
.action-num {
position: absolute;
top: 12px;
right: 16px;
font-family: 'Fraunces', serif;
font-style: italic;
color: var(--blueprint);
font-size: 1.8rem;
font-variation-settings: 'opsz' 144, 'SOFT' 100, 'WONK' 1;
font-weight: 300;
}
.action-want {
font-family: 'Fraunces', serif;
font-style: italic;
font-size: 1.05rem;
color: var(--ink-fade);
margin-bottom: 6px;
}
.action-do {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 1.25rem;
line-height: 1.2;
margin-bottom: 12px;
}
.action-cmd {
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
background: var(--ink);
color: var(--amber);
padding: 12px 14px;
margin-top: 8px;
display: block;
word-break: break-all;
}
/* ============================================================
FOOTER
============================================================ */
.foot {
margin-top: 80px;
padding: 48px 0 64px;
border-top: 2px solid var(--ink);
font-size: 0.85rem;
color: var(--ink-fade);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 16px;
}
.foot .mono { color: var(--ink); }
.foot-ornament {
font-family: 'Fraunces', serif;
font-style: italic;
font-variation-settings: 'opsz' 144, 'SOFT' 100, 'WONK' 1;
font-size: 1.6rem;
font-weight: 300;
color: var(--rust);
}
/* ============================================================
HIERARCHY SECTION
============================================================ */
.hier-section { margin-top: 32px; }
.hier-block {
margin-bottom: 48px;
border: 1px solid var(--rule);
background: var(--paper);
padding: 32px 28px;
position: relative;
}
.hier-block:last-child { margin-bottom: 0; }
.hier-block-tag {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--rust);
margin-bottom: 4px;
}
.hier-block-title {
font-family: 'Fraunces', serif;
font-weight: 500;
font-variation-settings: 'opsz' 48, 'SOFT' 30;
font-size: 1.8rem;
line-height: 1.1;
margin-bottom: 8px;
}
.hier-block-lede {
font-size: 0.92rem;
color: var(--ink-soft);
margin-bottom: 24px;
max-width: 640px;
line-height: 1.5;
}
/* 1. Containment ASCII tree */
.hier-tree {
font-family: 'JetBrains Mono', monospace;
font-size: 0.85rem;
line-height: 1.85;
background: var(--paper-shade);
border-left: 4px solid var(--blueprint);
padding: 20px 24px;
white-space: pre;
overflow-x: auto;
color: var(--ink);
}
.hier-tree .root { color: var(--rust); font-weight: 700; }
.hier-tree .leaf { color: var(--blueprint-deep); }
.hier-tree .note { color: var(--ink-fade); font-style: italic; }
/* 2. Priority chain — numbered list */
.hier-priority {
list-style: none;
counter-reset: prio;
}
.hier-priority-row {
display: grid;
grid-template-columns: 60px 1fr;
align-items: start;
gap: 16px;
padding: 14px 0;
border-bottom: 1px dashed var(--rule);
}
.hier-priority-row:last-child { border-bottom: none; }
.hier-priority-lvl {
font-family: 'Fraunces', serif;
font-style: italic;
font-variation-settings: 'opsz' 144, 'SOFT' 100, 'WONK' 1;
font-weight: 300;
font-size: 2.4rem;
line-height: 0.9;
color: var(--blueprint);
}
.hier-priority-row.locked .hier-priority-lvl { color: var(--rust); }
.hier-priority-content {
display: flex;
flex-direction: column;
gap: 4px;
}
.hier-priority-name {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 1.1rem;
line-height: 1.15;
color: var(--ink);
}
.hier-priority-name .badge {
font-family: 'JetBrains Mono', monospace;
font-size: 0.65rem;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 2px 6px;
background: var(--rust);
color: var(--paper);
margin-left: 8px;
vertical-align: middle;
}
.hier-priority-note {
font-size: 0.85rem;
color: var(--ink-soft);
line-height: 1.45;
}
/* 3. Event timing — horizontal cards */
.hier-timing {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
margin-top: 16px;
}
.hier-event {
padding: 16px 14px 14px;
background: var(--paper-shade);
border-top: 3px solid var(--sage);
position: relative;
}
.hier-event.once { border-top-color: var(--sage); }
.hier-event.per-turn { border-top-color: var(--blueprint); }
.hier-event.per-tool { border-top-color: var(--amber); }
.hier-event.special { border-top-color: var(--rust); }
.hier-event-num {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.1em;
color: var(--ink-fade);
margin-bottom: 4px;
}
.hier-event-name {
font-family: 'JetBrains Mono', monospace;
font-size: 0.95rem;
font-weight: 500;
color: var(--ink);
margin-bottom: 4px;
}
.hier-event-freq {
font-family: 'Fraunces', serif;
font-style: italic;
font-size: 0.82rem;
color: var(--ink-soft);
margin-bottom: 8px;
}
.hier-event-script {
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
color: var(--rust);
}
/* ============================================================
SKILLS DETAIL (replaces sp-skills chip-only)
============================================================ */
.sp-skills-detail {
display: flex;
flex-direction: column;
gap: 0;
}
.sp-skill-row {
display: grid;
grid-template-columns: 220px 1fr;
gap: 20px;
padding: 12px 0;
border-bottom: 1px dashed var(--rule);
align-items: baseline;
}
.sp-skill-row:last-child { border-bottom: none; }
@media (max-width: 720px) {
.sp-skill-row { grid-template-columns: 1fr; gap: 4px; padding: 14px 0; }
}
.sp-skill-key {
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
font-weight: 500;
color: var(--blueprint-deep);
}
.sp-skill-what {
font-size: 0.9rem;
color: var(--ink-soft);
line-height: 1.5;
}
.sp-skill-what em {
font-family: 'Fraunces', serif;
font-style: italic;
color: var(--rust);
font-weight: 500;
}
/* ============================================================
CONNECTIONS GRAPH (§X)
============================================================ */
.graph-controls {
margin-top: 32px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 16px;
flex-wrap: wrap;
padding: 16px 0;
border-top: 1px solid var(--rule);
border-bottom: 1px solid var(--rule);
}
.graph-filters {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.graph-filter {
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 6px 12px;
border: 1px solid var(--rule);
background: var(--paper);
color: var(--ink);
cursor: pointer;
transition: background 200ms, color 200ms;
}
.graph-filter.active {
background: var(--ink);
color: var(--paper);
border-color: var(--ink);
}
.graph-filter:hover { background: var(--paper-shade); }
.graph-filter.active:hover { background: var(--ink-soft); }
.graph-reset {
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 6px 14px;
border: 1px solid var(--rust);
background: var(--paper);
color: var(--rust);
cursor: pointer;
}
.graph-reset:hover { background: var(--rust); color: var(--paper); }
.graph-container {
margin-top: 24px;
display: grid;
grid-template-columns: 1fr 280px;
gap: 24px;
align-items: start;
}
.graph-svg-wrap {
border: 1px solid var(--rule);
background: var(--paper-shade);
background-image:
linear-gradient(rgba(14, 34, 53, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(14, 34, 53, 0.03) 1px, transparent 1px);
background-size: 24px 24px;
overflow: hidden;
min-height: 700px;
}
.graph-svg {
width: 100%;
height: 700px;
display: block;
cursor: default;
}
.graph-sidebar {
border: 1px solid var(--rule);
background: var(--paper);
padding: 20px 18px;
font-size: 0.88rem;
min-height: 200px;
position: relative;
}
.graph-sidebar-empty {
color: var(--ink-fade);
font-style: italic;
font-family: 'Fraunces', serif;
}
.graph-sidebar-title {
font-family: 'Fraunces', serif;
font-weight: 500;
font-size: 1.15rem;
margin-bottom: 4px;
line-height: 1.15;
}
.graph-sidebar-badge {
display: inline-block;
font-family: 'JetBrains Mono', monospace;
font-size: 0.65rem;
letter-spacing: 0.1em;
text-transform: uppercase;
padding: 2px 8px;
background: var(--ink);
color: var(--paper);
margin-bottom: 12px;
}
.graph-sidebar-section {
margin-top: 16px;
padding-top: 12px;
border-top: 1px dashed var(--rule);
}
.graph-sidebar-section.governance {
border-top: 2px solid var(--rust);
padding-top: 14px;
}
.graph-sidebar-navlink {
margin-top: 10px;
padding: 5px 10px;
border: 1px solid var(--blueprint);
background: var(--paper);
color: var(--blueprint-deep);
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
letter-spacing: 0.06em;
text-transform: uppercase;
cursor: pointer;
transition: background 200ms, color 200ms;
}
.graph-sidebar-navlink:hover { background: var(--blueprint); color: var(--paper); }
.graph-sidebar-section-label {
font-family: 'JetBrains Mono', monospace;
font-size: 0.68rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--ink-fade);
margin-bottom: 8px;
}
.graph-conn-list {
list-style: none;
font-family: 'JetBrains Mono', monospace;
font-size: 0.78rem;
line-height: 1.7;
color: var(--ink-soft);
}
.graph-conn-list li b { color: var(--rust); font-weight: 600; }
.graph-close {
position: absolute;
top: 12px;
right: 14px;
border: none;
background: transparent;
font-family: 'JetBrains Mono', monospace;
font-size: 1.2rem;
cursor: pointer;
color: var(--ink-fade);
}
.graph-close:hover { color: var(--rust); }
.graph-legend {
margin-top: 24px;
padding: 20px 24px;
background: var(--paper);
border: 1px solid var(--rule);
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
.graph-legend-section h4 {
font-family: 'JetBrains Mono', monospace;
font-size: 0.68rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--ink-fade);
margin-bottom: 12px;
font-weight: 500;
}
.graph-legend-item {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
font-size: 0.82rem;
color: var(--ink-soft);
}
.graph-legend-swatch {
width: 16px;
height: 16px;
border: 1.5px solid var(--ink);
flex-shrink: 0;
}
.graph-legend-line {
width: 28px;
height: 0;
border-top: 2px solid var(--ink);
flex-shrink: 0;
}
.graph-fallback {
padding: 48px;
text-align: center;
color: var(--rust);
font-family: 'Fraunces', serif;
font-style: italic;
font-size: 1rem;
}
/* SVG node styles */
.gn-plugin { fill: var(--rust); stroke: var(--ink); stroke-width: 2; }
.gn-skill { fill: var(--blueprint); stroke: var(--ink); stroke-width: 1.5; }
.gn-script { fill: var(--amber); stroke: var(--ink); stroke-width: 1.5; }
.gn-event { fill: var(--sage); stroke: var(--ink); stroke-width: 2; }
.gn-state { fill: var(--rust); stroke: var(--ink); stroke-width: 2; }
.gn-perm { fill: var(--ink-fade); stroke: var(--ink); stroke-width: 1.5; }
.gn-rule { fill: var(--ink); stroke: var(--rust); stroke-width: 2; }
.gn-label {
font-family: 'Fraunces', serif;
font-size: 9px;
font-weight: 500;
fill: var(--ink);
pointer-events: none;
text-anchor: middle;
}
.gn-node { cursor: pointer; }
.gn-node:hover { filter: brightness(1.1); }
.dimmed { opacity: 0.15; }
.highlighted { opacity: 1; }
/* SVG link styles */
.gl { fill: none; pointer-events: stroke; }
.gl-contains { stroke: var(--blueprint); stroke-width: 1.5; }
.gl-triggers { stroke: var(--sage); stroke-width: 2; }
.gl-writes { stroke: var(--amber); stroke-width: 2; }
.gl-reads { stroke: var(--amber); stroke-width: 1.5; stroke-dasharray: 4 3; }
.gl-mandates { stroke: var(--rust); stroke-width: 2.5; }
.gl-references { stroke: var(--ink-fade); stroke-width: 1; stroke-dasharray: 2 3; }
.gl-blocks { stroke: var(--rust); stroke-width: 1.5; stroke-dasharray: 5 3; }
.gl-denies { stroke: var(--rust); stroke-width: 2; stroke-dasharray: 2 2; }
.gl-tooltip {
position: absolute;
background: var(--ink);
color: var(--paper);
font-family: 'JetBrains Mono', monospace;
font-size: 0.72rem;
padding: 4px 8px;
pointer-events: none;
z-index: 100;
border: 1px solid var(--paper-shade);
}
@media (max-width: 900px) {
.graph-container { grid-template-columns: 1fr; }
.graph-svg-wrap { overflow-x: auto; }
.graph-svg { min-width: 900px; }
.graph-sidebar {
position: fixed;
bottom: 0; left: 0; right: 0;
border-top: 2px solid var(--ink);
border-left: none; border-right: none; border-bottom: none;
z-index: 50;
max-height: 60vh;
overflow-y: auto;
}
.graph-legend { grid-template-columns: 1fr; }
}
/* ============================================================
PAGE LOAD ANIMATION
============================================================ */
@keyframes rise {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.hero, .section { animation: rise 700ms cubic-bezier(0.2, 0.8, 0.2, 1) backwards; }
.section:nth-of-type(1) { animation-delay: 100ms; }
.section:nth-of-type(2) { animation-delay: 200ms; }
.section:nth-of-type(3) { animation-delay: 300ms; }
.section:nth-of-type(4) { animation-delay: 400ms; }
.section:nth-of-type(5) { animation-delay: 500ms; }
.section:nth-of-type(6) { animation-delay: 600ms; }
.section:nth-of-type(7) { animation-delay: 700ms; }
.section:nth-of-type(8) { animation-delay: 800ms; }
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation: none !important;
transition: none !important;
}
html { scroll-behavior: auto; }
}
/* Decorative SVG underline for hero title */
.hero-title-svg {
display: block;
width: 140px;
margin: 16px 0 0;
}
.hero-title-svg path {
fill: none;
stroke: var(--rust);
stroke-width: 2;
stroke-linecap: round;
}
</style>
</head>
<body>
<main>
<!-- ============================================================
HERO
============================================================ -->
<header class="hero">
<div class="frame">
<div class="hero-meta">
<span class="caps">Carta · vol. i · 2026</span>
<span class="hero-meta-divider"></span>
<span class="mono">10.05.2026</span>
</div>
<h1 class="hero-title display">
Карта работы<br>
<em>hooks</em> · <em>skills</em> · <em>plugins</em>
</h1>
<svg class="hero-title-svg" viewBox="0 0 140 16" xmlns="http://www.w3.org/2000/svg">
<path d="M2 10 Q 35 2, 70 8 T 138 6" />
</svg>
<p class="hero-sub">
Иллюстрированный справочник по внутреннему устройству Claude Code в проекте&nbsp;Лидерра.
Что срабатывает в начале промпта, что — посреди вашей работы, и что — в конце.
<strong>Три типа сущностей, семь хуков, пять событий, четыре плагина.</strong>
</p>
<div class="hero-stamp">
<span class="num">vIII</span>
<span class="lbl">Edition · Carta</span>
</div>
</div>
</header>
<!-- ============================================================
I. THE THREE CONCEPTS
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">I</div>
<h2 class="section-title">Три типа сущностей</h2>
<p class="section-lede">
Чтобы не путаться, важно понимать на каком слое живёт каждая вещь.
Один и тот же объект может быть одновременно частью плагина, скилом по природе и автоматизированным
хуком в действии — но <em>роль</em> у каждого своя.
</p>
<div class="triad">
<div class="triad-cell">
<div class="triad-tag">Слой&nbsp;01 · Доставка</div>
<div class="icon-shape">
<svg width="56" height="56" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
<rect x="8" y="14" width="40" height="32" fill="none" stroke="#0E2235" stroke-width="2"/>
<line x1="8" y1="22" x2="48" y2="22" stroke="#0E2235" stroke-width="2"/>
<line x1="28" y1="14" x2="28" y2="46" stroke="#0E2235" stroke-width="2" stroke-dasharray="2 3"/>
<circle cx="28" cy="22" r="3" fill="#C48B26"/>
</svg>
</div>
<div class="triad-name">Плагин</div>
<div class="triad-eq">= коробка / упаковка</div>
<div class="triad-body">
Контейнер. Сам ничего не исполняет — только доставляет в систему скилы, slash-команды,
хуки, MCP-серверы и subagent'ы. Как rpm-пакет или npm-модуль: внутри что-то полезное, наружу — манифест.
</div>
<div class="triad-key">
<b>Установка:</b> через marketplace в <span class="mono">~/.claude/settings.json</span>.
</div>
</div>
<div class="triad-cell">
<div class="triad-tag">Слой&nbsp;02 · Инструкция</div>
<div class="icon-shape">
<svg width="56" height="56" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
<path d="M14 8 L42 8 L42 48 L14 48 Z" fill="none" stroke="#0E2235" stroke-width="2"/>
<line x1="20" y1="16" x2="36" y2="16" stroke="#0E2235" stroke-width="2"/>
<line x1="20" y1="24" x2="36" y2="24" stroke="#0E2235" stroke-width="1.5"/>
<line x1="20" y1="30" x2="32" y2="30" stroke="#0E2235" stroke-width="1.5"/>
<line x1="20" y1="36" x2="36" y2="36" stroke="#0E2235" stroke-width="1.5"/>
</svg>
</div>
<div class="triad-name">Скил<em>·</em></div>
<div class="triad-eq">= инструкция в markdown</div>
<div class="triad-body">
Текст, который я (Claude) читаю и которому следую. Например, «брейнсторм, потом план, потом TDD».
Я <strong>могу его проигнорировать</strong> — это soft-правило: дисциплинированное самоприменение,
а не runtime gate.
</div>
<div class="triad-key">
<b>Вызов:</b> через <span class="mono">Skill</span>-tool или slash <span class="mono">/имя</span>.
</div>
</div>
<div class="triad-cell">
<div class="triad-tag">Слой&nbsp;03 · Принуждение</div>
<div class="icon-shape">
<svg width="56" height="56" viewBox="0 0 56 56" xmlns="http://www.w3.org/2000/svg">
<circle cx="28" cy="28" r="18" fill="none" stroke="#8B3A1B" stroke-width="2"/>
<rect x="22" y="22" width="12" height="12" fill="#8B3A1B"/>
<line x1="4" y1="28" x2="10" y2="28" stroke="#0E2235" stroke-width="2"/>
<line x1="46" y1="28" x2="52" y2="28" stroke="#0E2235" stroke-width="2"/>
<line x1="28" y1="4" x2="28" y2="10" stroke="#0E2235" stroke-width="2"/>
<line x1="28" y1="46" x2="28" y2="52" stroke="#0E2235" stroke-width="2"/>
</svg>
</div>
<div class="triad-name">Хук</div>
<div class="triad-eq">= shell-скрипт runtime'а</div>
<div class="triad-body">
Команда, которую запускает <strong>сам Claude Code</strong> в момент события (UserPromptSubmit, Stop, etc).
Я <strong>не могу его обойти</strong> и не вижу момент запуска. Это реальный gate.
</div>
<div class="triad-key">
<b>Зарегистрирован:</b> в <span class="mono">hooks</span> блоке settings.json.
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
II. HIERARCHY & ORDER
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">II</div>
<h2 class="section-title">Иерархия и&nbsp;порядок</h2>
<p class="section-lede">
Три разные «иерархии» накладываются друг на друга и часто путают.
<em>Контейнерная</em> отвечает на «что внутри чего».
<em>Приоритетная</em> — на «что важнее при конфликте».
<em>Временная</em> — на «что срабатывает раньше».
</p>
<div class="hier-section">
<!-- 1. CONTAINMENT -->
<div class="hier-block">
<div class="hier-block-tag">1 · Контейнерная</div>
<h3 class="hier-block-title">Что внутри чего лежит</h3>
<p class="hier-block-lede">
Плагин — упаковка. Содержит скилы, хуки, slash-команды, MCP-серверы, subagent'ы.
Сам плагин ничего не делает. Хуки регистрируются в&nbsp;settings.json и&nbsp;запускаются
самим Claude Code. Скилы — это .md-файлы, которые я читаю и которым следую.
</p>
<div class="hier-tree"><span class="root">ПЛАГИН</span> <span class="note">упаковка / контейнер</span>
├─ <span class="leaf">Skills</span> .md-инструкции для Claude
├─ <span class="leaf">Hooks</span> Python/Bash скрипты + регистрация в settings.json
├─ <span class="leaf">Slash-commands</span> /имя в промпте → запускает skill или агента
├─ <span class="leaf">MCP servers</span> внешние tool-провайдеры
└─ <span class="leaf">Subagents</span> специализированные агенты-исполнители
<span class="root">settings.json</span> <span class="note">главный runtime конфиг</span>
├─ <span class="leaf">enabledPlugins</span> что включить
├─ <span class="leaf">permissions</span> allow / deny / ask правила
├─ <span class="leaf">hooks</span> event → matcher → handler
└─ <span class="leaf">env / mcp / ...</span> остальное
<span class="note">─── ПОТОКИ ВЛИЯНИЯ ───────────────────────────────────</span>
Hook script ──> может запустить ЛЮБУЮ команду (Python, Bash, agent)
Skill markdown ──> я читаю и следую (но могу проигнорировать)
Plugin ──> ничего не делает сам — только доставляет содержимое</div>
</div>
<!-- 2. PRIORITY CHAIN -->
<div class="hier-block">
<div class="hier-block-tag">2 · Приоритетная</div>
<h3 class="hier-block-title">Что override'ит что при конфликте</h3>
<p class="hier-block-lede">
Когда правила противоречат — побеждает <em>верхний</em> уровень.
Так договорились в&nbsp;CLAUDE.md §1. Уровень 0 — единственный неотменяемый
(даже &laquo;отступления&raquo; не отменяют его).
</p>
<div class="hier-priority">
<div class="hier-priority-row locked">
<div class="hier-priority-lvl">0</div>
<div class="hier-priority-content">
<div class="hier-priority-name">Pravila §12 <span class="badge">hard rule</span></div>
<div class="hier-priority-note">Superpowers hard rule — инвокировать skill ПЕРВЫМ для подходящих задач.
§9 «Отступления» к этому не применяется. Неотменяемо.</div>
</div>
</div>
<div class="hier-priority-row">
<div class="hier-priority-lvl">1</div>
<div class="hier-priority-content">
<div class="hier-priority-name">Pravila_raboty_Claude_v1_1.md</div>
<div class="hier-priority-note">Продуктовые правила работы Claude, утверждены заказчиком (~13 секций).</div>
</div>
</div>
<div class="hier-priority-row">
<div class="hier-priority-lvl">2a</div>
<div class="hier-priority-content">
<div class="hier-priority-name">CLAUDE.md</div>
<div class="hier-priority-note">Общая оперативная карта проекта, ссылается на источники истины.</div>
</div>
</div>
<div class="hier-priority-row">
<div class="hier-priority-lvl">2b</div>
<div class="hier-priority-content">
<div class="hier-priority-name">Tooling Прил.&nbsp;Н</div>
<div class="hier-priority-note">Детальный реестр 33 инструментов. При конфликте с 2a — приоритет у CLAUDE.md как корневой карты.</div>
</div>
</div>
<div class="hier-priority-row">
<div class="hier-priority-lvl">3</div>
<div class="hier-priority-content">
<div class="hier-priority-name">Plugin_stack_rules_v1.md</div>
<div class="hier-priority-note">Координирующий слой между плагинами (Superpowers + Frontend Design + UPM + 21st Magic), 16 правил.</div>
</div>
</div>
<div class="hier-priority-row">
<div class="hier-priority-lvl">4</div>
<div class="hier-priority-content">
<div class="hier-priority-name">~/.claude/settings.json</div>
<div class="hier-priority-note">Runtime config: хуки, permissions, плагины. Исполняется средой Claude Code, не мной.</div>
</div>
</div>
<div class="hier-priority-row">
<div class="hier-priority-lvl">5</div>
<div class="hier-priority-content">
<div class="hier-priority-name">memory/*.md</div>
<div class="hier-priority-note">Кросс-сессионные факты о проекте, заказчике, окружении. Может быть stale — нужно verify.</div>
</div>
</div>
<div class="hier-priority-row">
<div class="hier-priority-lvl">6</div>
<div class="hier-priority-content">
<div class="hier-priority-name">Прочие плагины</div>
<div class="hier-priority-note">claude-md-management, ui-ux-pro-max, frontend-design, и&nbsp;т.&nbsp;д. — поведенческие подсказки.</div>
</div>
</div>
</div>
</div>
<!-- 3. TIMING -->
<div class="hier-block">
<div class="hier-block-tag">3 · Временная</div>
<h3 class="hier-block-title">В&nbsp;каком порядке всё срабатывает</h3>
<p class="hier-block-lede">
События имеют фиксированный порядок в&nbsp;жизненном цикле сессии. Цвет верхней полосы
— это частота срабатывания: <em>один раз</em> (зелёный), <em>на каждый submit</em> (синий),
<em>на каждый tool call</em> (янтарь), <em>нерегулярно</em> (ржавый).
</p>
<div class="hier-timing">
<div class="hier-event once">
<div class="hier-event-num">1·once</div>
<div class="hier-event-name">SessionStart</div>
<div class="hier-event-freq">один раз на старте сессии</div>
<div class="hier-event-script">economy-self-check.py</div>
</div>
<div class="hier-event per-turn">
<div class="hier-event-num">2·per submit</div>
<div class="hier-event-name">UserPromptSubmit</div>
<div class="hier-event-freq">каждый раз когда вы отправляете промпт</div>
<div class="hier-event-script">economy-mode.py</div>
</div>
<div class="hier-event per-tool">
<div class="hier-event-num">3·per tool</div>
<div class="hier-event-name">PreToolUse</div>
<div class="hier-event-freq">перед каждым моим tool-вызовом</div>
<div class="hier-event-script">skill-marker · skill-check · state-guard</div>
</div>
<div class="hier-event special">
<div class="hier-event-num">4·per response</div>
<div class="hier-event-name">Stop</div>
<div class="hier-event-freq">конец моего ответа на вас</div>
<div class="hier-event-script">Sonnet 4.6 verifier (agent-type)</div>
</div>
<div class="hier-event special">
<div class="hier-event-num">5·irregular</div>
<div class="hier-event-name">PostCompact</div>
<div class="hier-event-freq">когда runtime сжимает старые сообщения</div>
<div class="hier-event-script">economy-postcompact.py</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
III. TIMELINE OF A TURN
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">III</div>
<h2 class="section-title">Хронология одного запроса</h2>
<p class="section-lede">
Что именно происходит за кулисами, пока вы пишете промпт и читаете ответ.
Пять чёрных дисков — главные события. Внутренний прямоугольник —
то, что повторяется на каждый мой tool-вызов.
</p>
<div class="timeline">
<div class="timeline-rail">
<div class="timeline-line"></div>
<div class="timeline-steps">
<div class="t-step">
<div class="t-step-dot user"></div>
<div class="t-step-label">Шаг 1</div>
<div class="t-step-title">Вы пишете промпт</div>
<div class="t-step-desc">с опциональным «экономия N%» в самом конце</div>
</div>
<div class="t-step">
<div class="t-step-dot fire"></div>
<div class="t-step-label">UserPromptSubmit</div>
<div class="t-step-title">Парсер уровня</div>
<div class="t-step-desc">парсит экономию, пишет state-файл, инжектит правила в мой контекст</div>
<div class="t-step-script">economy-mode.py</div>
</div>
<div class="t-step">
<div class="t-step-dot"></div>
<div class="t-step-label">Шаг 3</div>
<div class="t-step-title">Я думаю и действую</div>
<div class="t-step-desc">читаю, вызываю инструменты, привожу аргументы</div>
</div>
<div class="t-step">
<div class="t-step-dot danger"></div>
<div class="t-step-label">Stop · agent</div>
<div class="t-step-title">Верификатор</div>
<div class="t-step-desc">Sonnet&nbsp;4.6 читает ответ, ловит cherry-pick и блокирует</div>
<div class="t-step-script">claude-sonnet-4-6</div>
</div>
<div class="t-step">
<div class="t-step-dot done"></div>
<div class="t-step-label">Шаг 5</div>
<div class="t-step-title">Вы видите ответ</div>
<div class="t-step-desc">проверенный, или с пометкой о повторной попытке</div>
</div>
</div>
<div class="timeline-inner">
<span class="timeline-inner-tag">Внутри шага 3 · PreToolUse</span>
<div class="timeline-inner-grid">
<div class="t-inner-step">
<div class="t-inner-title">Маркер скила</div>
<div class="t-inner-trigger">matcher: Skill</div>
<div class="t-inner-desc">Каждый раз, когда я вызываю Skill — фиксирует «отметку» в темпе сессии.
Часть механизма «дисциплина».</div>
<div class="t-step-script" style="margin-top:8px;">skill-marker.py</div>
</div>
<div class="t-inner-step">
<div class="t-inner-title">Проверка дисциплины</div>
<div class="t-inner-trigger">matcher: Edit|Write|MultiEdit</div>
<div class="t-inner-desc">Если я делаю правки без вызова скила — напоминает о §12 правил Claude.
Не блокирует, только подсказывает.</div>
<div class="t-step-script" style="margin-top:8px;">skill-check.py</div>
</div>
<div class="t-inner-step special">
<div class="t-inner-title">Страж экономии</div>
<div class="t-inner-trigger">matcher: Edit|Write|MultiEdit|Bash|Agent</div>
<div class="t-inner-desc">Напоминает активный уровень при каждом действии. Ловит Bash-обходы (<span class="mono">sed&nbsp;-i</span>, <span class="mono">echo&nbsp;&gt;</span>).
Передаёт уровень subagent'у.</div>
<div class="t-step-script" style="margin-top:8px;">economy-state-guard.py</div>
</div>
</div>
</div>
<div class="timeline-parallel">
<div class="timeline-parallel-title">Происходит отдельно</div>
<div class="timeline-parallel-list">
<div class="tp-item">
<span class="tp-item-when">На&nbsp;старте сессии</span>
<span class="tp-item-what">
<strong>economy-self-check.py</strong> проверяет, что все 5 hook-скриптов и Python на месте,
а settings.json валиден. При проблемах — видимое предупреждение в UI.
</span>
</div>
<div class="tp-item">
<span class="tp-item-when">После авто-сжатия</span>
<span class="tp-item-what">
<strong>economy-postcompact.py</strong> re-injects активный уровень обратно в контекст,
чтобы я не «забывал» правила после потери начала сессии.
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
III. HOOK ROSTER
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">IV</div>
<h2 class="section-title">Реестр хуков — построчно</h2>
<p class="section-lede">
Семь активных хуков, четыре уникальных события, два типа исполнителей.
Шесть хуков — обычные shell-команды (Python-скрипты), один — agent-type с моделью Sonnet&nbsp;4.6.
</p>
<div class="roster">
<div class="roster-row header">
<div>Событие</div>
<div>Matcher</div>
<div>Что делает</div>
<div>Тип</div>
</div>
<div class="roster-row">
<div class="r-event">SessionStart</div>
<div class="r-matcher">(any)</div>
<div class="r-desc">
<strong>economy-self-check.py</strong> · Проверяет инфраструктуру: hook-скрипты, Python в PATH, settings.json.
</div>
<div class="r-type cmd">command</div>
</div>
<div class="roster-row">
<div class="r-event">UserPromptSubmit</div>
<div class="r-matcher">(any)</div>
<div class="r-desc">
<strong>economy-mode.py</strong> · Парсит «экономия N%» в конце промпта, пишет state, инжектит правила.
</div>
<div class="r-type cmd">command</div>
</div>
<div class="roster-row">
<div class="r-event">PreToolUse</div>
<div class="r-matcher">Skill</div>
<div class="r-desc">
<strong>skill-marker.py</strong> · «Дисциплина»: ставит per-session отметку «скил был вызван».
</div>
<div class="r-type cmd">command</div>
</div>
<div class="roster-row">
<div class="r-event">PreToolUse</div>
<div class="r-matcher">Edit|Write|MultiEdit</div>
<div class="r-desc">
<strong>skill-check.py</strong> · «Дисциплина»: если отметки нет — напоминание о §12 правил Claude.
</div>
<div class="r-type cmd">command</div>
</div>
<div class="roster-row">
<div class="r-event">PreToolUse</div>
<div class="r-matcher">Edit|Write|MultiEdit|Bash|Agent</div>
<div class="r-desc">
<strong>economy-state-guard.py</strong> · Reminder активного уровня + Bash-bypass detection + subagent inheritance.
</div>
<div class="r-type cmd">command</div>
</div>
<div class="roster-row">
<div class="r-event">PostCompact</div>
<div class="r-matcher">(any)</div>
<div class="r-desc">
<strong>economy-postcompact.py</strong> · После авто-сжатия истории re-injects правила активного уровня.
</div>
<div class="r-type cmd">command</div>
</div>
<div class="roster-row">
<div class="r-event">Stop</div>
<div class="r-matcher">(any)</div>
<div class="r-desc">
<strong>Sonnet 4.6 verifier</strong> · Inline agent в settings.json. Читает мой ответ + tool calls,
эмитит decision: «block» при нарушениях. Timeout 90&nbsp;сек.
</div>
<div class="r-type agent">agent</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
IV. PERMISSIONS BLOCK
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">V</div>
<h2 class="section-title">Блок разрешений — Stage&nbsp;0&nbsp;ratchet</h2>
<p class="section-lede">
Декларативная защита от обхода. Эти правила в settings.json исполняются Claude Code <em>до</em> того,
как я успею что-то сделать. После их установки даже я сам не могу нейтрализовать систему через Edit или Bash —
проверено на практике.
</p>
<div class="perms">
<div class="perm-card allow">
<span class="perm-card-num">1</span>
<div class="perm-card-label">allow</div>
<div class="perm-card-title">Разрешено<br>без вопросов</div>
<div class="perm-card-body">
Одно правило: <span class="mono">Bash(git push origin main:*)</span> — пуш в main без диалога.
Всё остальное идёт по обычным правилам Claude Code.
</div>
</div>
<div class="perm-card deny">
<span class="perm-card-num">7</span>
<div class="perm-card-label">deny</div>
<div class="perm-card-title">Жёстко<br>заблокировано</div>
<div class="perm-card-body">
Семь паттернов: <span class="mono">rm</span> и <span class="mono">mv</span> на hook-скрипты,
на settings.json, и на state-файлы экономии. Никаких prompts — просто отказ.
</div>
</div>
<div class="perm-card ask">
<span class="perm-card-num">16</span>
<div class="perm-card-label">ask</div>
<div class="perm-card-title">Только<br>с вашим approve</div>
<div class="perm-card-body">
Шестнадцать правил: каждый Edit и Write на settings.json или любой hook-скрипт триггерит
permission prompt. Вы видите, что я хочу сделать, и решаете.
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
V. PLUGINS
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">VI</div>
<h2 class="section-title">Установленные плагины</h2>
<p class="section-lede">
Четыре плагина, доставленных через marketplace. Каждый включает в себя один или больше скилов.
Сами плагины ничего не делают — только привозят содержимое.
</p>
<div class="plugins">
<div class="plugin-card">
<div class="plugin-card-header">
<div class="plugin-card-name">superpowers</div>
<div class="plugin-card-scope">obra/superpowers · v5.1.0</div>
</div>
<div class="plugin-card-desc">
Главный плагин дисциплины процесса работы. Содержит 14 скилов: brainstorm, plan, execute,
TDD, debug, code-review, verify, parallel agents, worktrees, finishing-branch, writing-skills…
Связан с §12&nbsp;Pravila — half-вес проекта.
</div>
<div class="plugin-skills-list">
14&nbsp;skills · процесс работы
</div>
</div>
<div class="plugin-card">
<div class="plugin-card-header">
<div class="plugin-card-name">claude-md-management</div>
<div class="plugin-card-scope">anthropic · claude-plugins-official</div>
</div>
<div class="plugin-card-desc">
Единственный канал правок корневого CLAUDE.md. Не даёт ему расходиться с Pravila и Tooling.
Содержит claude-md-improver (audit) и revise-claude-md (capture learnings).
</div>
<div class="plugin-skills-list">
2&nbsp;skills · инфраструктура CLAUDE.md
</div>
</div>
<div class="plugin-card">
<div class="plugin-card-header">
<div class="plugin-card-name">frontend-design</div>
<div class="plugin-card-scope">anthropic · claude-plugins-official</div>
</div>
<div class="plugin-card-desc">
Помощник для построения отличающихся frontend-интерфейсов. Знает 50+ стилей,
принципы типографики, цвета, motion, layout. Используется для UI работы — например, для
этого документа.
</div>
<div class="plugin-skills-list">
1&nbsp;skill · UI/UX дизайн
</div>
</div>
<div class="plugin-card">
<div class="plugin-card-header">
<div class="plugin-card-name">ui-ux-pro-max</div>
<div class="plugin-card-scope">nextlevelbuilder · ui-ux-pro-max-skill</div>
</div>
<div class="plugin-card-desc">
Резерв-библиотека: 50+ стилей, 161 палитра, 57 пар шрифтов, 161 тип продукта, 99 UX-гайдлайнов.
Off-phase tool — активируется только через R14 pipeline в Plugin Stack Rules.
</div>
<div class="plugin-skills-list">
1&nbsp;skill · резерв-библиотека UI/UX
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
VI. SKILLS BY PURPOSE
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">VII</div>
<h2 class="section-title">Скилы по&nbsp;плагинам</h2>
<p class="section-lede">
Все 28 скилов сгруппированы по родительскому плагину. 18 принадлежат
одному из 4 установленных плагинов, 10 — &laquo;standalone / встроенные&raquo;
(не доставляются плагином, идут с Claude Code как часть базовой системы).
</p>
<div class="skills-purpose">
<div class="sp-row">
<div class="sp-cat">
<div class="sp-cat-symbol"></div>
<div class="sp-cat-name">superpowers</div>
<div class="sp-cat-meta">14&nbsp;skills · obra/superpowers v5.1.0</div>
</div>
<div class="sp-skills-detail">
<div class="sp-skill-row">
<div class="sp-skill-key">brainstorming</div>
<div class="sp-skill-what"><em>Идея → спек.</em> Через диалог уточняет требования, предлагает 2-3 подхода с trade-off'ами. Не позволяет писать код до утверждения дизайна.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">writing-plans</div>
<div class="sp-skill-what"><em>Спек → пошаговый план.</em> Bite-sized задачи (2-5 мин каждая) с точными путями файлов, кодом, командами и TDD-структурой.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">executing-plans</div>
<div class="sp-skill-what"><em>План → выполнение.</em> Исполняет существующий план по шагам с checkpoint'ами для ревью между задачами.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">test-driven-development</div>
<div class="sp-skill-what"><em>Iron law: тест ДО кода.</em> Red (увидеть как тест падает), потом green (минимальная реализация), потом refactor. Никаких «тестов после».</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">systematic-debugging</div>
<div class="sp-skill-what"><em>4 фазы перед любым фиксом.</em> Root cause → pattern → hypothesis → fix. Минимум 3 гипотезы. Запрет «быстрых патчей».</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">requesting-code-review</div>
<div class="sp-skill-what"><em>Перед merge / PR.</em> Двухстадийный review через subagent'ы: spec compliance + code quality.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">receiving-code-review</div>
<div class="sp-skill-what"><em>Когда получил feedback ревью.</em> Правила обработки замечаний без слепого согласия и без отвержения любой критики.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">verification-before-completion</div>
<div class="sp-skill-what"><em>Перед claim «готово».</em> Обязательно запустить verification команду, проверить exit code и output. Никаких «должно работать».</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">finishing-a-development-branch</div>
<div class="sp-skill-what"><em>Когда работа на ветке закончена.</em> Структурный выбор: merge / PR / cleanup с обоснованием каждого варианта.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">using-git-worktrees</div>
<div class="sp-skill-what"><em>Изоляция feature-работы.</em> Через `git worktree` — отдельный workspace вне current branch.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">subagent-driven-development</div>
<div class="sp-skill-what"><em>Диспатч свежего subagent'а на каждую задачу плана.</em> Изоляция контекста + 2-стадийный review между задачами.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">dispatching-parallel-agents</div>
<div class="sp-skill-what"><em>2+ независимых задачи без shared state.</em> Параллельные subagent'ы вместо последовательного выполнения. Экономия времени.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">using-superpowers</div>
<div class="sp-skill-what"><em>Базовый skill про другие skills.</em> Объясняет как находить и инвокировать остальные. Обычно срабатывает в начале сессии.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">writing-skills</div>
<div class="sp-skill-what"><em>Когда создаёшь новый skill.</em> Правила его написания, валидации и deployment.</div>
</div>
</div>
</div>
<div class="sp-row">
<div class="sp-cat">
<div class="sp-cat-symbol"></div>
<div class="sp-cat-name">claude-md-management</div>
<div class="sp-cat-meta">2&nbsp;skills · anthropic · claude-plugins-official</div>
</div>
<div class="sp-skills-detail">
<div class="sp-skill-row">
<div class="sp-skill-key">claude-md-improver</div>
<div class="sp-skill-what"><em>Audit + targeted updates CLAUDE.md.</em> Единственный канал структурных правок: добавление/удаление секций, версии в&nbsp;шапке, правки правил.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">revise-claude-md</div>
<div class="sp-skill-what"><em>Захват session-learnings.</em> Новые квирки, команды, паттерны из текущей сессии → автоматически в CLAUDE.md.</div>
</div>
</div>
</div>
<div class="sp-row">
<div class="sp-cat">
<div class="sp-cat-symbol"></div>
<div class="sp-cat-name">frontend-design</div>
<div class="sp-cat-meta">1&nbsp;skill · anthropic · claude-plugins-official</div>
</div>
<div class="sp-skills-detail">
<div class="sp-skill-row">
<div class="sp-skill-key">frontend-design</div>
<div class="sp-skill-what"><em>Создание distinctive UI.</em> Избегает generic AI-aesthetics. Distinctive fonts, bold direction, осознанные дизайн-выборы. Эта страница построена через него.</div>
</div>
</div>
</div>
<div class="sp-row">
<div class="sp-cat">
<div class="sp-cat-symbol"></div>
<div class="sp-cat-name">ui-ux-pro-max</div>
<div class="sp-cat-meta">1&nbsp;skill · nextlevelbuilder · ui-ux-pro-max-skill</div>
</div>
<div class="sp-skills-detail">
<div class="sp-skill-row">
<div class="sp-skill-key">ui-ux-pro-max</div>
<div class="sp-skill-what"><em>Резерв-библиотека UI/UX.</em> 50+ стилей, 161 палитра, 57 пар шрифтов, 99 UX guidelines, 25 типов графиков для 10 стеков. Активируется через PSR_v1 R14 pipeline.</div>
</div>
</div>
</div>
<div class="sp-row">
<div class="sp-cat">
<div class="sp-cat-symbol"></div>
<div class="sp-cat-name">Standalone / встроенные</div>
<div class="sp-cat-meta">10&nbsp;skills · без плагина (часть базовой системы Claude Code)</div>
</div>
<div class="sp-skills-detail">
<div class="sp-skill-row">
<div class="sp-skill-key">update-config</div>
<div class="sp-skill-what"><em>Правки settings.json.</em> Хуки, permissions, env vars, MCP servers, plugins. Любая автоматизация behaviour'а — через этот skill.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">keybindings-help</div>
<div class="sp-skill-what"><em>Клавиатурные сокращения.</em> Настройка `~/.claude/keybindings.json` — chord-bindings, rebind keys.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">simplify</div>
<div class="sp-skill-what"><em>Review недавнего кода.</em> Reuse / quality / efficiency. Находит дубли, over-engineering, dead code — потом фиксит.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">fewer-permission-prompts</div>
<div class="sp-skill-what"><em>Снижение шума permission prompts.</em> Сканирует transcript'ы, добавляет allowlist в settings.json для частых read-only действий.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">init</div>
<div class="sp-skill-what"><em>Новый проект → новый CLAUDE.md.</em> Инициализация документации для нового codebase с анализом структуры.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">claude-api</div>
<div class="sp-skill-what"><em>Claude API / Anthropic SDK apps.</em> Build / debug / optimize. Также миграция между версиями моделей (4.5 → 4.6 → 4.7).</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">loop</div>
<div class="sp-skill-what"><em>Запуск prompt'а на recurring interval.</em> Например `/loop 5m /foo` — каждые 5 мин. Или self-paced если без интервала. Для polling, мониторинга.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">schedule</div>
<div class="sp-skill-what"><em>Cron-расписания для агентов.</em> Создание / управление scheduled remote routines. Также one-time scheduled запуски («сделай это завтра в 15:00»).</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">review</div>
<div class="sp-skill-what"><em>Review текущего PR на GitHub.</em> Чтение diff'а, замечания по логике, стилю, тестам. Подготовка к merge.</div>
</div>
<div class="sp-skill-row">
<div class="sp-skill-key">security-review</div>
<div class="sp-skill-what"><em>Security audit pending changes.</em> Полный security review текущей ветки: OWASP top 10, secrets, RLS, injection vectors.</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
VII. FILESYSTEM
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">VIII</div>
<h2 class="section-title">Карта на&nbsp;диске</h2>
<p class="section-lede">
Где физически лежит каждый компонент. Хук-скрипты живут отдельно от проекта Лидерры —
это пользовательская инфраструктура Claude Code. Спеки и планы — в репозитории проекта.
</p>
<div class="fs">
<pre style="margin:0;">
<span class="fs-section-tag">user</span><span class="fs-path">C:\Users\Administrator\<b>.claude\</b></span>
├── settings.json <span class="fs-comment">главный конфиг: permissions + hooks</span>
├── settings.json.backup-pre-economy-hardening
├── <span class="fs-path"><b>hooks/</b></span> <span class="fs-comment">наши Python-скрипты хуков</span>
│ ├── skill-marker.py <span class="fs-comment">PreToolUse(Skill)</span>
│ ├── skill-check.py <span class="fs-comment">PreToolUse(Edit|Write|MultiEdit)</span>
│ ├── economy-mode.py (v3) <span class="fs-comment">UserPromptSubmit + state writer</span>
│ ├── economy-mode-test.py <span class="fs-comment">54 теста</span>
│ ├── economy-self-check.py <span class="fs-comment">SessionStart</span>
│ ├── economy-self-check-test.py <span class="fs-comment">4 теста</span>
│ ├── economy-state-guard.py <span class="fs-comment">PreToolUse + Bash detection</span>
│ ├── economy-state-guard-test.py <span class="fs-comment">6 тестов</span>
│ ├── economy-verifier.py <span class="fs-comment">Stop wrapper</span>
│ └── economy-postcompact.py <span class="fs-comment">PostCompact</span>
├── <span class="fs-path"><b>plugins/cache/</b></span> <span class="fs-comment">загруженные плагины</span>
│ ├── superpowers-dev/superpowers/5.1.0/
│ ├── claude-plugins-official/claude-md-management/
│ ├── claude-plugins-official/frontend-design/
│ └── ui-ux-pro-max-skill/ui-ux-pro-max/
└── <span class="fs-path"><b>projects/&lt;cwd&gt;/memory/</b></span> <span class="fs-comment">долгосрочная память</span>
├── MEMORY.md (index)
├── feedback_superpowers_hard_rule.md
├── project_state.md
└── reference_github.md
<span class="fs-section-tag">project</span><span class="fs-path">c:\моя\проекты\портал crm\<b>Документация\</b></span>
├── CLAUDE.md <span class="fs-comment">главная карта проекта</span>
├── cspell-words.txt <span class="fs-comment">словарь cspell для pre-commit</span>
├── docs/Pravila_raboty_Claude_v1_1.md <span class="fs-comment">§12 hard rule</span>
├── docs/Plugin_stack_rules_v1.md <span class="fs-comment">16 правил координации</span>
├── docs/Tooling_v8_3.md <span class="fs-comment">реестр 33 инструментов</span>
├── docs/superpowers/specs/ <span class="fs-comment">спеки фич (этот документ — для visualization)</span>
│ └── 2026-05-10-economy-hook-bypass-closure-design.md
├── docs/superpowers/plans/ <span class="fs-comment">пошаговые планы</span>
│ └── 2026-05-10-economy-hook-bypass-closure.md
└── docs/visualizations/ <span class="fs-comment">визуализации (вы тут)</span>
└── hooks-skills-plugins-map.html
</pre>
</div>
</div>
</section>
<!-- ============================================================
VIII. MENTAL MODEL
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">IX</div>
<h2 class="section-title">Кто за&nbsp;что отвечает</h2>
<p class="section-lede">
Четыре действующих лица — четыре роли. Каждый делает то, что больше никто не может,
и не лезет в чужой огород. Это про разделение обязанностей в системе.
</p>
<div class="mental">
<div class="mental-stage">
<div class="mental-actor you">
<div class="mental-tag">Actor&nbsp;01</div>
<div class="mental-name">Вы (Дмитрий)</div>
<ul class="mental-list">
<li>Пишете промпт, опционально ставите «экономия N%» в конце</li>
<li>Approve'ите permission prompts на правки hook-файлов</li>
<li>Можете выключить хуки через settings.json &rarr; disableAllHooks</li>
<li>Вне Claude Code — единственный, кто может править settings/hooks без вопросов</li>
</ul>
</div>
<div class="mental-actor runtime">
<div class="mental-tag">Actor&nbsp;02</div>
<div class="mental-name">Claude Code Runtime</div>
<ul class="mental-list">
<li>Запускает хуки на нужных событиях, не спрашивая меня</li>
<li>Применяет permissions (deny/ask) к моим действиям</li>
<li>Передаёт мне injected additionalContext от хуков</li>
<li>Auto-mode classifier ловит мои попытки обхода</li>
</ul>
</div>
<div class="mental-actor claude">
<div class="mental-tag">Actor&nbsp;03</div>
<div class="mental-name">Я (Claude Opus 4.7)</div>
<ul class="mental-list">
<li>Читаю injected правила и стараюсь им следовать</li>
<li>Вызываю skills — добровольно или по §12 требованию</li>
<li>Использую tools: Edit/Write/Bash/Read/Skill/etc.</li>
<li><em>Не могу</em> обойти хуки или permissions — проверено</li>
</ul>
</div>
<div class="mental-actor verifier">
<div class="mental-tag">Actor&nbsp;04</div>
<div class="mental-name">Sonnet 4.6 verifier</div>
<ul class="mental-list">
<li>Читает мой финальный ответ + последние tool calls</li>
<li>Сравнивает с активным уровнем экономии</li>
<li>При cherry-pick или claim'е без evidence — <strong>блок</strong></li>
<li>До 3 retry попыток за turn, потом — эскалация к вам</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
X. CONNECTIONS GRAPH
============================================================ -->
<section class="section" id="graph-section">
<div class="frame">
<div class="section-num display-i">X</div>
<h2 class="section-title">Связи &mdash; интерактивная карта</h2>
<p class="section-lede">
50 узлов, 52 ребра, 8 типов связей. Кликни на любой узел &mdash;
подсветятся все его связи + откроется панель с деталями справа.
Тяни узлы мышкой для перестановки. Фильтры в верхней панели прячут
категории по одной.
</p>
<div class="graph-controls">
<div class="graph-filters">
<button class="graph-filter active" data-type="plugin">Плагины</button>
<button class="graph-filter active" data-type="skill">Скилы</button>
<button class="graph-filter active" data-type="script">Скрипты</button>
<button class="graph-filter active" data-type="event">События</button>
<button class="graph-filter active" data-type="state">Состояние</button>
<button class="graph-filter active" data-type="perm">Права</button>
<button class="graph-filter active" data-type="rule">Правила</button>
</div>
<button class="graph-reset">Сбросить расположение</button>
</div>
<div class="graph-container">
<div class="graph-svg-wrap">
<svg class="graph-svg" viewBox="0 0 1100 700" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Граф связей: 50 узлов, 52 ребра, 8 типов связей между плагинами, скилами, хуками и правилами"></svg>
</div>
<aside class="graph-sidebar">
<div class="graph-sidebar-empty">
Кликни на узел чтобы увидеть его связи&hellip;
</div>
</aside>
</div>
<div class="graph-legend">
<div class="graph-legend-section">
<h4>Узлы (категории)</h4>
<div class="graph-legend-item"><span class="graph-legend-swatch" style="background:var(--rust); border-radius:50%;"></span>Плагин (4)</div>
<div class="graph-legend-item"><span class="graph-legend-swatch" style="background:var(--blueprint); border-radius:50%;"></span>Скил (28)</div>
<div class="graph-legend-item"><span class="graph-legend-swatch" style="background:var(--amber); border-radius:50%;"></span>Скрипт хука (7)</div>
<div class="graph-legend-item"><span class="graph-legend-swatch" style="background:var(--sage);"></span>Событие хука (5)</div>
<div class="graph-legend-item"><span class="graph-legend-swatch" style="background:var(--rust); transform:rotate(45deg);"></span>Файл состояния (1)</div>
<div class="graph-legend-item"><span class="graph-legend-swatch" style="background:var(--ink-fade);"></span>Права (3)</div>
<div class="graph-legend-item"><span class="graph-legend-swatch" style="background:var(--ink); border-color:var(--rust);"></span>Правило (2)</div>
</div>
<div class="graph-legend-section">
<h4>Связи (типы)</h4>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--blueprint);"></span>содержит</div>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--sage); border-width:2px;"></span>запускает</div>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--amber); border-width:2px;"></span>пишет</div>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--amber); border-style:dashed;"></span>читает</div>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--rust); border-width:3px;"></span>обязывает</div>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--ink-fade); border-style:dotted;"></span>ссылается</div>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--rust); border-style:dashed;"></span>блокирует</div>
<div class="graph-legend-item"><span class="graph-legend-line" style="border-color:var(--rust); border-style:dashed; border-width:2px;"></span>запрещает</div>
</div>
</div>
</div>
</section>
<!-- ============================================================
XI. ACTIONS
============================================================ -->
<section class="section">
<div class="frame">
<div class="section-num display-i">XI</div>
<h2 class="section-title">Что вы можете сделать</h2>
<p class="section-lede">
Шесть практических действий. Каждое — одна команда или одно изменение в одном файле.
</p>
<div class="actions">
<div class="action">
<span class="action-num">1</span>
<div class="action-want">Если вы хотите…</div>
<div class="action-do">Активировать экономию для одной задачи</div>
<div class="r-desc">Допишите в самый конец промпта одно из: <span class="mono">экономия 0%</span> · <span class="mono">экономия 25%</span> · <span class="mono">экономия 50%</span> · <span class="mono">экономия 75%</span>. 0% — максимум, 75% — мягко, без ключа — обычный режим.</div>
</div>
<div class="action">
<span class="action-num">2</span>
<div class="action-want">Если вы хотите…</div>
<div class="action-do">Посмотреть, что сейчас включено</div>
<code class="action-cmd">python -c "import json; s=json.load(open(r'C:\Users\Administrator\.claude\settings.json',encoding='utf-8')); print(list(s['hooks'].keys()))"</code>
</div>
<div class="action">
<span class="action-num">3</span>
<div class="action-want">Если вы хотите…</div>
<div class="action-do">Вызвать скил вручную</div>
<div class="r-desc">Напишите в промпте slash-команду: <span class="mono">/superpowers:brainstorming</span> · <span class="mono">/claude-md-management:claude-md-improver</span> · <span class="mono">/init</span> · <span class="mono">/review</span> · etc.</div>
</div>
<div class="action">
<span class="action-num">4</span>
<div class="action-want">Если вы хотите…</div>
<div class="action-do">Выключить все хуки временно</div>
<div class="r-desc">В <span class="mono">~/.claude/settings.json</span> на верхнем уровне добавьте <span class="mono">"disableAllHooks":&nbsp;true</span>. Потребует ваш approve через ask-rule (так как Edit на settings.json). Уберёт всё включая дисциплину и верификатор.</div>
</div>
<div class="action">
<span class="action-num">5</span>
<div class="action-want">Если вы хотите…</div>
<div class="action-do">Проверить, что хуки работают</div>
<div class="r-desc">Откройте новую сессию Claude Code. SessionStart self-check сработает автоматически. Если что-то сломано — увидите <em>«Economy hook self-check FAILED»</em> в начале сессии.</div>
</div>
<div class="action">
<span class="action-num">6</span>
<div class="action-want">Если вы хотите…</div>
<div class="action-do">Откатить всё на до-economy состояние</div>
<code class="action-cmd">copy "C:\Users\Administrator\.claude\settings.json.backup-pre-economy-hardening" "C:\Users\Administrator\.claude\settings.json"</code>
</div>
</div>
</div>
</section>
<!-- ============================================================
FOOTER
============================================================ -->
<footer class="foot">
<div class="frame" style="display:flex; justify-content:space-between; flex-wrap:wrap; gap:32px; align-items:baseline;">
<div>
<div class="caps" style="margin-bottom:6px;">Document meta</div>
<span class="mono">hooks-skills-plugins-map.html</span> &middot;
<span class="mono">2026-05-10</span> &middot;
<span class="mono">10 ч / 7 хуков / 4 плагина / ~22 скила</span>
</div>
<div class="foot-ornament">~&nbsp;fin&nbsp;~</div>
<div style="text-align:right;">
<div class="caps" style="margin-bottom:6px;">Источник правды</div>
<span class="mono">~/.claude/settings.json</span><br>
<span style="font-style:italic;">прочитан напрямую, не из memory</span>
</div>
</div>
</footer>
</main>
<script>
// ============================================================
// CONNECTIONS GRAPH DATA
// ============================================================
const GRAPH_NODES = [
// === Plugins (4) ===
{ id: 'plg:superpowers', type: 'plugin', label: 'superpowers', desc: 'Главный плагин дисциплины процесса работы. 14 скилов.' },
{ id: 'plg:claude-md', type: 'plugin', label: 'claude-md-management', desc: 'Единственный канал правок CLAUDE.md. 2 скила.' },
{ id: 'plg:fd', type: 'plugin', label: 'frontend-design', desc: 'Создание distinctive frontend. Anthropic plugin.' },
{ id: 'plg:upm', type: 'plugin', label: 'ui-ux-pro-max', desc: 'Резерв-библиотека UI/UX (50+ стилей, 161 палитра).' },
// === Skills (28) ===
// Superpowers (14)
{ id: 'skl:brainstorming', type: 'skill', label: 'brainstorming', desc: 'Превращает идею в спек через диалог.' },
{ id: 'skl:writing-plans', type: 'skill', label: 'writing-plans', desc: 'Из спека → пошаговый план.' },
{ id: 'skl:executing-plans', type: 'skill', label: 'executing-plans', desc: 'Исполняет план по шагам.' },
{ id: 'skl:tdd', type: 'skill', label: 'test-driven-development', desc: 'Тест ДО кода. Red-green-refactor.' },
{ id: 'skl:debug', type: 'skill', label: 'systematic-debugging', desc: '4 фазы root cause. ≥3 гипотезы.' },
{ id: 'skl:req-review', type: 'skill', label: 'requesting-code-review', desc: 'Перед merge — двухстадийный review.' },
{ id: 'skl:recv-review', type: 'skill', label: 'receiving-code-review', desc: 'Обработка feedback ревью.' },
{ id: 'skl:verify', type: 'skill', label: 'verification-before-completion', desc: 'Перед claim готово — verify.' },
{ id: 'skl:finishing', type: 'skill', label: 'finishing-a-development-branch', desc: 'merge / PR / cleanup.' },
{ id: 'skl:worktrees', type: 'skill', label: 'using-git-worktrees', desc: 'Изоляция feature-работы.' },
{ id: 'skl:subagent', type: 'skill', label: 'subagent-driven-development', desc: 'Свежий subagent на каждую задачу.' },
{ id: 'skl:parallel', type: 'skill', label: 'dispatching-parallel-agents', desc: "Параллельные subagent'ы." },
{ id: 'skl:using-sp', type: 'skill', label: 'using-superpowers', desc: 'Базовый: как находить skills.' },
{ id: 'skl:writing-skills', type: 'skill', label: 'writing-skills', desc: 'Создание новых skills.' },
// claude-md-management (2)
{ id: 'skl:md-improver', type: 'skill', label: 'claude-md-improver', desc: 'Audit + targeted updates CLAUDE.md.' },
{ id: 'skl:md-revise', type: 'skill', label: 'revise-claude-md', desc: 'Захват session-learnings.' },
// frontend-design (1)
{ id: 'skl:fd-skill', type: 'skill', label: 'frontend-design', desc: 'Distinctive UI без AI-aesthetics.' },
// ui-ux-pro-max (1)
{ id: 'skl:upm-skill', type: 'skill', label: 'ui-ux-pro-max', desc: 'Резерв-библиотека стилей.' },
// Standalone (10)
{ id: 'skl:update-config', type: 'skill', label: 'update-config', desc: 'Правки settings.json.' },
{ id: 'skl:keybindings', type: 'skill', label: 'keybindings-help', desc: 'Клавиатурные сокращения.' },
{ id: 'skl:simplify', type: 'skill', label: 'simplify', desc: 'Review кода на reuse/quality.' },
{ id: 'skl:fewer-prompts', type: 'skill', label: 'fewer-permission-prompts', desc: 'Снижение шума prompts.' },
{ id: 'skl:init', type: 'skill', label: 'init', desc: 'Новый CLAUDE.md для нового проекта.' },
{ id: 'skl:claude-api', type: 'skill', label: 'claude-api', desc: 'Claude API / SDK apps.' },
{ id: 'skl:loop', type: 'skill', label: 'loop', desc: 'Recurring prompt на интервале.' },
{ id: 'skl:schedule', type: 'skill', label: 'schedule', desc: 'Cron-расписания для агентов.' },
{ id: 'skl:review', type: 'skill', label: 'review', desc: 'Review текущего PR.' },
{ id: 'skl:sec-review', type: 'skill', label: 'security-review', desc: 'Security audit pending changes.' },
// === Hook scripts (7) ===
{ id: 'scr:skill-marker', type: 'script', label: 'skill-marker.py', desc: 'Отметка о вызове Skill.' },
{ id: 'scr:skill-check', type: 'script', label: 'skill-check.py', desc: 'Reminder §12 если skill не вызван.' },
{ id: 'scr:economy-mode', type: 'script', label: 'economy-mode.py', desc: 'Парсит экономию N%, пишет state.' },
{ id: 'scr:economy-self-check', type: 'script', label: 'economy-self-check.py', desc: 'SessionStart runtime guard.' },
{ id: 'scr:economy-state-guard', type: 'script', label: 'economy-state-guard.py', desc: 'PreToolUse reminder + Bash bypass.' },
{ id: 'scr:economy-verifier', type: 'script', label: 'economy-verifier.py (Sonnet 4.6)', desc: 'Stop verifier — блокирует cherry-pick.' },
{ id: 'scr:economy-postcompact', type: 'script', label: 'economy-postcompact.py', desc: 'Re-inject правил после компакции.' },
// === Hook events (5) ===
{ id: 'evt:session-start', type: 'event', label: 'SessionStart', desc: 'Один раз на старте сессии.' },
{ id: 'evt:user-prompt-submit', type: 'event', label: 'UserPromptSubmit', desc: 'Каждый submit от пользователя.' },
{ id: 'evt:pre-tool-use', type: 'event', label: 'PreToolUse', desc: 'Перед каждым tool call.' },
{ id: 'evt:post-compact', type: 'event', label: 'PostCompact', desc: 'После авто-компакции.' },
{ id: 'evt:stop', type: 'event', label: 'Stop', desc: 'Конец моего ответа.' },
// === State file (1) ===
{ id: 'st:economy-state', type: 'state', label: 'claude-economy-state.json', desc: '$TEMP/claude-economy-<session_id>.json — shared state.' },
// === Permissions (3) ===
{ id: 'prm:allow', type: 'perm', label: 'permissions.allow (1)', desc: 'Разрешено без вопросов: Bash(git push origin main:*).' },
{ id: 'prm:deny', type: 'perm', label: 'permissions.deny (7)', desc: 'Жёстко заблокировано: rm/mv hook/settings/state.' },
{ id: 'prm:ask', type: 'perm', label: 'permissions.ask (16)', desc: 'С approve пользователя: Edit/Write hook files и settings.json.' },
// === Rules (2) ===
{ id: 'rul:pravila-12', type: 'rule', label: 'Pravila §12', desc: 'Hard rule: Superpowers skill ПЕРВЫМ.' },
{ id: 'rul:claude-md', type: 'rule', label: 'CLAUDE.md', desc: 'Главная карта проекта.' }
];
console.log('GRAPH_NODES:', GRAPH_NODES.length, '(expected 50)');
const GRAPH_LINKS = [
// === contains (18) — plugin → skill ===
// superpowers (14)
{ source: 'plg:superpowers', target: 'skl:brainstorming', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:writing-plans', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:executing-plans', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:tdd', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:debug', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:req-review', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:recv-review', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:verify', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:finishing', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:worktrees', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:subagent', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:parallel', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:using-sp', type: 'contains' },
{ source: 'plg:superpowers', target: 'skl:writing-skills', type: 'contains' },
// claude-md (2)
{ source: 'plg:claude-md', target: 'skl:md-improver', type: 'contains' },
{ source: 'plg:claude-md', target: 'skl:md-revise', type: 'contains' },
// fd (1)
{ source: 'plg:fd', target: 'skl:fd-skill', type: 'contains' },
// upm (1)
{ source: 'plg:upm', target: 'skl:upm-skill', type: 'contains' },
// === triggers (7) — event → script ===
{ source: 'evt:session-start', target: 'scr:economy-self-check', type: 'triggers' },
{ source: 'evt:user-prompt-submit', target: 'scr:economy-mode', type: 'triggers' },
{ source: 'evt:pre-tool-use', target: 'scr:skill-marker', type: 'triggers' },
{ source: 'evt:pre-tool-use', target: 'scr:skill-check', type: 'triggers' },
{ source: 'evt:pre-tool-use', target: 'scr:economy-state-guard', type: 'triggers' },
{ source: 'evt:post-compact', target: 'scr:economy-postcompact', type: 'triggers' },
{ source: 'evt:stop', target: 'scr:economy-verifier', type: 'triggers' },
// === writes (1) — script → state ===
{ source: 'scr:economy-mode', target: 'st:economy-state', type: 'writes' },
// === reads (3) — script → state ===
{ source: 'scr:economy-state-guard', target: 'st:economy-state', type: 'reads' },
{ source: 'scr:economy-verifier', target: 'st:economy-state', type: 'reads' },
{ source: 'scr:economy-postcompact', target: 'st:economy-state', type: 'reads' },
// === mandates (14) — Pravila §12 → 14 superpowers skills ===
{ source: 'rul:pravila-12', target: 'skl:brainstorming', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:writing-plans', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:executing-plans', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:tdd', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:debug', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:req-review', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:recv-review', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:verify', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:finishing', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:worktrees', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:subagent', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:parallel', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:using-sp', type: 'mandates' },
{ source: 'rul:pravila-12', target: 'skl:writing-skills', type: 'mandates' },
// === references (1) — CLAUDE.md → Pravila §12 ===
{ source: 'rul:claude-md', target: 'rul:pravila-12', type: 'references' },
// === blocks (7) — permissions.ask → hook scripts ===
{ source: 'prm:ask', target: 'scr:skill-marker', type: 'blocks' },
{ source: 'prm:ask', target: 'scr:skill-check', type: 'blocks' },
{ source: 'prm:ask', target: 'scr:economy-mode', type: 'blocks' },
{ source: 'prm:ask', target: 'scr:economy-self-check', type: 'blocks' },
{ source: 'prm:ask', target: 'scr:economy-state-guard', type: 'blocks' },
{ source: 'prm:ask', target: 'scr:economy-verifier', type: 'blocks' },
{ source: 'prm:ask', target: 'scr:economy-postcompact', type: 'blocks' },
// === denies (1) — permissions.deny → state file ===
{ source: 'prm:deny', target: 'st:economy-state', type: 'denies' }
];
console.log('GRAPH_LINKS:', GRAPH_LINKS.length, '(expected 52)');
// ============================================================
// RUSSIAN LABEL MAPPINGS + CLASSIFICATION
// ============================================================
const EDGE_TYPE_RU = {
contains: 'содержит',
triggers: 'запускает',
writes: 'пишет',
reads: 'читает',
mandates: 'обязывает',
references: 'ссылается',
blocks: 'блокирует',
denies: 'запрещает'
};
const NODE_TYPE_RU = {
plugin: 'Плагин',
skill: 'Скил',
script: 'Скрипт',
event: 'Событие',
state: 'Состояние',
perm: 'Права',
rule: 'Правило'
};
const EDGE_DESC = {
contains: 'Плагин упаковывает скил и доставляет его в систему как часть marketplace-пакета.',
triggers: 'Hook event запускает скрипт автоматически в момент события (SessionStart, PreToolUse, Stop, etc.).',
writes: 'Скрипт записывает данные в общий state-файл $TEMP/claude-economy-<session_id>.json для других хуков той же сессии.',
reads: 'Скрипт читает данные из state-файла, чтобы знать активный уровень экономии и применять правила.',
mandates: 'Pravila §12 hard rule обязывает использовать этот skill для подходящих задач — §9 «Отступления» НЕ применяется.',
references: 'Документ ссылается на другой как часть приоритетной цепочки правил (CLAUDE.md §1). При конфликте — приоритет у верхнего уровня.',
blocks: 'permissions.ask требует явный approve пользователя перед Edit/Write этого файла. Защита от tampering.',
denies: 'permissions.deny полностью запрещает Bash-операции (rm/mv/cp) над защищёнными файлами — обхода нет.'
};
const GOVERNANCE_TYPES = ['contains', 'mandates', 'triggers', 'blocks', 'denies'];
// ============================================================
// D3 SIMULATION + RENDER + INTERACTIVITY
// ============================================================
window.addEventListener('DOMContentLoaded', () => {
if (typeof d3 === 'undefined') {
document.querySelector('.graph-svg-wrap').innerHTML =
'<div class="graph-fallback">⚠ D3.js не загружен (нужен интернет для CDN). Остальная страница работает offline.</div>';
return;
}
const W = 1100, H = 700;
const svg = d3.select('.graph-svg');
// Y-position по типу для clustering
const Y_BY_TYPE = {
'rule': 80, 'plugin': 200, 'skill': 340,
'event': 540, 'script': 460, 'state': 380, 'perm': 600
};
const LINK_DISTANCE = {
'contains': 60, 'triggers': 50, 'writes': 40, 'reads': 40,
'mandates': 100, 'references': 80, 'blocks': 70, 'denies': 50
};
const NODE_SIZE = {
'plugin': 22, 'skill': 10, 'script': 14, 'event': 14,
'state': 16, 'perm': 16, 'rule': 18
};
// Force simulation
const sim = d3.forceSimulation(GRAPH_NODES)
.force('link', d3.forceLink(GRAPH_LINKS).id(d => d.id)
.distance(d => LINK_DISTANCE[d.type] || 50)
.strength(0.5))
.force('charge', d3.forceManyBody().strength(-280))
.force('center', d3.forceCenter(W / 2, H / 2))
.force('y', d3.forceY(d => Y_BY_TYPE[d.type] || H/2).strength(0.18))
.force('collide', d3.forceCollide().radius(d => (NODE_SIZE[d.type] || 10) + 6));
// Render links
const linkSel = svg.append('g').attr('class', 'links')
.selectAll('line')
.data(GRAPH_LINKS)
.join('line')
.attr('class', d => `gl gl-${d.type}`);
// Render nodes как <g>
const nodeSel = svg.append('g').attr('class', 'nodes')
.selectAll('g.gn-node')
.data(GRAPH_NODES)
.join('g')
.attr('class', d => `gn-node gn-node-${d.type}`);
// Shape по type
nodeSel.each(function(d) {
const g = d3.select(this);
const size = NODE_SIZE[d.type];
if (d.type === 'plugin' || d.type === 'skill' || d.type === 'script') {
g.append('circle').attr('r', size).attr('class', `gn-${d.type}`);
} else if (d.type === 'event') {
g.append('rect').attr('x', -size).attr('y', -size)
.attr('width', size*2).attr('height', size*2).attr('class', 'gn-event');
} else if (d.type === 'state') {
g.append('polygon')
.attr('points', `0,-${size} ${size},0 0,${size} -${size},0`)
.attr('class', 'gn-state');
} else if (d.type === 'perm') {
const s = size;
g.append('polygon')
.attr('points', `-${s},-${s/2} 0,-${s} ${s},-${s/2} ${s},${s/2} 0,${s} -${s},${s/2}`)
.attr('class', 'gn-perm');
} else if (d.type === 'rule') {
g.append('rect').attr('x', -size).attr('y', -size)
.attr('width', size*2).attr('height', size*2).attr('class', 'gn-rule');
}
});
// Labels
nodeSel.append('text')
.attr('class', 'gn-label')
.attr('dy', d => (NODE_SIZE[d.type] || 10) + 14)
.text(d => d.label);
// ============================================================
// INTERACTIVITY: click + highlight + sidebar
// ============================================================
const sidebar = document.querySelector('.graph-sidebar');
let activeId = null;
function showSidebar(node) {
const incoming = GRAPH_LINKS.filter(l =>
(typeof l.target === 'object' ? l.target.id : l.target) === node.id);
const outgoing = GRAPH_LINKS.filter(l =>
(typeof l.source === 'object' ? l.source.id : l.source) === node.id);
const governedBy = incoming.filter(l => GOVERNANCE_TYPES.includes(l.type));
const governing = outgoing.filter(l => GOVERNANCE_TYPES.includes(l.type));
const infoFlow = [...incoming, ...outgoing].filter(l => !GOVERNANCE_TYPES.includes(l.type));
const fmtIncoming = list => list.map(l => {
const srcId = typeof l.source === 'object' ? l.source.id : l.source;
const src = GRAPH_NODES.find(n => n.id === srcId);
return `<li>${src ? src.label : srcId} <b>${EDGE_TYPE_RU[l.type] ?? l.type}</b></li>`;
}).join('');
const fmtOutgoing = list => list.map(l => {
const tgtId = typeof l.target === 'object' ? l.target.id : l.target;
const tgt = GRAPH_NODES.find(n => n.id === tgtId);
return `<li><b>${EDGE_TYPE_RU[l.type] ?? l.type}</b> → ${tgt ? tgt.label : tgtId}</li>`;
}).join('');
const fmtInfoFlow = list => list.map(l => {
const sId = typeof l.source === 'object' ? l.source.id : l.source;
const tId = typeof l.target === 'object' ? l.target.id : l.target;
const isIncoming = tId === node.id;
if (isIncoming) {
const src = GRAPH_NODES.find(n => n.id === sId);
return `<li>${src ? src.label : sId} <b>${EDGE_TYPE_RU[l.type] ?? l.type}</b></li>`;
} else {
const tgt = GRAPH_NODES.find(n => n.id === tId);
return `<li><b>${EDGE_TYPE_RU[l.type] ?? l.type}</b> → ${tgt ? tgt.label : tId}</li>`;
}
}).join('');
const typeLabel = NODE_TYPE_RU[node.type] ?? node.type;
sidebar.innerHTML = `
<button class="graph-close" aria-label="Close">×</button>
<div class="graph-sidebar-title">${node.label}</div>
<div class="graph-sidebar-badge">${typeLabel}</div>
<div class="graph-sidebar-section">
<div class="graph-sidebar-section-label">За что отвечает</div>
<div style="font-size:0.85rem; color: var(--ink-soft); line-height: 1.5;">${node.desc}</div>
</div>
${governedBy.length ? `
<div class="graph-sidebar-section governance">
<div class="graph-sidebar-section-label">Кто руководит (${governedBy.length})</div>
<ul class="graph-conn-list">${fmtIncoming(governedBy)}</ul>
</div>` : ''}
${governing.length ? `
<div class="graph-sidebar-section governance">
<div class="graph-sidebar-section-label">Кем руководит (${governing.length})</div>
<ul class="graph-conn-list">${fmtOutgoing(governing)}</ul>
</div>` : ''}
${infoFlow.length ? `
<div class="graph-sidebar-section">
<div class="graph-sidebar-section-label">Связи — info flow (${infoFlow.length})</div>
<ul class="graph-conn-list">${fmtInfoFlow(infoFlow)}</ul>
</div>` : ''}
`;
sidebar.querySelector('.graph-close').addEventListener('click', clearHighlight);
}
function highlightNode(node) {
activeId = node.id;
const relatedIds = new Set([node.id]);
GRAPH_LINKS.forEach(l => {
const sId = typeof l.source === 'object' ? l.source.id : l.source;
const tId = typeof l.target === 'object' ? l.target.id : l.target;
if (sId === node.id) relatedIds.add(tId);
if (tId === node.id) relatedIds.add(sId);
});
nodeSel.classed('dimmed', d => !relatedIds.has(d.id));
linkSel.classed('dimmed', l => {
const sId = typeof l.source === 'object' ? l.source.id : l.source;
const tId = typeof l.target === 'object' ? l.target.id : l.target;
return sId !== node.id && tId !== node.id;
});
showSidebar(node);
}
function clearHighlight() {
activeId = null;
nodeSel.classed('dimmed', false);
linkSel.classed('dimmed', false);
sidebar.innerHTML = '<div class="graph-sidebar-empty">Кликни на узел или связь чтобы увидеть детали…</div>';
}
function showEdgeSidebar(edge) {
const sId = typeof edge.source === 'object' ? edge.source.id : edge.source;
const tId = typeof edge.target === 'object' ? edge.target.id : edge.target;
const src = GRAPH_NODES.find(n => n.id === sId);
const tgt = GRAPH_NODES.find(n => n.id === tId);
const ruLabel = EDGE_TYPE_RU[edge.type] ?? edge.type;
const desc = EDGE_DESC[edge.type] ?? '';
const srcType = NODE_TYPE_RU[src?.type] ?? '';
const tgtType = NODE_TYPE_RU[tgt?.type] ?? '';
sidebar.innerHTML = `
<button class="graph-close" aria-label="Close">×</button>
<div class="graph-sidebar-title">${ruLabel}</div>
<div class="graph-sidebar-badge">Связь</div>
<div class="graph-sidebar-section">
<div class="graph-sidebar-section-label">Что это значит</div>
<div style="font-size:0.85rem; color: var(--ink-soft); line-height: 1.5;">${desc}</div>
</div>
<div class="graph-sidebar-section">
<div class="graph-sidebar-section-label">Источник — ${srcType}</div>
<div style="font-family: 'Fraunces', serif; font-weight: 500; font-size: 1rem;">${src?.label ?? sId}</div>
<div style="font-size:0.82rem; color: var(--ink-soft); line-height: 1.45; margin-top: 4px;">${src?.desc ?? ''}</div>
<button class="graph-sidebar-navlink" data-nav-to="${sId}">Перейти к узлу →</button>
</div>
<div class="graph-sidebar-section">
<div class="graph-sidebar-section-label">Цель — ${tgtType}</div>
<div style="font-family: 'Fraunces', serif; font-weight: 500; font-size: 1rem;">${tgt?.label ?? tId}</div>
<div style="font-size:0.82rem; color: var(--ink-soft); line-height: 1.45; margin-top: 4px;">${tgt?.desc ?? ''}</div>
<button class="graph-sidebar-navlink" data-nav-to="${tId}">Перейти к узлу →</button>
</div>
`;
sidebar.querySelector('.graph-close').addEventListener('click', clearHighlight);
sidebar.querySelectorAll('.graph-sidebar-navlink').forEach(btn => {
btn.addEventListener('click', () => {
const targetId = btn.dataset.navTo;
const targetNode = GRAPH_NODES.find(n => n.id === targetId);
if (targetNode) highlightNode(targetNode);
});
});
}
function highlightEdge(edge) {
const sId = typeof edge.source === 'object' ? edge.source.id : edge.source;
const tId = typeof edge.target === 'object' ? edge.target.id : edge.target;
activeId = `edge:${sId}->${tId}`;
nodeSel.classed('dimmed', d => d.id !== sId && d.id !== tId);
linkSel.classed('dimmed', l => l !== edge);
showEdgeSidebar(edge);
}
nodeSel.on('click', (event, d) => {
if (event.defaultPrevented) return; // was a drag, not a click
event.stopPropagation();
if (activeId === d.id) clearHighlight();
else highlightNode(d);
});
svg.on('click', clearHighlight);
// ============================================================
// INTERACTIVITY: hover edge → tooltip
// ============================================================
const tooltip = d3.select(document.body)
.append('div').attr('class', 'gl-tooltip').style('display', 'none');
linkSel.on('mouseover', function(event, l) {
const sId = typeof l.source === 'object' ? l.source.id : l.source;
const tId = typeof l.target === 'object' ? l.target.id : l.target;
const src = GRAPH_NODES.find(n => n.id === sId);
const tgt = GRAPH_NODES.find(n => n.id === tId);
tooltip.html(`<b>${EDGE_TYPE_RU[l.type] ?? l.type}</b>: ${src?.label ?? sId}${tgt?.label ?? tId}`)
.style('display', 'block')
.style('left', (event.pageX + 12) + 'px')
.style('top', (event.pageY - 8) + 'px');
d3.select(this).style('stroke-width', '4');
});
linkSel.on('mousemove', function(event) {
tooltip.style('left', (event.pageX + 12) + 'px').style('top', (event.pageY - 8) + 'px');
});
linkSel.on('mouseout', function() {
tooltip.style('display', 'none');
d3.select(this).style('stroke-width', null);
});
linkSel.on('click', function(event, l) {
event.stopPropagation();
tooltip.style('display', 'none');
highlightEdge(l);
});
// ============================================================
// INTERACTIVITY: drag
// ============================================================
function dragstart(event, d) {
if (!event.active) sim.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragmove(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragend(event, d) {
if (!event.active) sim.alphaTarget(0);
d.fx = null;
d.fy = null;
}
nodeSel.call(d3.drag()
.on('start', dragstart)
.on('drag', dragmove)
.on('end', dragend));
// ============================================================
// INTERACTIVITY: filter chips + reset
// ============================================================
const filters = document.querySelectorAll('.graph-filter');
const hiddenTypes = new Set();
function applyFilters() {
// If currently-selected node's type is being hidden — clear selection
if (activeId && !activeId.startsWith('edge:')) {
const activeNode = GRAPH_NODES.find(n => n.id === activeId);
if (activeNode && hiddenTypes.has(activeNode.type)) clearHighlight();
}
// If currently-selected edge has hidden source/target type — clear
if (activeId && activeId.startsWith('edge:')) {
const match = activeId.match(/^edge:(.+)->(.+)$/);
if (match) {
const sN = GRAPH_NODES.find(n => n.id === match[1]);
const tN = GRAPH_NODES.find(n => n.id === match[2]);
if ((sN && hiddenTypes.has(sN.type)) || (tN && hiddenTypes.has(tN.type))) clearHighlight();
}
}
nodeSel.style('display', d => hiddenTypes.has(d.type) ? 'none' : null);
linkSel.style('display', l => {
const sT = (typeof l.source === 'object' ? l.source.type : GRAPH_NODES.find(n=>n.id===l.source).type);
const tT = (typeof l.target === 'object' ? l.target.type : GRAPH_NODES.find(n=>n.id===l.target).type);
return (hiddenTypes.has(sT) || hiddenTypes.has(tT)) ? 'none' : null;
});
}
filters.forEach(btn => {
btn.addEventListener('click', () => {
const type = btn.dataset.type;
if (hiddenTypes.has(type)) {
hiddenTypes.delete(type);
btn.classList.add('active');
} else {
hiddenTypes.add(type);
btn.classList.remove('active');
}
applyFilters();
});
});
document.querySelector('.graph-reset').addEventListener('click', () => {
hiddenTypes.clear();
filters.forEach(b => b.classList.add('active'));
applyFilters();
clearHighlight();
GRAPH_NODES.forEach(n => { n.fx = null; n.fy = null; });
sim.alpha(0.7).restart();
});
// Tick — обновление позиций
sim.on('tick', () => {
linkSel
.attr('x1', d => d.source.x).attr('y1', d => d.source.y)
.attr('x2', d => d.target.x).attr('y2', d => d.target.y);
nodeSel.attr('transform', d => `translate(${d.x},${d.y})`);
});
});
</script>
</body>
</html>