Skip to content

Commit 55cdc5d

Browse files
committed
update
1 parent 557a35d commit 55cdc5d

12 files changed

Lines changed: 1222 additions & 0 deletions

File tree

.claude/settings.json

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(git *)"
5+
]
6+
},
7+
"hooks": {
8+
"PostToolUse": [
9+
{
10+
"matcher": "",
11+
"hooks": [
12+
{
13+
"type": "command",
14+
"command": "caliber learn observe",
15+
"description": "Caliber: recording tool usage for session learning"
16+
}
17+
]
18+
}
19+
],
20+
"PostToolUseFailure": [
21+
{
22+
"matcher": "",
23+
"hooks": [
24+
{
25+
"type": "command",
26+
"command": "caliber learn observe --failure",
27+
"description": "Caliber: recording tool failure for session learning"
28+
}
29+
]
30+
}
31+
],
32+
"UserPromptSubmit": [
33+
{
34+
"matcher": "",
35+
"hooks": [
36+
{
37+
"type": "command",
38+
"command": "caliber learn observe --prompt",
39+
"description": "Caliber: recording user prompt for correction detection"
40+
}
41+
]
42+
}
43+
],
44+
"SessionEnd": [
45+
{
46+
"matcher": "",
47+
"hooks": [
48+
{
49+
"type": "command",
50+
"command": "caliber learn finalize --auto",
51+
"description": "Caliber: finalizing session learnings"
52+
}
53+
]
54+
},
55+
{
56+
"matcher": "",
57+
"hooks": [
58+
{
59+
"type": "command",
60+
"command": "caliber refresh --quiet",
61+
"description": "Caliber: auto-refreshing docs based on code changes"
62+
}
63+
]
64+
}
65+
]
66+
}
67+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
---
2+
name: add-event-handler
3+
description: Adds a new GitHub event type case to the switch block in `web/github.php`. Handles extracting fields from `$Message`, building a `$ChatMsg` string with markdown links, setting `$Msg['text']`, and calling `SendToChat('notifications', $Msg, $useRC, $useTeams)`. Use when user says 'add support for X event', 'handle X webhook', 'new event type', or adds a case to `web/github.php`. Do NOT use for modifying existing event handlers.
4+
---
5+
# add-event-handler
6+
7+
## Critical
8+
9+
- **Never** remove the `break` at the end of a case — fall-through will corrupt routing.
10+
- `$Msg['text']` MUST be set before calling `SendToChat`. Teams POSTs only `$Payload['text']`; missing it sends `null`.
11+
- Add new cases **before** `default:` — the default catch-all must remain last.
12+
- `$useRC` and `$useTeams` are already `true` at switch entry. Only override them if the event or repo should skip a target (see `issues` case for the per-repo pattern).
13+
- Do NOT re-declare `$RepositoryName`, `$EventType`, `$Message`, `$User`, or `$Msg` — they are set before the switch.
14+
15+
## Instructions
16+
17+
1. **Look up the GitHub payload schema** for the target event at `https://docs.github.com/en/webhooks/webhook-events-and-payloads#<event_name>`. Note which top-level keys carry the entity (e.g. `pull_request`, `issue`, `release`) and which carry `action`, `html_url`, `title`/`name`.
18+
Verify: you know the exact key names before writing any `$Message['...']` access.
19+
20+
2. **Add the case block** inside the `switch ($EventType)` block in `web/github.php`, immediately before `default:` (line ~265):
21+
22+
```php
23+
case 'your_event':
24+
$Entity = $Message['your_event']; // top-level entity key
25+
$Action = $Message['action'] ?? '';
26+
$Url = $Entity['html_url'] ?? '';
27+
$Title = $Entity['title'] ?? $Entity['name'] ?? '';
28+
29+
$ChatMsg = "🔔 [{$User}](https://github.com/{$User}) **{$Action}** "
30+
. "[{$Title}]({$Url}) "
31+
. "in [{$RepositoryName}](https://github.com/{$RepositoryName}).";
32+
33+
// Optional: append body preview (max 500 chars)
34+
if (!empty($Entity['body'])) {
35+
$ChatMsg .= "\n\n> " . substr($Entity['body'], 0, 500)
36+
. (strlen($Entity['body']) > 500 ? "…" : "");
37+
}
38+
39+
$Msg['text'] = $ChatMsg;
40+
SendToChat('notifications', $Msg, $useRC, $useTeams);
41+
break;
42+
```
43+
44+
This step uses `$User`, `$RepositoryName`, `$Msg` set before the switch.
45+
46+
3. **Choose an emoji** that fits the event. Reference from existing cases:
47+
- Push / release: `📦`
48+
- Issue / bug: `🐛`
49+
- Pull request: `🔀`
50+
- Wiki (gollum): `📝`
51+
- CI check pass/fail: `` / `` / ``
52+
- Generic/info: `ℹ️`
53+
54+
4. **Suppress noisy CI-style events** by commenting out `SendToChat` and leaving `$Msg['text']` set (matches `check_suite`/`workflow_run` pattern):
55+
```php
56+
$Msg['text'] = $ChatMsg;
57+
//SendToChat('notifications', $Msg, $useRC, $useTeams);
58+
break;
59+
```
60+
61+
5. **Suppress for specific repos** using the `issues` case pattern:
62+
```php
63+
case 'your_event':
64+
if (in_array($RepositoryName, ['owner/repo1', 'owner/repo2'])) {
65+
$useTeams = false;
66+
break;
67+
}
68+
// ... rest of handler
69+
```
70+
71+
6. **Add a test fixture** under `tests/events/<event_name>/`:
72+
- `payload.json` — raw GitHub webhook payload (copy from GitHub delivery logs or docs)
73+
- `type.txt` — event name only, e.g. `your_event` (no trailing newline needed; `trim()` is applied)
74+
- Note: `expected.bin` / `discord.json` are consumed by `IrcConverter`/`DiscordConverter` tests, not by `web/github.php` — add them only if those converters also need updating.
75+
76+
7. **Run quality checks** to verify no syntax errors or style violations:
77+
```bash
78+
vendor/bin/phpstan analyse
79+
vendor/bin/php-cs-fixer fix --dry-run
80+
```
81+
Fix any reported issues before committing.
82+
83+
## Examples
84+
85+
**User says:** "Add support for the `milestone` webhook event"
86+
87+
**Actions taken:**
88+
1. GitHub docs show payload has `$Message['milestone']` with keys `html_url`, `title`, `description`, and `$Message['action']`.
89+
2. Insert before `default:`:
90+
```php
91+
case 'milestone':
92+
$Milestone = $Message['milestone'];
93+
$Action = $Message['action'] ?? '';
94+
$Url = $Milestone['html_url'] ?? '';
95+
$Title = $Milestone['title'] ?? '';
96+
97+
$ChatMsg = "🏁 [{$User}](https://github.com/{$User}) **{$Action}** "
98+
. "milestone [{$Title}]({$Url}) "
99+
. "in [{$RepositoryName}](https://github.com/{$RepositoryName}).";
100+
101+
if (!empty($Milestone['description'])) {
102+
$ChatMsg .= "\n\n> " . substr($Milestone['description'], 0, 500)
103+
. (strlen($Milestone['description']) > 500 ? "…" : "");
104+
}
105+
106+
$Msg['text'] = $ChatMsg;
107+
SendToChat('notifications', $Msg, $useRC, $useTeams);
108+
break;
109+
```
110+
3. Created `tests/events/milestone/payload.json` and `tests/events/milestone/type.txt` containing `milestone`.
111+
4. Ran `vendor/bin/phpstan analyse` — no errors.
112+
113+
**Result:** `milestone` events POST to Rocket Chat and Teams with action, linked title, and optional description preview.
114+
115+
## Common Issues
116+
117+
- **`Undefined array key 'your_event'`** at `$Entity = $Message['your_event']`: The payload root key name differs from the event name (e.g. `deployment_status` event uses `$Message['deployment_status']` but `check_run` also appears nested). Dump `$Message` via the log file in `log/` and check the actual keys.
118+
119+
- **Teams receives `null` body**: `$Msg['text']` was not set before `SendToChat`. `SendToChat` sends `$Payload['text']` to Teams — ensure `$Msg['text'] = $ChatMsg` is present.
120+
121+
- **`php-cs-fixer` reports indentation errors**: The switch body uses a **tab** for the `case` line and **8 spaces** for the body (mixed in the file). Match the surrounding case's indentation exactly — do not auto-convert.
122+
123+
- **Event still hits `default:`**: The `$EventType` string comes from the `X-GitHub-Event` header. Verify your `case` string matches exactly (lowercase, underscores) — e.g. `'pull_request'` not `'pullRequest'`.
124+
125+
- **`SendToChat` returns `false` silently**: The channel key `'notifications'` must exist in both `$chatChannels['rocketchat']` and `$chatChannels['teams']` in `src/config.php`. If the URL is missing, curl will fail with HTTP 0.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
---
2+
name: add-test-fixture
3+
description: Creates a new test event fixture directory under tests/events/{event_name}/ with payload.json, type.txt, expected.bin, and discord.json. Use when user says 'add test for X event', 'create fixture', 'test event payload', or needs to add files to tests/events/. Do NOT use for modifying existing fixtures or writing unit tests that don't involve GitHub webhook payloads.
4+
---
5+
# Add Test Fixture
6+
7+
## Critical
8+
9+
- Every fixture directory requires **all four files**`EventTest.php:64` calls `file_get_contents` on all four unconditionally; missing any will cause a fatal test error:
10+
```
11+
tests/events/deployment/type.txt
12+
tests/events/deployment/payload.json
13+
tests/events/deployment/expected.bin
14+
tests/events/deployment/discord.json
15+
```
16+
- `expected.bin` contains IRC color codes (non-printable bytes) — **never hand-write it**. Use the generation workflow in Step 4.
17+
- The value in `tests/events/deployment/type.txt` must match the `X-GitHub-Event` header value exactly (e.g. `push`, `pull_request`, `ping`) — no trailing newline issues; `EventTest.php:61` trims it.
18+
19+
## Instructions
20+
21+
1. **Create the fixture directory.**
22+
```bash
23+
mkdir tests/events/deployment
24+
```
25+
Use the GitHub event name as the directory name (e.g. `deployment`, `workflow_dispatch`). For action variants, append `_{action}` (e.g. `pull_request_merged`, `issue_closed`). Verify no existing directory with that name exists under `tests/events/`.
26+
27+
2. **Write the event type file.**
28+
```bash
29+
echo -n 'deployment' > tests/events/deployment/type.txt
30+
```
31+
Content is the bare event type only — no action suffix, no newline required (the test trims it). Matches the value GitHub sends in the `X-GitHub-Event` HTTP header.
32+
33+
3. **Write the payload file.**
34+
Save a real GitHub webhook payload to `tests/events/deployment/payload.json`. Mirror the formatting of existing fixtures — tabs for indentation, escaped forward-slashes (`\/`), `JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES` style:
35+
```json
36+
{
37+
"action": "created",
38+
"sender": {
39+
"login": "octocat",
40+
"id": 1,
41+
"avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/1?v=3"
42+
},
43+
"repository": {
44+
"id": 1296269,
45+
"name": "Hello-World",
46+
"full_name": "octocat\/Hello-World",
47+
"html_url": "https:\/\/github.com\/octocat\/Hello-World",
48+
"private": false
49+
}
50+
}
51+
```
52+
Verify `tests/events/deployment/payload.json` is valid JSON:
53+
```bash
54+
php -r "json_decode(file_get_contents('tests/events/deployment/payload.json')); echo json_last_error();"
55+
```
56+
Must print `0`.
57+
58+
4. **Generate the binary and Discord files using the test harness.**
59+
In `tests/EventTest.php`, uncomment the two generation lines (lines 27 and 38):
60+
```php
61+
file_put_contents( $Path . '/expected.bin', $Message . "\n" );
62+
// and
63+
file_put_contents( $Path . '/discord.json', json_encode( $Discord, JSON_PRETTY_PRINT ) . "\n" );
64+
```
65+
Run only the new fixture:
66+
```bash
67+
vendor/bin/phpunit --filter testEvent --group default tests/EventTest.php
68+
```
69+
This writes the real IRC-formatted output (with color codes) to `tests/events/deployment/expected.bin` and the Discord embed to `tests/events/deployment/discord.json`. **Re-comment both lines immediately after**; leaving them uncommented will overwrite all fixtures on every test run.
70+
71+
5. **Verify the fixture passes.**
72+
```bash
73+
vendor/bin/phpunit tests/EventTest.php
74+
```
75+
All tests must pass. If the new fixture is listed as a failure, inspect the generated `tests/events/deployment/expected.bin` content and compare against `web/github.php`'s handler for that event type.
76+
77+
## Examples
78+
79+
**User says:** "Add a test fixture for the `deployment` event"
80+
81+
**Actions taken:**
82+
1. Create `tests/events/deployment/`
83+
2. Write `tests/events/deployment/type.txt``deployment`
84+
3. Write `tests/events/deployment/payload.json` with a real GitHub deployment payload
85+
4. Uncomment lines 27 & 38 in `tests/EventTest.php`, run `vendor/bin/phpunit tests/EventTest.php`, re-comment
86+
5. Verify `tests/events/deployment/expected.bin` and `tests/events/deployment/discord.json` were written and tests pass
87+
88+
**Result:** `tests/events/deployment/` contains all four files; `vendor/bin/phpunit tests/EventTest.php` exits green.
89+
90+
## Common Issues
91+
92+
- **`file_get_contents(...): Failed to open stream`** — one of the four required files is missing. Check `tests/events/deployment/` contains `tests/events/deployment/type.txt`, `tests/events/deployment/payload.json`, `tests/events/deployment/expected.bin`, `tests/events/deployment/discord.json`.
93+
- **`assertEquals failed: expected '' got '[10GitHub-WebHook]...'`**`tests/events/deployment/expected.bin` is empty or was hand-written without IRC codes. Re-run Step 4 generation.
94+
- **`json_decode` returns null for `discord.json`**`tests/events/deployment/discord.json` was hand-written with invalid JSON. Re-run Step 4 or validate:
95+
```bash
96+
php -r "var_dump(json_decode(file_get_contents('tests/events/deployment/discord.json')));"
97+
```
98+
Must not print `NULL`.
99+
- **New fixture not picked up by test runner** — confirm the directory is directly under `tests/events/` (not nested). `EventTest.php:51` uses a single-level `DirectoryIterator`.
100+
- **`GetEventType()` assertion fails**`tests/events/deployment/type.txt` contains extra whitespace or wrong casing. The value must exactly match the GitHub `X-GitHub-Event` header (all lowercase, underscores, e.g. `pull_request` not `Pull-Request`).
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
name: analyse-with-phpstan
3+
description: Analyse PHP code with PHPStan via the playground API. Tests across all PHP versions (7.2–8.5) and reports errors grouped by version. Supports configuring level, strict rules, and bleeding edge.
4+
argument-hint: <php-code-or-file>
5+
disable-model-invocation: false
6+
---
7+
8+
# Analyse PHP code with PHPStan
9+
10+
Analyse PHP code using the PHPStan playground API at `https://api.phpstan.org/analyse`. This runs PHPStan across PHP versions 7.2–8.5 and returns errors for each version.
11+
12+
The code to analyse: `$ARGUMENTS`
13+
14+
## Step 1: Prepare the code
15+
16+
Get the PHP code to analyse. If `$ARGUMENTS` is a file path, read the file contents. The code must start with `<?php`.
17+
18+
## Step 2: Determine settings
19+
20+
Unless the user specified otherwise, use these defaults:
21+
- **level**: `"10"` (strictest)
22+
- **strictRules**: `false`
23+
- **bleedingEdge**: `false`
24+
- **treatPhpDocTypesAsCertain**: `true`
25+
26+
If the user asked for strict rules or bleeding edge, set those to `true`.
27+
28+
## Step 3: Call the playground API
29+
30+
Submit the code via POST:
31+
32+
```bash
33+
curl -s -X POST 'https://api.phpstan.org/analyse' \
34+
-H 'Content-Type: application/json' \
35+
-d '{
36+
"code": "<PHP code, JSON-escaped>",
37+
"level": "<level>",
38+
"strictRules": <true|false>,
39+
"bleedingEdge": <true|false>,
40+
"treatPhpDocTypesAsCertain": <true|false>,
41+
"saveResult": true
42+
}'
43+
```
44+
45+
The code value must be properly JSON-escaped (escape quotes, backslashes, newlines).
46+
47+
## Step 4: Parse the response
48+
49+
The response JSON contains:
50+
- `versionedErrors` — array of objects, one per PHP version, each with:
51+
- `phpVersion` — integer encoding: e.g. `80400` = PHP 8.4, `70400` = PHP 7.4
52+
- `errors` — array of error objects with `message`, `line`, `identifier`, `tip` (optional), `ignorable`
53+
- `id` — UUID for the saved result
54+
55+
Convert `phpVersion` integers to readable strings: `Math.floor(v / 10000)` `.` `Math.floor((v % 10000) / 100)`.
56+
57+
## Step 5: Present results as markdown
58+
59+
Output the results in this format:
60+
61+
### Playground link
62+
63+
`https://phpstan.org/r/<id>`
64+
65+
### Settings used
66+
67+
**Level:** `<level>` | **Strict rules:** yes/no | **Bleeding edge:** yes/no
68+
69+
### Errors
70+
71+
Group consecutive PHP versions that have identical errors (same messages, lines, and identifiers) into ranges. For example, if PHP 7.2–8.3 all report the same errors, show them as one group.
72+
73+
If all PHP versions report identical errors, show a single group:
74+
75+
**All PHP versions (no differences):**
76+
77+
| Line | Error | Identifier |
78+
|------|-------|------------|
79+
| 10 | `Parameter #1 $foo expects string, int given.` | `argument.type` |
80+
81+
If errors differ across versions, show separate groups:
82+
83+
**PHP 8.0 – 8.5:**
84+
85+
| Line | Error | Identifier |
86+
|------|-------|------------|
87+
| 10 | `Parameter #1 $foo expects string, int given.` | `argument.type` |
88+
89+
**PHP 7.2 – 7.4:**
90+
91+
No errors.
92+
93+
If there are no errors on any PHP version, say: **No errors found on any PHP version.**

0 commit comments

Comments
 (0)