Skip to content

Commit 6130c89

Browse files
committed
update adding claude.md
1 parent 0a9a44a commit 6130c89

12 files changed

Lines changed: 1475 additions & 47 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/sh
2+
if grep -q "caliber" .git/hooks/pre-commit 2>/dev/null; then
3+
exit 0
4+
fi
5+
FLAG="/tmp/caliber-nudge-$(echo "$PWD" | shasum | cut -c1-8)"
6+
find /tmp -maxdepth 1 -name "caliber-nudge-*" -mmin +120 -delete 2>/dev/null
7+
if [ -f "$FLAG" ]; then
8+
exit 0
9+
fi
10+
touch "$FLAG"
11+
printf '{"decision":"block","reason":"Caliber agent config sync is not set up on this machine. Tell the user: This project uses Caliber to keep agent configs in sync — it is not set up yet. Want me to run /setup-caliber? (~30 seconds)"}'

.claude/settings.json

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"hooks": {
3+
"Stop": [
4+
{
5+
"matcher": "",
6+
"hooks": [
7+
{
8+
"type": "command",
9+
"command": ".claude/hooks/caliber-check-sync.sh",
10+
"description": "Caliber: offer setup if not configured"
11+
}
12+
]
13+
}
14+
],
15+
"PostToolUse": [
16+
{
17+
"matcher": "",
18+
"hooks": [
19+
{
20+
"type": "command",
21+
"command": "caliber learn observe",
22+
"description": "Caliber: recording tool usage for session learning"
23+
}
24+
]
25+
}
26+
],
27+
"PostToolUseFailure": [
28+
{
29+
"matcher": "",
30+
"hooks": [
31+
{
32+
"type": "command",
33+
"command": "caliber learn observe --failure",
34+
"description": "Caliber: recording tool failure for session learning"
35+
}
36+
]
37+
}
38+
],
39+
"UserPromptSubmit": [
40+
{
41+
"matcher": "",
42+
"hooks": [
43+
{
44+
"type": "command",
45+
"command": "caliber learn observe --prompt",
46+
"description": "Caliber: recording user prompt for correction detection"
47+
}
48+
]
49+
}
50+
],
51+
"SessionEnd": [
52+
{
53+
"matcher": "",
54+
"hooks": [
55+
{
56+
"type": "command",
57+
"command": "caliber learn finalize --auto",
58+
"description": "Caliber: finalizing session learnings"
59+
}
60+
]
61+
},
62+
{
63+
"matcher": "",
64+
"hooks": [
65+
{
66+
"type": "command",
67+
"command": "caliber refresh --quiet",
68+
"description": "Caliber: auto-refreshing docs based on code changes"
69+
}
70+
]
71+
}
72+
]
73+
},
74+
"permissions": {
75+
"allow": [
76+
"Bash(git *)"
77+
]
78+
}
79+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
---
2+
name: abuse-record-insert
3+
description: Inserts a complete abuse incident across the `abuse` and `abuse_data` tables using make_insert_query(). Use when adding new code that reports an abuse incident — manual, CSV import, or IMAP-triggered. Covers the exact field list for both tables, getLastInsertId(), ImapAbuseCheck::fix_headers() for abuse_headers, and clientMail() notification via client/abuse.tpl. Trigger phrases: 'insert abuse record', 'report abuse', 'add abuse entry', 'log abuse incident'. Do NOT use for querying, updating, or deleting existing abuse records.
4+
---
5+
# Abuse Record Insert
6+
7+
## Critical
8+
9+
- **Never use PDO.** Always use `$db = get_module_db('default')` and `make_insert_query()`.
10+
- **Never interpolate raw `$_GET`/`$_POST` into SQL.** Use `$db->real_escape()` or `make_insert_query()`.
11+
- **Always validate the IP first** with `validIp($ip, false)` before any DB write.
12+
- **Always resolve the owner** via `get_server_from_ip($ip)` and verify `$server_data['email'] != ''` before inserting.
13+
- **`abuse_id` must be `null`** in the `abuse` insert — the DB auto-increments it.
14+
- **`abuse_status` must be `'pending'`** for all new records.
15+
- Valid `abuse_type` values: `'scanning'`, `'hacking'`, `'spam'`, `'child porn'`, `'phishing site'`, `'other'`, `'uceprotect'`, `'trendmicro'`.
16+
17+
## Instructions
18+
19+
1. **Get the DB handle**
20+
21+
```php
22+
$db = get_module_db('default');
23+
```
24+
25+
Verify `$db` is not null before proceeding.
26+
27+
2. **Validate the IP and resolve the server owner**
28+
29+
```php
30+
if (!validIp($ip, false)) {
31+
// skip or surface an error — do not insert
32+
}
33+
$server_data = get_server_from_ip($ip);
34+
if (!isset($server_data['email']) || $server_data['email'] == '') {
35+
add_output('Error Finding Owner For ' . $ip . '<br>');
36+
return;
37+
}
38+
$email = $server_data['email'];
39+
```
40+
41+
`$server_data['email']` is the billing contact; `$server_data['email_abuse']` is the address to notify (may differ).
42+
43+
3. **Insert the `abuse` row**
44+
45+
Use `mysql_now()` for current-time inserts, or a pre-formatted `MYSQL_DATE_FORMAT` string for historical dates (e.g. from CSV).
46+
47+
```php
48+
$db->query(make_insert_query('abuse', [
49+
'abuse_id' => null,
50+
'abuse_time' => mysql_now(), // or $date->format(MYSQL_DATE_FORMAT)
51+
'abuse_ip' => $ip,
52+
'abuse_type' => $type, // one of the valid values listed in Critical
53+
'abuse_amount' => 1,
54+
'abuse_lid' => $email,
55+
'abuse_status' => 'pending',
56+
]), __LINE__, __FILE__);
57+
$id = $db->getLastInsertId('abuse', 'abuse_id');
58+
```
59+
60+
Verify `$id > 0` before proceeding to Step 4.
61+
62+
4. **Insert the `abuse_data` row** (when raw headers/body are available)
63+
64+
Always pass headers through `ImapAbuseCheck::fix_headers()` to strip HTML and normalise newlines.
65+
66+
```php
67+
$db->query(make_insert_query('abuse_data', [
68+
'abuse_id' => $id,
69+
'abuse_headers' => ImapAbuseCheck::fix_headers($rawHeaders),
70+
]), __LINE__, __FILE__);
71+
```
72+
73+
For IMAP-sourced records the class concatenates plain + HTML message bodies:
74+
75+
```php
76+
'abuse_headers' => trim(ImapAbuseCheck::fix_headers($this->plainmsg . $this->htmlmsg)),
77+
```
78+
79+
Skip this step only when no header/body content exists (e.g. bare IP-list CSV imports without evidence).
80+
81+
5. **Build and send the client notification email**
82+
83+
The template key is always `'client/abuse.tpl'`. The link key used in the template is `md5($id . $ip . $type)`.
84+
85+
```php
86+
$subject = 'InterServer Abuse Report for ' . $ip;
87+
$message = str_replace(
88+
['{$email}', '{$ip}', '{$type}', '{$count}', '{$id}', '{$key}'],
89+
[
90+
$server_data['email_abuse'],
91+
$ip,
92+
$type,
93+
$amount, // 1 for most sources; $amount for manual single-IP form
94+
$id,
95+
md5($id . $ip . $type),
96+
],
97+
$email_template // loaded earlier from the abuse email template file
98+
);
99+
(new \MyAdmin\Mail())->clientMail($subject, $message, $server_data['email_abuse'], 'client/abuse.tpl');
100+
```
101+
102+
Send to `$server_data['email_abuse']`, not `$email` (they can differ).
103+
104+
6. **Confirm to the admin UI**
105+
106+
```php
107+
add_output('Abuse Entry for ' . $ip . ' Added - Emailing '
108+
. ($server_data['email_abuse'] !== $email
109+
? $server_data['email_abuse'] . ' (for client ' . $email . ')'
110+
: $server_data['email_abuse'])
111+
. '<br>');
112+
```
113+
114+
## Examples
115+
116+
**User says:** "Add code that manually reports a single IP for spam abuse and emails the owner."
117+
118+
**Actions taken:**
119+
120+
```php
121+
$db = get_module_db('default');
122+
$ip = $db->real_escape($GLOBALS['tf']->variables->request['ip']);
123+
$type = 'spam';
124+
125+
if (!validIp($ip, false)) {
126+
add_output('Invalid IP<br>');
127+
return;
128+
}
129+
130+
$server_data = get_server_from_ip($ip);
131+
if (!isset($server_data['email']) || $server_data['email'] == '') {
132+
add_output('Error Finding Owner For ' . $ip . '<br>');
133+
return;
134+
}
135+
$email = $server_data['email'];
136+
137+
// Step 3 — abuse row
138+
$db->query(make_insert_query('abuse', [
139+
'abuse_id' => null,
140+
'abuse_time' => mysql_now(),
141+
'abuse_ip' => $ip,
142+
'abuse_type' => $type,
143+
'abuse_amount' => 1,
144+
'abuse_lid' => $email,
145+
'abuse_status' => 'pending',
146+
]), __LINE__, __FILE__);
147+
$id = $db->getLastInsertId('abuse', 'abuse_id');
148+
149+
// Step 4 — abuse_data row
150+
$rawHeaders = $GLOBALS['tf']->variables->request['headers'] ?? '';
151+
$db->query(make_insert_query('abuse_data', [
152+
'abuse_id' => $id,
153+
'abuse_headers' => ImapAbuseCheck::fix_headers($rawHeaders),
154+
]), __LINE__, __FILE__);
155+
156+
// Step 5 — notify
157+
$subject = 'InterServer Abuse Report for ' . $ip;
158+
$message = str_replace(
159+
['{$email}', '{$ip}', '{$type}', '{$count}', '{$id}', '{$key}'],
160+
[$server_data['email_abuse'], $ip, $type, 1, $id, md5($id . $ip . $type)],
161+
$email_template
162+
);
163+
(new \MyAdmin\Mail())->clientMail($subject, $message, $server_data['email_abuse'], 'client/abuse.tpl');
164+
165+
// Step 6 — UI feedback
166+
add_output('Abuse Entry for ' . $ip . ' Added - Emailing ' . $server_data['email_abuse'] . '<br>');
167+
```
168+
169+
**Result:** One row in `abuse` with `abuse_status='pending'`, one row in `abuse_data` with sanitised headers, and a notification email sent to the abuse contact.
170+
171+
## Common Issues
172+
173+
**`make_insert_query` produces a query with wrong column count or missing fields:**
174+
- Ensure `abuse_id => null` is present — omitting it causes an auto-increment mismatch.
175+
- `abuse_lid` must be the billing email string, not an integer ID.
176+
177+
**`getLastInsertId` returns 0 or null:**
178+
- The `make_insert_query()` call failed silently. Always pass `__LINE__, __FILE__` as the 2nd/3rd args to `$db->query()` so errors surface in logs.
179+
- Confirm the `abuse` table exists and the DB handle is for `'default'`, not `'mail'`.
180+
181+
**`ImapAbuseCheck::fix_headers()` not found:**
182+
- The class file is not autoloaded. Ensure `function_requirements('class.ImapAbuseCheck')` has been called, or that the file is included via `add_requirement` in `Plugin::getRequirements()`.
183+
184+
**`clientMail()` sends to the wrong address:**
185+
- Use `$server_data['email_abuse']`, not `$server_data['email']`. These can be different accounts (e.g., resellers).
186+
187+
**Email template placeholders not replaced:**
188+
- The `str_replace` keys must exactly match `{$email}`, `{$ip}`, `{$type}`, `{$count}`, `{$id}`, `{$key}` — curly braces and dollar signs included.
189+
- `$email_template` must be loaded from the abuse email template before the `str_replace` call. Check that `$email_template` is not empty.
190+
191+
**Historical date inserts (CSV import) showing wrong timestamps:**
192+
- Do not use `mysql_now()` for CSV rows. Parse the source date with `new \DateTime(...)` and format with `$date->format(MYSQL_DATE_FORMAT)` before passing to `make_insert_query`.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
name: find-skills
3+
description: Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills.
4+
---
5+
6+
# Find Skills
7+
8+
Search the public skill registry for community-contributed skills
9+
relevant to the user's current task and install them into this project.
10+
11+
## Instructions
12+
13+
1. Identify the key technologies, frameworks, or task types from the
14+
user's request that might have community skills available
15+
2. Ask the user: "Would you like me to search for community skills
16+
for [identified technologies]?"
17+
3. If the user agrees, run:
18+
```bash
19+
caliber skills --query "<relevant terms>"
20+
```
21+
This outputs the top 5 matching skills with scores and descriptions.
22+
4. Present the results to the user and ask which ones to install
23+
5. Install the selected skills:
24+
```bash
25+
caliber skills --install <slug1>,<slug2>
26+
```
27+
6. Read the installed SKILL.md files to load them into your current
28+
context so you can use them immediately in this session
29+
7. Summarize what was installed and continue with the user's task
30+
31+
## Examples
32+
33+
User: "let's build a web app using React"
34+
-> "I notice you want to work with React. Would you like me to search
35+
for community skills that could help with React development?"
36+
-> If yes: run `caliber skills --query "react frontend"`
37+
-> Show the user the results, ask which to install
38+
-> Run `caliber skills --install <selected-slugs>`
39+
-> Read the installed files and continue
40+
41+
User: "help me set up Docker for this project"
42+
-> "Would you like me to search for Docker-related skills?"
43+
-> If yes: run `caliber skills --query "docker deployment"`
44+
45+
User: "I need to write tests for this Python ML pipeline"
46+
-> "Would you like me to find skills for Python ML testing?"
47+
-> If yes: run `caliber skills --query "python machine-learning testing"`
48+
49+
## When NOT to trigger
50+
51+
- The user is working within an already well-configured area
52+
- You already suggested skills for this technology in this session
53+
- The user is in the middle of urgent debugging or time-sensitive work
54+
- The technology is too generic (e.g. just "code" or "programming")

0 commit comments

Comments
 (0)