Skip to content

Commit 464d43d

Browse files
committed
update adding claude.md
1 parent aa53d4b commit 464d43d

10 files changed

Lines changed: 909 additions & 0 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: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
---
2+
name: directadmin-api-call
3+
description: Creates a DirectAdmin API call using HTTPSocket for connect/authenticate/query/parse pattern. Use when user says 'call DirectAdmin API', 'query DA endpoint', 'add DA command', or adds new bin/ scripts. Covers SSL connection on port 2222, set_login, query with options array, fetch_parsed_body, and error checking via $result['error']. Do NOT use for modifying src/HTTPSocket.php itself or for non-DirectAdmin API integrations.
4+
---
5+
# DirectAdmin API Call
6+
7+
## Critical
8+
9+
- **Never hardcode credentials** in committed files — `$hash`/`$password` must come from DB (`backup_key`) or be placeholder comments only
10+
- Always use the `'ssl://'` stream prefix for production connections — plain HTTP is only for local debug (seen commented out in bin scripts)
11+
- Check `$result['error'] != "0"` (string `"0"`, not int) — this is DirectAdmin's convention
12+
- Always call `fetch_parsed_body()` immediately after `query()` before any other query
13+
- In `src/Plugin.php` event handlers, always call `$event->stopPropagation()` after handling
14+
15+
## Instructions
16+
17+
1. **Import HTTPSocket** at the top of the file:
18+
```php
19+
use Detain\MyAdminDirectAdminStorage\HTTPSocket;
20+
```
21+
For `bin/` scripts also add: `require_once('../vendor/autoload.php');`
22+
For DB-accessing bin scripts: `include_once __DIR__.'/../../../../include/functions.inc.php';`
23+
24+
2. **Get server credentials** — in Plugin event handlers use:
25+
```php
26+
$settings = get_module_settings(self::$module); // self::$module = 'backups'
27+
$serverdata = get_service_master($serviceClass->getServer(), self::$module);
28+
$hash = $serverdata[$settings['PREFIX'].'_key'];
29+
$ip = $serverdata[$settings['PREFIX'].'_ip'];
30+
```
31+
In bin/ scripts query the DB directly:
32+
```php
33+
$db = get_module_db('backups');
34+
$db->query("select * from backup_masters where backup_name='".$db->real_escape($argv[1])."'", __LINE__, __FILE__);
35+
$db->next_record(MYSQL_ASSOC);
36+
$ip = $db->Record['backup_ip'];
37+
$password = $db->Record['backup_key'];
38+
```
39+
Verify credentials are non-empty before continuing.
40+
41+
3. **Connect and authenticate:**
42+
```php
43+
$sock = new HTTPSocket();
44+
$sock->connect('ssl://'.$ip, 2222);
45+
$sock->set_login('admin', $hash);
46+
```
47+
Use `($server_ssl == 'Y' ? 'ssl://' : '').$ip` if SSL is configurable.
48+
49+
4. **Build `$apiCmd` and `$apiOptions`, then query:**
50+
- POST with params array (most mutating commands):
51+
```php
52+
$apiCmd = '/CMD_API_SELECT_USERS';
53+
$apiOptions = [
54+
'location' => 'CMD_SELECT_USERS',
55+
'suspend' => 'Suspend',
56+
'select0' => $username,
57+
];
58+
$sock->query($apiCmd, $apiOptions);
59+
```
60+
- GET with query string (read-only commands):
61+
```php
62+
$sock->query('/CMD_API_SHOW_USER_CONFIG?user='.$username);
63+
```
64+
Verify `$apiCmd` starts with `/CMD_API_`.
65+
66+
5. **Fetch and check result:**
67+
```php
68+
$result = $sock->fetch_parsed_body();
69+
if ($result['error'] != "0") {
70+
// error path
71+
myadmin_log('directadmin', 'error', 'Error: Text:'.$result['text'].' Details:'.$result['details'], __LINE__, __FILE__, self::$module, $serviceClass->getId());
72+
$event['success'] = false;
73+
$event->stopPropagation();
74+
return;
75+
}
76+
```
77+
Special terminate case — also treat `$result['text'] == "System user {$username} does not exist!"` as success.
78+
79+
6. **Log the call** (required in Plugin event handlers):
80+
```php
81+
request_log(self::$module, $serviceClass->getCustid(), __FUNCTION__, 'directadmin', $apiCmd, $apiOptions, $result, $serviceClass->getId());
82+
myadmin_log('myadmin', 'info', 'DirectAdmin '.$apiCmd.' '.json_encode($apiOptions).' Response: '.json_encode($result), __LINE__, __FILE__, self::$module, $serviceClass->getId());
83+
```
84+
85+
## Examples
86+
87+
**User says:** "Add a bin script to unsuspend a user by hostname"
88+
89+
**Actions taken:**
90+
1. Create `bin/unsuspend_user.php`
91+
2. Include functions, query `backup_masters` for credentials by hostname arg
92+
3. Connect with `'ssl://'.$ip` on port 2222, `set_login('admin', $password)`
93+
4. Query `/CMD_API_SELECT_USERS` with `dounsuspend=y`, `select0=$username`
94+
5. `fetch_parsed_body()`, `print_r($result)`
95+
96+
**Result:**
97+
```php
98+
<?php
99+
use Detain\MyAdminDirectAdminStorage\HTTPSocket;
100+
include_once __DIR__.'/../../../../include/functions.inc.php';
101+
$db = get_module_db('backups');
102+
if (count($_SERVER['argv']) < 3) {
103+
die("Usage: {$_SERVER['argv'][0]} <hostname> <username>\n");
104+
}
105+
$db->query("select * from backup_masters where backup_name='".$db->real_escape($_SERVER['argv'][1])."'", __LINE__, __FILE__);
106+
if ($db->num_rows() == 0) { die("Invalid server\n"); }
107+
$db->next_record(MYSQL_ASSOC);
108+
$sock = new HTTPSocket();
109+
$sock->connect('ssl://'.$db->Record['backup_ip'], 2222);
110+
$sock->set_login('admin', $db->Record['backup_key']);
111+
$sock->query('/CMD_API_SELECT_USERS', [
112+
'location' => 'CMD_SELECT_USERS',
113+
'dounsuspend' => 'y',
114+
'select0' => $_SERVER['argv'][2],
115+
]);
116+
$result = $sock->fetch_parsed_body();
117+
print_r($result);
118+
```
119+
120+
## Common Issues
121+
122+
- **`$result['error']` is always `"1"` / empty response:** SSL handshake failed. Verify the server actually has a valid cert on port 2222: `openssl s_client -connect $ip:2222`. If self-signed, `HTTPSocket` still accepts it (verify_peer is off by default).
123+
- **`fetch_parsed_body()` returns `null` or non-array:** Called a second time after already fetching, or `query()` was never called. Always call `query()` then `fetch_parsed_body()` in sequence.
124+
- **`result['text']` = `"Cannot Login"` :** Wrong `$hash`/password. Confirm `backup_key` column value in `backup_masters` matches the DirectAdmin admin password.
125+
- **`result['details']` = `"Sorry, the password..."` :** DirectAdmin rejects the generated password. Retry with `generateRandomString(10, 2, 2, 2, 1)` in a loop as done in `src/Plugin.php` around the activate method.
126+
- **`result['details']` = `"Sorry, a group for that username already exists."` :** Username collision. Append/rotate a character: `$username = mb_substr($username.'a', 1)` and retry (see `src/Plugin.php` activate handler).
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)