Skip to content

Commit 9ecdc51

Browse files
committed
update adding claude.md
1 parent b10dde8 commit 9ecdc51

12 files changed

Lines changed: 1341 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: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
---
2+
name: directadmin-api
3+
description: Adds a new DirectAdmin API function to `src/directadmin.inc.php` using the `directadmin_req()` cURL wrapper. Use when user says 'add API call', 'new DA endpoint', 'call directadmin', or needs a new function in `directadmin.inc.php`. Handles `DIRECTADMIN_USERNAME`/`DIRECTADMIN_PASSWORD` auth and `myadmin_log()` logging automatically via the wrapper. Do NOT use for modifying `Plugin.php` hooks or settings registration.
4+
---
5+
# DirectAdmin API
6+
7+
## Critical
8+
9+
- **Never** build cURL requests manually — all HTTP calls MUST go through `directadmin_req($url, $post, $options)` in `src/directadmin.inc.php`.
10+
- **Never** hardcode credentials — always use the `DIRECTADMIN_USERNAME` and `DIRECTADMIN_PASSWORD` constants in `$post`.
11+
- `StatisticClient` tick/report is handled inside `directadmin_req()` automatically — do NOT add it to your new function.
12+
- After adding a function, you MUST update `tests/DirectadminIncTest.php`: increment the count in `testFunctionCount()` (currently asserts 16) and add a `testYourFunctionIsDefined()` test.
13+
- All API endpoints live under `https://www.directadmin.com/clients/api/` (PHP scripts) or `https://www.directadmin.com/cgi-bin/` (CGI endpoints).
14+
15+
## Instructions
16+
17+
### Step 1 — Identify the endpoint type
18+
19+
Determine the correct base URL:
20+
- Read-only data endpoints: `https://www.directadmin.com/clients/api/{endpoint}.php`
21+
- Mutating CGI actions: `https://www.directadmin.com/cgi-bin/{action}`
22+
23+
Verify the endpoint exists in the DA portal docs or an existing `bin/` script before proceeding.
24+
25+
### Step 2 — Write the function in `src/directadmin.inc.php`
26+
27+
Append the function at the bottom of the file. Use this exact structure:
28+
29+
**For a read-only API endpoint** (mirrors `directadmin_get_os_list`, `directadmin_get_products`):
30+
```php
31+
/**
32+
* Short description of what this fetches.
33+
*
34+
* @param string $paramName Description
35+
* @return string Raw API response
36+
*/
37+
function directadmin_get_something($paramName = '')
38+
{
39+
$url = 'https://www.directadmin.com/clients/api/something.php';
40+
$post = [
41+
'uid' => DIRECTADMIN_USERNAME,
42+
'id' => DIRECTADMIN_USERNAME,
43+
'password' => DIRECTADMIN_PASSWORD,
44+
'api' => 1,
45+
];
46+
$response = directadmin_req($url, $post);
47+
myadmin_log('licenses', 'info', $response, __LINE__, __FILE__);
48+
return $response;
49+
}
50+
```
51+
52+
**For a mutating endpoint** (mirrors `activate_directadmin`, `directadmin_makepayment`):
53+
```php
54+
/**
55+
* Short description of the action.
56+
*
57+
* @param string $lid License ID
58+
* @param string $param Additional param
59+
* @return string|false API response or false on failure
60+
*/
61+
function directadmin_do_action($lid, $param)
62+
{
63+
myadmin_log('licenses', 'info', "Called directadmin_do_action({$lid}, {$param})", __LINE__, __FILE__);
64+
$url = 'https://www.directadmin.com/cgi-bin/someaction';
65+
$post = [
66+
'uid' => DIRECTADMIN_USERNAME,
67+
'id' => DIRECTADMIN_USERNAME,
68+
'password' => DIRECTADMIN_PASSWORD,
69+
'api' => 1,
70+
'lid' => $lid,
71+
'param' => $param,
72+
];
73+
$options = [
74+
CURLOPT_REFERER => 'https://www.directadmin.com/clients/license.php?lid='.$lid
75+
];
76+
$response = directadmin_req($url, $post, $options);
77+
request_log('licenses', $GLOBALS['tf']->session->account_id, __FUNCTION__, 'directadmin', 'someaction', $post, $response);
78+
myadmin_log('licenses', 'info', $response, __LINE__, __FILE__);
79+
return $response;
80+
}
81+
```
82+
83+
Verify: the function appears exactly once in the file and uses `directadmin_req()` — not `getcurlpage()` directly.
84+
85+
### Step 3 — Parse the response if needed
86+
87+
DA API responses are either:
88+
- **Query-string lines** (list endpoints) — parse with `parse_str()`:
89+
```php
90+
$lines = explode("\n", trim($response));
91+
foreach (array_values($lines) as $line) {
92+
parse_str($line, $item);
93+
if (isset($item['lid'])) {
94+
$results[$item['lid']] = $item;
95+
}
96+
}
97+
return $results;
98+
```
99+
- **Key=value lines** (OS list) — parse with `explode('=', $row)`
100+
- **Raw string** (success/error messages) — return directly
101+
102+
Verify: for list endpoints, your return type is `array`; for scalar responses, it is `string|false`.
103+
104+
### Step 4 — Update `tests/DirectadminIncTest.php`
105+
106+
**4a.** Increment the function count assertion (this step uses output from Step 2 — you must know your new total):
107+
```php
108+
// Change:
109+
$this->assertCount(16, $matches[0], 'Expected 16 function definitions in directadmin.inc.php');
110+
// To (if you added 1 function):
111+
$this->assertCount(17, $matches[0], 'Expected 17 function definitions in directadmin.inc.php');
112+
```
113+
114+
**4b.** Add a test for your function's existence in the `DirectadminIncTest` class:
115+
```php
116+
/**
117+
* Tests that directadmin_get_something() is defined in the source.
118+
* Brief description of what it does.
119+
*/
120+
public function testDirectadminGetSomethingIsDefined(): void
121+
{
122+
$this->assertStringContainsString(
123+
'function directadmin_get_something(',
124+
self::$sourceContents
125+
);
126+
}
127+
```
128+
129+
Verify: the PHPUnit test suite passes with no failures.
130+
131+
### Step 5 — Run the tests
132+
133+
Run the full test suite from the package root to confirm all tests pass:
134+
135+
```php
136+
// In tests/DirectadminIncTest.php — confirm updated count guard:
137+
$this->assertCount(17, $matches[0], 'Expected 17 function definitions in src/directadmin.inc.php');
138+
```
139+
140+
Execute `phpunit` (via the binary in `vendor/bin/` or `composer test`) and verify all assertions are green before considering the function complete.
141+
142+
## Examples
143+
144+
**User says:** "Add an API call to suspend a DirectAdmin license by lid"
145+
146+
**Actions taken:**
147+
1. Identify endpoint: mutating action → `https://www.directadmin.com/cgi-bin/suspendlicense`
148+
2. Append to `src/directadmin.inc.php`:
149+
```php
150+
/**
151+
* Suspend an active DirectAdmin license.
152+
*
153+
* @param string $lid License ID to suspend
154+
* @return string API response
155+
*/
156+
function directadmin_suspend_license($lid)
157+
{
158+
myadmin_log('licenses', 'info', "Called directadmin_suspend_license({$lid})", __LINE__, __FILE__);
159+
$url = 'https://www.directadmin.com/cgi-bin/suspendlicense';
160+
$post = [
161+
'uid' => DIRECTADMIN_USERNAME,
162+
'id' => DIRECTADMIN_USERNAME,
163+
'password' => DIRECTADMIN_PASSWORD,
164+
'api' => 1,
165+
'lid' => $lid,
166+
];
167+
$options = [
168+
CURLOPT_REFERER => 'https://www.directadmin.com/clients/license.php?lid='.$lid
169+
];
170+
$response = directadmin_req($url, $post, $options);
171+
request_log('licenses', $GLOBALS['tf']->session->account_id, __FUNCTION__, 'directadmin', 'suspendlicense', $post, $response);
172+
myadmin_log('licenses', 'info', $response, __LINE__, __FILE__);
173+
return $response;
174+
}
175+
```
176+
3. Update `testFunctionCount()` in `tests/DirectadminIncTest.php`: `assertCount(16, ...)``assertCount(17, ...)`
177+
4. Add `testDirectadminSuspendLicenseIsDefined()` test
178+
5. Run the PHPUnit test suite → all green
179+
180+
**Result:** New function available to callers; monitored by StatisticClient via `directadmin_req()`.
181+
182+
## Common Issues
183+
184+
**`testFunctionCount` fails with "Expected 16 function definitions, got 17"**
185+
You added a function but forgot to update the count in `tests/DirectadminIncTest.php:248`. Change `assertCount(16, ...)` to `assertCount(17, ...)`.
186+
187+
**`Call to undefined constant DIRECTADMIN_USERNAME`**
188+
This constant is registered by `Plugin::getSettings()` and only exists inside the MyAdmin runtime. In bin scripts, bootstrap MyAdmin first: `require_once __DIR__.'/../../../../include/functions.inc.php';`. Tests cannot call functions that use this constant without mocking the constant.
189+
190+
**`getcurlpage()` returns `false` / StatisticClient reports failure**
191+
The DA API returned a cURL error. Check: (1) the full URL is correct including scheme; (2) `DIRECTADMIN_USERNAME`/`DIRECTADMIN_PASSWORD` are set; (3) the endpoint path matches DA portal docs exactly — `clients/api/` vs `cgi-bin/` matters.
192+
193+
**Response is empty string instead of data**
194+
DA returns empty body for auth failures, not an HTTP error code. Confirm credentials are correct by testing via `php bin/directadmin_licenses.php` which uses the same auth path.
195+
196+
**`request_log()` — undefined function**
197+
Only call `request_log()` for mutating operations inside the MyAdmin runtime where `$GLOBALS['tf']->session->account_id` is set. Do not call it in read-only functions or it will fatal in CLI contexts.
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)