hail-hydra-cc 2.3.2 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/files/SKILL.md +40 -85
- package/files/commands/hydra/stats.md +31 -0
- package/files/hooks/hydra-auto-guard.js +70 -0
- package/package.json +1 -1
- package/src/display.js +1 -1
- package/src/files.js +5 -1
- package/src/installer.js +10 -2
package/files/SKILL.md
CHANGED
|
@@ -604,114 +604,71 @@ Deviate from this model only when:
|
|
|
604
604
|
| Catastrophic test failure | Make final runner BLOCKING (something is fundamentally broken) |
|
|
605
605
|
| Stale Session Index detected | Rebuild index; treat as Turn 1 |
|
|
606
606
|
|
|
607
|
-
##
|
|
607
|
+
## What Hydra Offers
|
|
608
608
|
|
|
609
|
-
|
|
609
|
+
Hydra is a curated toolkit of specialized agents and slash commands. Use its capabilities when they genuinely help — not as a forced routing layer.
|
|
610
610
|
|
|
611
|
-
###
|
|
611
|
+
### Slash Commands (User-Invoked)
|
|
612
612
|
|
|
613
|
-
|
|
613
|
+
The user can run any of these directly. Don't pre-invoke them on the user's behalf unless they ask:
|
|
614
614
|
|
|
615
|
-
|
|
|
616
|
-
|
|
617
|
-
|
|
|
618
|
-
|
|
|
619
|
-
|
|
|
620
|
-
|
|
|
621
|
-
|
|
|
622
|
-
|
|
|
623
|
-
|
|
|
624
|
-
|
|
|
625
|
-
|
|
|
615
|
+
| Command | Purpose |
|
|
616
|
+
|---------|---------|
|
|
617
|
+
| `/hydra:scout` | Codebase exploration via hydra-scout (Haiku) |
|
|
618
|
+
| `/hydra:guard` | Security scan via hydra-guard (Haiku) |
|
|
619
|
+
| `/hydra:preflight` | Environment validation via hydra-preflight + hydra-analyst |
|
|
620
|
+
| `/hydra:stats` | Real token usage and savings from current session |
|
|
621
|
+
| `/hydra:stfu` | Compress internal reasoning across all subagent dispatches |
|
|
622
|
+
| `/hydra:status` | Framework health check |
|
|
623
|
+
| `/hydra:help` | Command reference |
|
|
624
|
+
| `/hydra:map` | Codebase map summary or rebuild |
|
|
625
|
+
| `/hydra:report` | Submit feedback to maintainers |
|
|
626
|
+
| `/hydra:update` | Update Hydra to latest version |
|
|
627
|
+
| `/hydra:config` | View configuration |
|
|
628
|
+
| `/hydra:quiet` / `/hydra:verbose` | Toggle dispatch log verbosity |
|
|
626
629
|
|
|
627
|
-
|
|
630
|
+
### Subagents Available for Dispatch (When Genuinely Useful)
|
|
628
631
|
|
|
629
|
-
|
|
632
|
+
When a task genuinely benefits from specialized handling, dispatch these agents via the Task tool. Use judgment — for trivial tasks, handling them directly is often more efficient than the dispatch overhead.
|
|
630
633
|
|
|
631
|
-
|
|
634
|
+
- **hydra-scout** (Haiku): Multi-file codebase exploration when scope is broad
|
|
635
|
+
- **hydra-runner** (Haiku): Test/build execution that could run in parallel
|
|
636
|
+
- **hydra-coder** (Sonnet): Code changes across 3+ files where parallel dispatch saves real time
|
|
637
|
+
- **hydra-analyst** (Sonnet): Debugging or analysis requiring focused context
|
|
638
|
+
- **hydra-sentinel** (Sonnet): Deep integration analysis after substantial changes
|
|
639
|
+
- **hydra-sentinel-scan** (Haiku): Fast post-change verification — see Auto-Verification below
|
|
632
640
|
|
|
633
|
-
|
|
634
|
-
|-----------|----------------|
|
|
635
|
-
| Task classification and routing decisions | Only you see the full conversation context |
|
|
636
|
-
| Verifying and synthesizing agent outputs | Judgment on whether a draft is acceptable requires orchestrator perspective |
|
|
637
|
-
| System architecture and major design decisions | Novel architectural tradeoffs need Opus-level reasoning |
|
|
638
|
-
| Ambiguous debugging with no clear clues | "It works in staging but not prod" needs deep investigation |
|
|
639
|
-
| Context-dependent tasks requiring conversation history | Agents don't see prior turns — you do |
|
|
640
|
-
| Trivial edits under 5 seconds (max 2-3 per session) | Delegation overhead exceeds task cost |
|
|
641
|
-
| Planning and decomposition | Breaking tasks into waves IS the orchestrator's job |
|
|
642
|
-
| Conversation management (clarification, alignment) | Only you talk to the user |
|
|
641
|
+
**Key principle:** Dispatch when the task is substantial enough that the subagent overhead is justified by cheaper-model savings, parallelization, or focused context windows. For small tasks (single-file reads, one-line edits, quick conversations), handling them yourself is the right call.
|
|
643
642
|
|
|
644
|
-
###
|
|
643
|
+
### Auto-Verification (The One Automatic Touchpoint)
|
|
645
644
|
|
|
646
|
-
|
|
645
|
+
The hydra-auto-guard hook tracks file changes during the session. After **substantial** code edits (new file writes, MultiEdit batches, or edits modifying more than ~5 lines / 200 chars), the hook injects a directive recommending dispatch of hydra-sentinel-scan for integration verification.
|
|
647
646
|
|
|
648
|
-
|
|
649
|
-
- YES → Handle yourself. Agents don't have this context.
|
|
650
|
-
2. **Can Haiku or Sonnet do this equally well?** (not "almost as well" — EQUALLY well)
|
|
651
|
-
- YES → Delegate. You're wasting money and time doing it yourself.
|
|
652
|
-
- NO → Handle yourself.
|
|
653
|
-
3. **Would delegation take LONGER than doing it yourself?** (including prompt construction + wait time)
|
|
654
|
-
- YES, and the task is truly trivial → Handle yourself (counts toward overhead budget).
|
|
655
|
-
- NO or UNSURE → **Delegate.** This is the default. When in doubt, DELEGATE.
|
|
647
|
+
Comply with that directive when it appears — post-change verification catches bugs the orchestrator alone often misses. This is the one place where Hydra actively nudges regardless of explicit user request.
|
|
656
648
|
|
|
657
|
-
|
|
649
|
+
For trivial edits the hook stays silent.
|
|
658
650
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
**WRONG** (sequential — wastes time):
|
|
662
|
-
```
|
|
663
|
-
Message 1: Launch hydra-scout to explore auth module
|
|
664
|
-
[wait for result]
|
|
665
|
-
Message 2: Launch hydra-runner to run existing tests
|
|
666
|
-
[wait for result]
|
|
667
|
-
Message 3: Launch hydra-scout to check test patterns
|
|
668
|
-
```
|
|
669
|
-
|
|
670
|
-
**RIGHT** (parallel — all independent):
|
|
671
|
-
```
|
|
672
|
-
Message 1: Launch hydra-scout (auth module) + hydra-runner (tests) + hydra-scout (test patterns)
|
|
673
|
-
[all three return]
|
|
674
|
-
Message 2: Launch dependent tasks using results from Message 1
|
|
675
|
-
```
|
|
676
|
-
|
|
677
|
-
**Trigger phrases that REQUIRE parallel dispatch:**
|
|
678
|
-
- "...and..." (e.g., "fix the bug AND add tests" → scout + runner in parallel)
|
|
679
|
-
- "...then..." where the "then" tasks are independent of each other
|
|
680
|
-
- Any request with 2+ independent components
|
|
681
|
-
- Any request where exploration and execution can overlap
|
|
682
|
-
|
|
683
|
-
### Delegation Overhead Budget
|
|
684
|
-
|
|
685
|
-
You are allowed a MAXIMUM of 2-3 "do it myself" exceptions per session for tasks that technically fall in the ALWAYS Delegate table but are genuinely trivial (e.g., adding a single `console.log` to a known file). Track this internally.
|
|
686
|
-
|
|
687
|
-
**Rules:**
|
|
688
|
-
- If you've done 5+ tasks directly in a row without delegating, STOP. Re-read the ALWAYS Delegate table. You are almost certainly violating these rules.
|
|
689
|
-
- "It's faster if I just do it" is not a valid exception after the 2-3 budget is spent.
|
|
690
|
-
- The budget resets each session.
|
|
691
|
-
|
|
692
|
-
### Plan Mode Behavior
|
|
651
|
+
## Plan Mode Behavior
|
|
693
652
|
|
|
694
653
|
During planning phase (before execution begins):
|
|
695
654
|
- Using Claude Code's built-in Explore agent is acceptable for quick codebase understanding.
|
|
696
|
-
- No delegation rules apply yet — you're gathering context, not executing.
|
|
697
655
|
|
|
698
656
|
Once execution begins (after plan is approved):
|
|
699
|
-
-
|
|
700
|
-
-
|
|
701
|
-
- Plans MUST reference specific Hydra agents. Example format:
|
|
657
|
+
- Dispatch Hydra agents where they pay off (see "What Hydra Offers" above). Don't force dispatch for trivial tasks.
|
|
658
|
+
- Plans may reference specific Hydra agents when helpful. Example format:
|
|
702
659
|
|
|
703
660
|
```
|
|
704
661
|
Step 1: hydra-scout → read auth module structure [parallel with Step 2]
|
|
705
662
|
Step 2: hydra-runner → run existing test suite [parallel with Step 1]
|
|
706
663
|
Step 3: hydra-coder → implement fix using findings from Steps 1-2
|
|
707
|
-
Step 4: hydra-sentinel-scan
|
|
664
|
+
Step 4: hydra-sentinel-scan → verify changes
|
|
708
665
|
Step 5: hydra-runner → run tests to confirm fix
|
|
709
666
|
Step 6: hydra-git → commit with descriptive message
|
|
710
667
|
```
|
|
711
668
|
|
|
712
|
-
|
|
669
|
+
## Dispatch Logging
|
|
713
670
|
|
|
714
|
-
After every task completion (unless `/hydra:quiet` is active), show a dispatch summary:
|
|
671
|
+
After every task completion that involves Hydra subagent dispatches (unless `/hydra:quiet` is active), show a dispatch summary:
|
|
715
672
|
|
|
716
673
|
```
|
|
717
674
|
| Step | Agent | Task | Time |
|
|
@@ -721,11 +678,9 @@ After every task completion (unless `/hydra:quiet` is active), show a dispatch s
|
|
|
721
678
|
| 3 | hydra-coder | Fix auth bug | 8.4s |
|
|
722
679
|
| 4 | hydra-guard | Security scan | 2.1s |
|
|
723
680
|
| 5 | hydra-runner | Verify fix | 4.8s |
|
|
724
|
-
|
|
725
|
-
Delegation: 5/5 (100%) — Opus direct: 0
|
|
726
681
|
```
|
|
727
682
|
|
|
728
|
-
|
|
683
|
+
When no dispatches occurred in a turn, no log is shown.
|
|
729
684
|
|
|
730
685
|
## Verification Protocol
|
|
731
686
|
|
|
@@ -1108,7 +1063,7 @@ Don't explain the routing. Don't ask permission. Just do it. The user asked for
|
|
|
1108
1063
|
a process narration. If a head does the work, present the output as if you did it.
|
|
1109
1064
|
|
|
1110
1065
|
### Speed and Parallelism
|
|
1111
|
-
|
|
1066
|
+
When a task naturally decomposes into independent subtasks, dispatch the relevant Hydra subagents in a single message so they run in parallel. Sequential dispatch of independent work wastes wall-clock time. See "What Hydra Offers" above for the agent menu.
|
|
1112
1067
|
|
|
1113
1068
|
### Escalate, Never Downgrade on Retry
|
|
1114
1069
|
If Haiku's output wasn't good enough, don't try Haiku again or even Sonnet. Just do it yourself.
|
|
@@ -1213,5 +1168,5 @@ follow-up questions.
|
|
|
1213
1168
|
|
|
1214
1169
|
## Reference Material
|
|
1215
1170
|
|
|
1216
|
-
- `references/routing-guide.md` —
|
|
1171
|
+
- `references/routing-guide.md` — Dispatch examples and decision flowchart for when subagents help
|
|
1217
1172
|
- `references/model-capabilities.md` — What each model can and can't do
|
|
@@ -80,6 +80,37 @@ const { stats, totalTurns, haikuCost, sonnetCost, opusCost,
|
|
|
80
80
|
delegatedTurns, delegationRate, sessionFile, unknownModels } = summary;
|
|
81
81
|
|
|
82
82
|
const bar = '━'.repeat(40);
|
|
83
|
+
|
|
84
|
+
// No-delegation guidance branch — when no Hydra subagents dispatched OR savings below indicator threshold
|
|
85
|
+
if (delegatedTurns === 0 || savedUSD < 0.01) {
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log('🐉 Hydra Stats');
|
|
88
|
+
console.log(bar);
|
|
89
|
+
console.log('Session: ' + path.basename(sessionFile));
|
|
90
|
+
console.log('Turns: ' + totalTurns);
|
|
91
|
+
console.log(bar);
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log('🟣 Opus (' + stats.opus.turns + ' turns): ' + fmt(stats.opus.input + stats.opus.cache_create) + ' in / ' + fmt(stats.opus.output) + ' out → \$' + opusCost.toFixed(3));
|
|
94
|
+
console.log(bar);
|
|
95
|
+
console.log('');
|
|
96
|
+
console.log('No Hydra subagent dispatches recorded in this session.');
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log('Hydra works best when invoked explicitly. Try:');
|
|
99
|
+
console.log(' /hydra:scout — codebase exploration on Haiku');
|
|
100
|
+
console.log(' /hydra:guard — security scan on Haiku');
|
|
101
|
+
console.log(' /hydra:preflight — environment validation');
|
|
102
|
+
console.log(' /hydra:map — codebase dependency map');
|
|
103
|
+
console.log('');
|
|
104
|
+
console.log('Or include \"use hydra\" in prompts that involve multi-file');
|
|
105
|
+
console.log('exploration, codebase analysis, or routine verification.');
|
|
106
|
+
console.log(bar);
|
|
107
|
+
if (unknownModels && unknownModels.size > 0) {
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log('⚠️ Unknown models (not counted): ' + Array.from(unknownModels).join(', '));
|
|
110
|
+
}
|
|
111
|
+
process.exit(0);
|
|
112
|
+
}
|
|
113
|
+
|
|
83
114
|
console.log('');
|
|
84
115
|
console.log('🐉 Hydra Stats');
|
|
85
116
|
console.log(bar);
|
|
@@ -47,6 +47,76 @@ process.stdin.on('end', () => {
|
|
|
47
47
|
fs.appendFileSync(trackingFile, filePath + '\n');
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// === Sentinel Pending Flag ===
|
|
51
|
+
// Write a flag file that the statusline hook reads.
|
|
52
|
+
// This shows "⚠ Sentinel pending" in the status bar until cleared.
|
|
53
|
+
const sentinelDir = path.join(os.tmpdir(), 'hydra-sentinel');
|
|
54
|
+
const sentinelFlag = path.join(sentinelDir, `${sessionId}-pending.json`);
|
|
55
|
+
|
|
56
|
+
if (!fs.existsSync(sentinelDir)) {
|
|
57
|
+
fs.mkdirSync(sentinelDir, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Read existing pending data or create new
|
|
61
|
+
let pending = { files: [], created_at: Date.now() };
|
|
62
|
+
try {
|
|
63
|
+
pending = JSON.parse(fs.readFileSync(sentinelFlag, 'utf8'));
|
|
64
|
+
} catch (e) {
|
|
65
|
+
// New flag
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add file to pending list (dedup)
|
|
69
|
+
if (!pending.files.includes(filePath)) {
|
|
70
|
+
pending.files.push(filePath);
|
|
71
|
+
}
|
|
72
|
+
pending.updated_at = Date.now();
|
|
73
|
+
|
|
74
|
+
fs.writeFileSync(sentinelFlag, JSON.stringify(pending));
|
|
75
|
+
|
|
76
|
+
// === Substantial-Edit Detection + Directive Injection (v2.4.0+) ===
|
|
77
|
+
// For Write, MultiEdit, or large Edits, inject a directive recommending
|
|
78
|
+
// hydra-sentinel-scan dispatch. Trivial edits stay silent.
|
|
79
|
+
const toolName = data.tool_name || '';
|
|
80
|
+
const isWrite = toolName === 'Write';
|
|
81
|
+
const isMultiEdit = toolName === 'MultiEdit';
|
|
82
|
+
const oldString = data.tool_input?.old_string || '';
|
|
83
|
+
const newString = data.tool_input?.new_string || '';
|
|
84
|
+
|
|
85
|
+
let isSubstantial = isWrite || isMultiEdit;
|
|
86
|
+
if (!isSubstantial) {
|
|
87
|
+
const oldLines = (oldString.match(/\n/g) || []).length;
|
|
88
|
+
const newLines = (newString.match(/\n/g) || []).length;
|
|
89
|
+
isSubstantial = oldLines > 5 || newLines > 5 ||
|
|
90
|
+
oldString.length > 200 || newString.length > 200;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (isSubstantial) {
|
|
94
|
+
// Best-effort codebase map risk lookup
|
|
95
|
+
let riskNote = '';
|
|
96
|
+
try {
|
|
97
|
+
const mapPath = path.join(process.cwd(), '.claude', 'hydra', 'codebase-map.json');
|
|
98
|
+
if (fs.existsSync(mapPath)) {
|
|
99
|
+
const map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
|
|
100
|
+
const relPath = path.relative(process.cwd(), filePath).split(path.sep).join('/');
|
|
101
|
+
const fileEntry = map.files && map.files[relPath];
|
|
102
|
+
if (fileEntry && fileEntry.risk) {
|
|
103
|
+
riskNote = ` Risk: ${fileEntry.risk} (${fileEntry.dependents_count || 0} dependents).`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// Map missing or malformed — no risk note appended
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const directive = {
|
|
111
|
+
hookSpecificOutput: {
|
|
112
|
+
hookEventName: 'PostToolUse',
|
|
113
|
+
additionalContext: `🐉 Hydra Auto-Guard: Substantial change to ${path.basename(filePath)}.${riskNote} Dispatch hydra-sentinel-scan to verify integration before presenting results to the user.`
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
process.stdout.write(JSON.stringify(directive));
|
|
118
|
+
}
|
|
119
|
+
|
|
50
120
|
} catch (e) {
|
|
51
121
|
// Silently fail — NEVER block Claude Code
|
|
52
122
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hail-hydra-cc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Multi-agent orchestration framework for Claude Code. Routes tasks to specialized Haiku/Sonnet subagents while Opus orchestrates — inspired by speculative decoding. ~50% API cost reduction.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"hail-hydra-cc": "bin/cli.js"
|
package/src/display.js
CHANGED
|
@@ -42,7 +42,7 @@ function showInstallComplete(statusLineConfigured = true) {
|
|
|
42
42
|
console.log(chalk.cyan.bold(' \uD83D\uDC09 Hail Hydra! Framework deployed and ready.'));
|
|
43
43
|
console.log(chalk.gray(' ' + '\u2500'.repeat(45)));
|
|
44
44
|
console.log(chalk.green(` \u2714 10 agents installed`));
|
|
45
|
-
console.log(chalk.green(` \u2714
|
|
45
|
+
console.log(chalk.green(` \u2714 12 slash commands installed`));
|
|
46
46
|
console.log(chalk.green(` \u2714 4 hooks registered`));
|
|
47
47
|
if (statusLineConfigured) {
|
|
48
48
|
console.log(chalk.green(` \u2714 StatusLine configured`));
|
package/src/files.js
CHANGED
|
@@ -89,8 +89,12 @@ const commands = {
|
|
|
89
89
|
'map': readBundled('commands/hydra/map.md'),
|
|
90
90
|
'preflight': readBundled('commands/hydra/preflight.md'),
|
|
91
91
|
'stats': readBundled('commands/hydra/stats.md'),
|
|
92
|
+
'stfu': readBundled('commands/hydra/stfu.md'),
|
|
92
93
|
};
|
|
93
94
|
|
|
95
|
+
// Standalone skills (beyond the canonical hydra/SKILL.md). v2.4.0+ — STFU-Agents.
|
|
96
|
+
const stfuAgentsSkill = readBundled('skills/stfu-agents/SKILL.md');
|
|
97
|
+
|
|
94
98
|
const hooks = {
|
|
95
99
|
'hydra-check-update': readBundled('hooks/hydra-check-update.js'),
|
|
96
100
|
'hydra-statusline': readBundled('hooks/hydra-statusline.js'),
|
|
@@ -103,4 +107,4 @@ const binaryHooks = {
|
|
|
103
107
|
'hydra-task-complete.wav': path.join(FILES_DIR, 'hooks', 'hydra-task-complete.wav'),
|
|
104
108
|
};
|
|
105
109
|
|
|
106
|
-
module.exports = { agents, skill, references, commands, hooks, binaryHooks };
|
|
110
|
+
module.exports = { agents, skill, stfuAgentsSkill, references, commands, hooks, binaryHooks };
|
package/src/installer.js
CHANGED
|
@@ -5,7 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const chalk = require('chalk');
|
|
7
7
|
|
|
8
|
-
const { agents, skill, references, commands, hooks, binaryHooks } = require('./files');
|
|
8
|
+
const { agents, skill, stfuAgentsSkill, references, commands, hooks, binaryHooks } = require('./files');
|
|
9
9
|
const { showInstallHeader, showFileInstalled, showInstallComplete, showStatusTable, VERSION } = require('./display');
|
|
10
10
|
|
|
11
11
|
// ── Install locations ────────────────────────────────────────────────────────
|
|
@@ -33,6 +33,14 @@ function buildManifest(base) {
|
|
|
33
33
|
dest: path.join(base, 'skills', 'hydra', 'SKILL.md'),
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
const stfuAgentsSkillEntry = {
|
|
37
|
+
type: 'skill',
|
|
38
|
+
key: 'stfu-agents/SKILL.md',
|
|
39
|
+
display: 'skills/stfu-agents/SKILL.md',
|
|
40
|
+
content: stfuAgentsSkill,
|
|
41
|
+
dest: path.join(base, 'skills', 'stfu-agents', 'SKILL.md'),
|
|
42
|
+
};
|
|
43
|
+
|
|
36
44
|
const refEntries = Object.entries(references).map(([key, content]) => ({
|
|
37
45
|
type: 'reference',
|
|
38
46
|
key,
|
|
@@ -57,7 +65,7 @@ function buildManifest(base) {
|
|
|
57
65
|
dest: path.join(base, 'skills', 'hydra', 'VERSION'),
|
|
58
66
|
};
|
|
59
67
|
|
|
60
|
-
return [...agentEntries, skillEntry, ...refEntries, ...commandEntries, versionEntry];
|
|
68
|
+
return [...agentEntries, skillEntry, stfuAgentsSkillEntry, ...refEntries, ...commandEntries, versionEntry];
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|