af32dfcd92
Adapt HTML visualization template under actual brain v1.0 state on 2026-05-11. Original copy from Liderra had stale context. Changes: - Hero block: date 2026-05-11, edition vIV, deconxt from Liderra («четыре MCP-сервера» added to counters) - §II Hierarchy: remove version suffixes (Pravila_raboty_Claude.md, Plugin_stack_rules.md) — brain templates have no versions - §VI Plugins: claude-md-management now "1 skill + 1 slash-command", ui-ux-pro-max stats actualized (67 styles, 161 palette, 57 fonts, 99 UX guidelines, 25 chart types) - §VII Skills: lede updated to "28 capabilities (27 skills + 1 slash-command)" - §VIII Filesystem: rebuilt to dual-source brain v1.0 model (user / brain source / project consumer) - §XI MCP servers (NEW): 4 servers — magic, playwright, github, semgrep - §XII Actions renumbered (was §XI) - §X Graph data: revise-claude-md labeled as command, legend label "Скил/команда (28)" - Footer meta: 2026-05-11, "brain v1.0 / 7 хуков / 4 плагина / 4 MCP / 28 capabilities" - manifest.json SHA-256 hash sync (both project-mode and brain-internal entries) Verified: HTML parses OK, JSON parses OK, hash matches manifest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3227 lines
130 KiB
HTML
3227 lines
130 KiB
HTML
<!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">11.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 в проекте с установленным <span class="mono">claude-brain v1.0</span>.
|
||
Что срабатывает в начале промпта, что — посреди вашей работы, и что — в конце.
|
||
<strong>Три типа сущностей, семь хуков, пять событий, четыре плагина, четыре MCP-сервера.</strong>
|
||
</p>
|
||
|
||
<div class="hero-stamp">
|
||
<span class="num">vIV</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">Слой 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">Слой 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">Слой 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">Иерархия и порядок</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'ы.
|
||
Сам плагин ничего не делает. Хуки регистрируются в settings.json и запускаются
|
||
самим 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> уровень.
|
||
Так договорились в CLAUDE.md §1. Уровень 0 — единственный неотменяемый
|
||
(даже «отступления» не отменяют его).
|
||
</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.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 Прил. Н</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.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, и т. д. — поведенческие подсказки.</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 3. TIMING -->
|
||
<div class="hier-block">
|
||
<div class="hier-block-tag">3 · Временная</div>
|
||
<h3 class="hier-block-title">В каком порядке всё срабатывает</h3>
|
||
<p class="hier-block-lede">
|
||
События имеют фиксированный порядок в жизненном цикле сессии. Цвет верхней полосы
|
||
— это частота срабатывания: <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 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 -i</span>, <span class="mono">echo ></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">На старте сессии</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 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 сек.
|
||
</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 0 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 Pravila — half-вес проекта.
|
||
</div>
|
||
<div class="plugin-skills-list">
|
||
14 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 (skill — audit) и revise-claude-md (slash-command — capture learnings).
|
||
</div>
|
||
<div class="plugin-skills-list">
|
||
1 skill + 1 slash-command · инфраструктура 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 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">
|
||
Резерв-библиотека: 67 UI styles, 161 palette, 57 font pairings, 99 UX guidelines, 25 chart types
|
||
(across 15+ tech stacks). Off-phase tool — активируется только через R14 pipeline в Plugin Stack Rules.
|
||
</div>
|
||
<div class="plugin-skills-list">
|
||
1 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">Скилы по плагинам</h2>
|
||
<p class="section-lede">
|
||
Все 28 capabilities (27 skills + 1 slash-command) сгруппированы по родительскому плагину.
|
||
18 принадлежат одному из 4 установленных плагинов (17 skills + 1 command), 10 — «standalone / встроенные»
|
||
(не доставляются плагином, идут с 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 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">1 skill + 1 command · 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> Единственный канал структурных правок: добавление/удаление секций, версии в шапке, правки правил.</div>
|
||
</div>
|
||
<div class="sp-skill-row">
|
||
<div class="sp-skill-key">revise-claude-md <span style="color:var(--rust); font-style:italic;">(command)</span></div>
|
||
<div class="sp-skill-what"><em>Захват session-learnings.</em> Новые квирки, команды, паттерны из текущей сессии → автоматически в CLAUDE.md. Доставляется как slash-command в <span class="mono">commands/</span>, не skill.</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 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 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> 67 UI styles, 161 palette, 57 font pairings, 99 UX guidelines, 25 chart types (across 15+ tech stacks). Активируется через PSR 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 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">Карта на диске</h2>
|
||
<p class="section-lede">
|
||
Где физически лежит каждый компонент. Три уровня: <em>user</em> (общая инфраструктура Claude Code),
|
||
<em>brain (source)</em> (репозиторий-источник, который правится осознанно), <em>project (consumer)</em>
|
||
(любой проект, в который brain установлен через <span class="mono">install.sh</span>).
|
||
</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 (собран из settings-fragment.json)</span>
|
||
├── settings.json.backup-pre-economy-hardening
|
||
│
|
||
├── <span class="fs-path"><b>hooks/</b></span> <span class="fs-comment">Python-скрипты хуков (синкаются из brain user-level-files/hooks/)</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">загруженные плагины (marketplace-источники в marketplaces.json)</span>
|
||
│ ├── superpowers-dev/superpowers/5.1.0/ <span class="fs-comment">obra/superpowers</span>
|
||
│ ├── claude-plugins-official/claude-md-management/1.0.0/
|
||
│ ├── claude-plugins-official/frontend-design/
|
||
│ └── ui-ux-pro-max-skill/ui-ux-pro-max/2.5.0/
|
||
│
|
||
└── <span class="fs-path"><b>projects/<cwd>/memory/</b></span> <span class="fs-comment">долгосрочная память (per-project)</span>
|
||
├── MEMORY.md (index)
|
||
└── ... per-project files
|
||
|
||
<span class="fs-section-tag">brain (source)</span><span class="fs-path">c:\моя\проекты\<b>claude-brain\</b></span>
|
||
├── manifest.json <span class="fs-comment">SHA-256 hashes всех файлов brain v1.0</span>
|
||
├── CLAUDE.md / README.md / CHANGELOG.md
|
||
│
|
||
├── <span class="fs-path"><b>project-files/</b></span> <span class="fs-comment">копируется в consumer-проекты install.sh'ом</span>
|
||
│ ├── CLAUDE.md.template
|
||
│ ├── .mcp.json.template <span class="fs-comment">playwright + github + semgrep</span>
|
||
│ └── docs/
|
||
│ ├── Pravila_raboty_Claude.template.md
|
||
│ ├── Plugin_stack_rules.template.md
|
||
│ ├── Tooling.template.md
|
||
│ └── visualizations/hooks-skills-plugins-map.html <span class="fs-comment">вы тут</span>
|
||
│
|
||
├── <span class="fs-path"><b>user-level-files/</b></span> <span class="fs-comment">копируется в ~/.claude/ install.sh'ом</span>
|
||
│ ├── hooks/ <span class="fs-comment">7 рантайм + 3 test .py</span>
|
||
│ ├── settings-fragment.json <span class="fs-comment">фрагмент для merge в settings.json</span>
|
||
│ ├── marketplaces.json <span class="fs-comment">3 marketplace-источника</span>
|
||
│ ├── plugins-manifest.json <span class="fs-comment">4 плагина + версии</span>
|
||
│ └── mcp-user.template.json <span class="fs-comment">magic (21st.dev)</span>
|
||
│
|
||
├── <span class="fs-path"><b>scripts/</b></span> <span class="fs-comment">инструменты sync</span>
|
||
│ ├── install.sh <span class="fs-comment">copy brain → consumer / ~/.claude</span>
|
||
│ ├── verify.sh <span class="fs-comment">cross-check hashes vs manifest.json</span>
|
||
│ └── extract.sh <span class="fs-comment">rescue: consumer → brain</span>
|
||
│
|
||
└── docs/ <span class="fs-comment">документация brain'а самого</span>
|
||
├── architecture.md
|
||
├── how-to-use-brain.md
|
||
├── secrets-and-tokens.md
|
||
└── sessions/2026-05-11-bootstrap-session.md
|
||
|
||
<span class="fs-section-tag">project (consumer)</span><span class="fs-path"><consumer-project>/</span>
|
||
├── CLAUDE.md <span class="fs-comment">из template, substitutions заполнены при install</span>
|
||
├── .mcp.json <span class="fs-comment">project-mode MCP (playwright/github/semgrep)</span>
|
||
│
|
||
└── docs/
|
||
├── Pravila_raboty_Claude.md <span class="fs-comment">§12 hard rule (без версии в имени)</span>
|
||
├── Plugin_stack_rules.md <span class="fs-comment">16 правил координации</span>
|
||
├── Tooling.md <span class="fs-comment">реестр инструментов проекта</span>
|
||
├── CHANGELOG_claude_md.md
|
||
├── superpowers/{specs,plans}/ <span class="fs-comment">создаются по мере работы</span>
|
||
└── visualizations/hooks-skills-plugins-map.html <span class="fs-comment">этот файл, installed copy</span>
|
||
</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">Кто за что отвечает</h2>
|
||
<p class="section-lede">
|
||
Четыре действующих лица — четыре роли. Каждый делает то, что больше никто не может,
|
||
и не лезет в чужой огород. Это про разделение обязанностей в системе.
|
||
</p>
|
||
|
||
<div class="mental">
|
||
<div class="mental-stage">
|
||
|
||
<div class="mental-actor you">
|
||
<div class="mental-tag">Actor 01</div>
|
||
<div class="mental-name">Вы (Дмитрий)</div>
|
||
<ul class="mental-list">
|
||
<li>Пишете промпт, опционально ставите «экономия N%» в конце</li>
|
||
<li>Approve'ите permission prompts на правки hook-файлов</li>
|
||
<li>Можете выключить хуки через settings.json → disableAllHooks</li>
|
||
<li>Вне Claude Code — единственный, кто может править settings/hooks без вопросов</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="mental-actor runtime">
|
||
<div class="mental-tag">Actor 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 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 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">Связи — интерактивная карта</h2>
|
||
<p class="section-lede">
|
||
50 узлов, 52 ребра, 8 типов связей. Кликни на любой узел —
|
||
подсветятся все его связи + откроется панель с деталями справа.
|
||
Тяни узлы мышкой для перестановки. Фильтры в верхней панели прячут
|
||
категории по одной.
|
||
</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">
|
||
Кликни на узел чтобы увидеть его связи…
|
||
</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. MCP SERVERS
|
||
============================================================ -->
|
||
<section class="section">
|
||
<div class="frame">
|
||
<div class="section-num display-i">XI</div>
|
||
<h2 class="section-title">MCP-серверы — внешние tool-провайдеры</h2>
|
||
<p class="section-lede">
|
||
Четыре сервера Model Context Protocol. Один на user-уровне (магазин компонентов), три на project-уровне
|
||
(браузер, GitHub API, статанализ). MCP — это <em>не</em> плагин и <em>не</em> хук: это отдельный процесс,
|
||
который запускается рядом с Claude Code и предоставляет дополнительные tools через stdio или HTTP.
|
||
</p>
|
||
|
||
<div class="plugins">
|
||
|
||
<div class="plugin-card">
|
||
<div class="plugin-card-header">
|
||
<div class="plugin-card-name">magic</div>
|
||
<div class="plugin-card-scope">user · stdio · npx</div>
|
||
</div>
|
||
<div class="plugin-card-desc">
|
||
UI-генерация компонентов от 21st.dev. Поиск, inspiration, refiner, logo search — для быстрых
|
||
визуальных набросков. Зарегистрирован в <span class="mono">~/.claude/mcp.json</span>
|
||
(template: <span class="mono">user-level-files/mcp-user.template.json</span>).
|
||
</div>
|
||
<div class="plugin-skills-list">
|
||
<span class="mono">npx @21st-dev/magic@latest</span> · API_KEY required
|
||
</div>
|
||
</div>
|
||
|
||
<div class="plugin-card">
|
||
<div class="plugin-card-header">
|
||
<div class="plugin-card-name">playwright</div>
|
||
<div class="plugin-card-scope">project · stdio · npx</div>
|
||
</div>
|
||
<div class="plugin-card-desc">
|
||
Headless-браузер. Открытие <span class="mono">web/*.html</span>, screenshot, проверка интерактива,
|
||
навигация по DOM. Используется для визуальной верификации UI-работы.
|
||
</div>
|
||
<div class="plugin-skills-list">
|
||
<span class="mono">npx @playwright/mcp@latest</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="plugin-card">
|
||
<div class="plugin-card-header">
|
||
<div class="plugin-card-name">github</div>
|
||
<div class="plugin-card-scope">project · HTTP · hosted</div>
|
||
</div>
|
||
<div class="plugin-card-desc">
|
||
Официальный hosted GitHub MCP. Issues, PRs, файлы, search, actions. Требует
|
||
<span class="mono">GITHUB_TOKEN</span> (PAT, scopes: repo, read:org).
|
||
Заменил deprecated <span class="mono">@modelcontextprotocol/server-github</span> 06.05.2026.
|
||
</div>
|
||
<div class="plugin-skills-list">
|
||
<span class="mono">https://api.githubcopilot.com/mcp</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="plugin-card">
|
||
<div class="plugin-card-header">
|
||
<div class="plugin-card-name">semgrep</div>
|
||
<div class="plugin-card-scope">project · stdio · npx</div>
|
||
</div>
|
||
<div class="plugin-card-desc">
|
||
SAST. Семантический поиск и анализ кода через Semgrep rules прямо в Claude Code.
|
||
Для security-review и поиска паттернов уязвимостей.
|
||
</div>
|
||
<div class="plugin-skills-list">
|
||
<span class="mono">npx semgrep-mcp</span>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================================================
|
||
XII. ACTIONS
|
||
============================================================ -->
|
||
<section class="section">
|
||
<div class="frame">
|
||
<div class="section-num display-i">XII</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": 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> ·
|
||
<span class="mono">2026-05-11</span> ·
|
||
<span class="mono">brain v1.0 · 7 хуков / 4 плагина / 4 MCP / 28 capabilities</span>
|
||
</div>
|
||
<div class="foot-ornament">~ fin ~</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 (command)', desc: 'Захват session-learnings. Доставляется как slash-command (commands/), не skill.' },
|
||
// 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>
|