Skip to content

Commit 10af074

Browse files
committed
Migrate to the native toolchain
1 parent 5b0fc3f commit 10af074

48 files changed

Lines changed: 4061 additions & 8845 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/rules/matterbridge/matterbridge.instructions.md

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
description: 'How to create MatterbridgeEndpoint instances, register them in Matterbridge plugins, and use the single-class devices exported by the package v. 1.0.0'
2+
description: 'How to create MatterbridgeEndpoint instances, register them in Matterbridge plugins, and use the single-class devices exported by the package v. 1.0.1'
33
---
44

55
# Matterbridge Endpoint Guide
@@ -49,24 +49,18 @@ Recommended pattern:
4949
```ts
5050
const device = new MatterbridgeEndpoint([contactSensor, powerSource], { id: 'EntryDoor' })
5151
.createDefaultIdentifyClusterServer()
52-
.createDefaultBridgedDeviceBasicInformationClusterServer(
53-
'Entry Door',
54-
'ENTRY-DOOR-001',
55-
0xfff1,
56-
'Matterbridge',
57-
'Entry Door Sensor',
58-
)
52+
.createDefaultBridgedDeviceBasicInformationClusterServer('Entry Door', 'ENTRY-DOOR-001', 0xfff1, 'Matterbridge', 'Entry Door Sensor')
5953
.createDefaultBooleanStateClusterServer(false)
6054
.createDefaultPowerSourceReplaceableBatteryClusterServer(75)
61-
.addRequiredClusterServers();
55+
.addRequiredClusters();
6256
```
6357

6458
Rules that matter:
6559

6660
- `definition` can be a single device type or an array of device types.
6761
- Use multiple device types when the endpoint needs more than one role, for example `[contactSensor, powerSource]`.
6862
- Call one of the Basic Information helpers before `registerDevice()`. Without `deviceName`, `serialNumber`, and `uniqueId`, registration fails.
69-
- Call `addRequiredClusterServers()` at the end of the chain so any required clusters that you did not explicitly create are added automatically.
63+
- Call `addRequiredClusters()` at the end of the chain so any required clusters (server or client) that you did not explicitly create are added automatically.
7064
- Use `addOptionalClusterServers()` only when you really want the optional clusters defined by the selected device type(s).
7165

7266
## MatterbridgeEndpointOptions
@@ -116,7 +110,7 @@ Important behavior:
116110

117111
In plugin code, prefer `this.registerDevice(device)` instead of calling Matterbridge internals directly.
118112

119-
DynamicPlatform bridged endpoint:
113+
DynamicPlatform bridged device:
120114

121115
```ts
122116
import { MatterbridgeDynamicPlatform, MatterbridgeEndpoint, onOffLight } from 'matterbridge';
@@ -130,13 +124,7 @@ class ExamplePlatform extends MatterbridgeDynamicPlatform {
130124
await this.ready;
131125

132126
const device = new MatterbridgeEndpoint(onOffLight, { id: 'OnOffLightPlugin' })
133-
.createDefaultBridgedDeviceBasicInformationClusterServer(
134-
'Kitchen Light',
135-
'LIGHT-001',
136-
0xfff1,
137-
'Matterbridge',
138-
'Matterbridge OnOffLight',
139-
)
127+
.createDefaultBridgedDeviceBasicInformationClusterServer('Kitchen Light', 'LIGHT-001', 0xfff1, 'Matterbridge', 'Matterbridge OnOffLight')
140128
.addRequiredClusterServers();
141129

142130
await this.registerDevice(device);
@@ -158,14 +146,7 @@ class ExamplePlatform extends MatterbridgeAccessoryPlatform {
158146
await this.ready;
159147

160148
const device = new MatterbridgeEndpoint(temperatureSensor, { id: 'TemperatureSensorPlugin' })
161-
.createDefaultBasicInformationClusterServer(
162-
'Temperature Sensor',
163-
'TEMP-001',
164-
0xfff1,
165-
'Matterbridge',
166-
0x8000,
167-
'Matterbridge Temperature Sensor',
168-
)
149+
.createDefaultBasicInformationClusterServer('Temperature Sensor', 'TEMP-001', 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Temperature Sensor')
169150
.addRequiredClusterServers();
170151

171152
await this.registerDevice(device);
@@ -177,14 +158,7 @@ Standalone Matter device from a plugin:
177158

178159
```ts
179160
const device = new MatterbridgeEndpoint(pressureSensor, { id: 'ServerNodeDevice', mode: 'server' })
180-
.createDefaultBasicInformationClusterServer(
181-
'Server Node Device',
182-
'SERVER-001',
183-
0xfff1,
184-
'Matterbridge',
185-
0x8000,
186-
'Matterbridge Server Node Device',
187-
)
161+
.createDefaultBasicInformationClusterServer('Server Node Device', 'SERVER-001', 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Server Node Device')
188162
.addRequiredClusterServers();
189163

190164
await this.registerDevice(device);
@@ -194,14 +168,7 @@ Native Matter endpoint on the server node:
194168

195169
```ts
196170
const device = new MatterbridgeEndpoint(pressureSensor, { id: 'MatterNodeDevice', mode: 'matter' })
197-
.createDefaultBasicInformationClusterServer(
198-
'Matter Node Device',
199-
'MATTER-001',
200-
0xfff1,
201-
'Matterbridge',
202-
0x8000,
203-
'Matterbridge Matter Node Device',
204-
)
171+
.createDefaultBasicInformationClusterServer('Matter Node Device', 'MATTER-001', 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Matter Node Device')
205172
.addRequiredClusterServers();
206173

207174
await this.registerDevice(device);
@@ -227,6 +194,7 @@ Common helpers on the endpoint instance:
227194
- `subscribeAttribute(cluster, attribute, listener)`
228195
- `addRequiredClusterServers()`
229196
- `addOptionalClusterServers()`
197+
- `addRequiredClusters()`
230198

231199
Example:
232200

@@ -241,6 +209,8 @@ Cluster references can be passed in several ways:
241209
- cluster id
242210
- cluster name string such as `'OnOff'`
243211

212+
Behavior type and cluster type are preferred because they are type-safe and avoid typos.
213+
244214
Using the cluster name string is useful in plugins because it avoids importing every cluster type.
245215

246216
## When to use a raw endpoint vs a single-class device

.claude/rules/testing/unit-tests.instructions.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@ paths:
55
- '**/src/**/*.spec.ts'
66
- '**/test/**/*.ts'
77
- '**/vitest/**/*.ts'
8+
- '**/buntest/**/*.ts'
89
---
910

1011
# Testing Standards for Unit Tests
1112

1213
## 1. Test Framework
1314

14-
- Use Jest (with ts-jest) or Vitest as the testing framework for all unit tests.
1515
- Jest is available in the repository when the file `jest.config.js` exists.
1616
- Vitest is available in the repository when the file `vite.config.ts` exists.
17-
- Avoid using both Jest and Vitest in the same project to prevent conflicts. When both are available, prefer Vitest for new tests and consider migrating existing Jest tests to Vitest over time.
18-
- Jest tests live adjacent to the code being tested with a `.test.ts` suffix or in `test` folders. Follow the existing convention in the repository for test file placement.
19-
- Vitest tests live adjacent to the code being tested with a `.test.ts` suffix or in `vitest` folders. Follow the existing convention in the repository for test file placement.
17+
- Bun test is available in the repository when the file `bunfig.toml` exists.
18+
- Jest tests live in `test` folders. Follow the existing convention in the repository for test file placement.
19+
- Vitest tests live in `vitest` folders. Follow the existing convention in the repository for test file placement.
20+
- Bun test tests live in `buntest` folders. Follow the existing convention in the repository for test file placement.
2021
- Ensure that tests are written in TypeScript and follow the ESM module format.
2122

2223
## 2. Test Structure
2324

24-
- Organize tests in file name `*.test.ts` adjacent to the code being tested.
25+
- Organize tests in file name `*.test.ts` in the `test`, `vitest`, or `buntest` folders.
2526
- Use `describe` blocks to group related tests and `test` blocks for individual test cases.
2627

2728
## 3. Test Naming
@@ -47,12 +48,13 @@ paths:
4748
- Run the relevant full test unit from start to finish rather than assuming isolated single-test execution is reliable.
4849
- If only one test framework is installed, use `npm run test -- yourTest.test.ts` or `npm run test:coverage -- yourTest.test.ts` when the touched area can be validated by running the full relevant test file.
4950
- When both Jest and Vitest are installed, use `npm run test -- yourTest.test.ts` for Jest or `npm run test:vitest -- yourTest.test.ts` for Vitest.
51+
- When Bun test is installed, use `bun test yourTest.test.ts`.
5052
- Use the existing `tasks.json` test tasks for areas that require grouped test files, custom coverage targets, or custom ignore-pattern handling.
5153
- Avoid running all tests unnecessarily to save time and tokens.
5254

5355
## 8. Test Assertions
5456

55-
- Use appropriate Jest or Vitest matchers for assertions (e.g., `toBe`, `toEqual`, `toThrow`).
57+
- Use appropriate Jest, Vitest, or Bun test matchers for assertions (e.g., `toBe`, `toEqual`, `toThrow`).
5658
- Ensure that assertions are clear and directly related to the behavior being tested.
5759

5860
## 9. Performance

.claude/settings.json

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{
22
"$schema": "https://json.schemastore.org/claude-code-settings.json",
3+
"_comment": "Claude settings v. 1.0.3",
34
"permissions": {
45
"deny": [
5-
"Read(./.env)",
6-
"Read(./.env.*)",
7-
"Read(./**/secrets/**)",
6+
"Read(.env)",
7+
"Read(.env.*)",
8+
"Read(**/.env)",
9+
"Read(**/.env.*)",
10+
"Read(**/secrets/**)",
811
"Bash(git pull:*)",
912
"Bash(git merge:*)",
1013
"Bash(git push:*)",
@@ -16,12 +19,36 @@
1619
"Bash(git branch -d:*)",
1720
"Bash(git tag -d:*)",
1821
"Bash(git reflog expire:*)",
19-
"Bash(git reflog delete:*)",
20-
"Bash(git push --delete:*)",
21-
"Bash(git push --force:*)",
22-
"Bash(git push -f:*)"
22+
"Bash(git reflog delete:*)"
2323
],
24-
"ask": ["Bash(git commit:*)", "Bash(npm install:*)", "Bash(npm i:*)"],
25-
"allow": ["Bash(npm run typecheck)", "Bash(npm run build)", "Bash(npm run test:*)", "Bash(npm run lint)", "Bash(npm run format)"]
24+
"ask": [
25+
"Bash(git commit:*)",
26+
"Bash(npm install:*)",
27+
"Bash(npm i:*)",
28+
"Bash(npm uninstall:*)",
29+
"Bash(bun install:*)",
30+
"Bash(bun i:*)",
31+
"Bash(bun uninstall:*)",
32+
"Bash(bun add:*)",
33+
"Bash(bun remove:*)"
34+
],
35+
"allow": [
36+
"Bash(npm run typecheck)",
37+
"Bash(npm run build)",
38+
"Bash(npm run test:*)",
39+
"Bash(npm run lint)",
40+
"Bash(npm run format)",
41+
"Bash(bun test)",
42+
"Bash(npx vitest:*)",
43+
"Bash(vitest:*)",
44+
"Bash(npx oxfmt:*)",
45+
"Bash(oxfmt:*)",
46+
"Bash(npx oxlint:*)",
47+
"Bash(oxlint:*)",
48+
"Bash(npx tsc:*)",
49+
"Bash(tsc:*)",
50+
"Bash(npx tsgo:*)",
51+
"Bash(tsgo:*)"
52+
]
2653
}
2754
}

.codex/config.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Codex config v. 1.0.1
2+
3+
approval_policy = "on-request"
4+
approvals_reviewer = "user"
5+
default_permissions = "matterbridge_safe"
6+
web_search = "live"
7+
8+
[shell_environment_policy]
9+
inherit = "core"
10+
11+
[windows]
12+
sandbox = "elevated"
13+
14+
[permissions.matterbridge_safe.network]
15+
enabled = false
16+
17+
[permissions.matterbridge_safe]
18+
description = "Workspace access with sensitive files denied and network disabled."
19+
extends = ":workspace"
20+
21+
[permissions.matterbridge_safe.filesystem]
22+
glob_scan_max_depth = 4
23+
24+
[permissions.matterbridge_safe.filesystem.":workspace_roots"]
25+
".env" = "deny"
26+
".env.*" = "deny"
27+
"**/.env" = "deny"
28+
"**/.env.*" = "deny"
29+
"**/secrets/**" = "deny"

.codex/rules/default.rules

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Codex rules v. 1.0.1
2+
3+
# Git mutations
4+
prefix_rule(pattern = ["git", "pull"], decision = "forbidden", justification = "Do not change local refs without explicit manual control.")
5+
prefix_rule(pattern = ["git", "merge"], decision = "forbidden", justification = "Do not merge automatically.")
6+
prefix_rule(pattern = ["git", "push"], decision = "forbidden", justification = "Do not push automatically.")
7+
prefix_rule(pattern = ["git", "rebase"], decision = "forbidden", justification = "Do not rewrite branch history automatically.")
8+
prefix_rule(pattern = ["git", "commit"], decision = "forbidden", justification = "Do not create commits automatically.")
9+
prefix_rule(pattern = ["git", "commit", "--amend"], decision = "forbidden", justification = "Do not amend commits automatically.")
10+
prefix_rule(pattern = ["git", "reset", "--hard"], decision = "forbidden", justification = "Destructive reset is blocked.")
11+
prefix_rule(pattern = ["git", "filter-branch"], decision = "forbidden", justification = "History rewriting is blocked.")
12+
prefix_rule(pattern = ["git", "branch", "-D"], decision = "forbidden", justification = "Branch deletion is blocked.")
13+
prefix_rule(pattern = ["git", "branch", "-d"], decision = "forbidden", justification = "Branch deletion is blocked.")
14+
prefix_rule(pattern = ["git", "tag", "-d"], decision = "forbidden", justification = "Tag deletion is blocked.")
15+
prefix_rule(pattern = ["git", "reflog", "expire"], decision = "forbidden", justification = "Reflog mutation is blocked.")
16+
prefix_rule(pattern = ["git", "reflog", "delete"], decision = "forbidden", justification = "Reflog mutation is blocked.")
17+
18+
# Dependency installs
19+
prefix_rule(pattern = ["npm", "install"], decision = "prompt", justification = "Ask before installing dependencies.")
20+
prefix_rule(pattern = ["npm", "i"], decision = "prompt", justification = "Ask before installing dependencies.")
21+
prefix_rule(pattern = ["npm", "uninstall"], decision = "prompt", justification = "Ask before uninstalling dependencies.")
22+
prefix_rule(pattern = ["bun", "install"], decision = "prompt", justification = "Ask before installing dependencies.")
23+
prefix_rule(pattern = ["bun", "i"], decision = "prompt", justification = "Ask before installing dependencies.")
24+
prefix_rule(pattern = ["bun", "uninstall"], decision = "prompt", justification = "Ask before uninstalling dependencies.")
25+
prefix_rule(pattern = ["bun", "add"], decision = "prompt", justification = "Ask before installing dependencies.")
26+
prefix_rule(pattern = ["bun", "remove"], decision = "prompt", justification = "Ask before uninstalling dependencies.")
27+
28+
# Validation scripts
29+
prefix_rule(pattern = ["npm", "run", "typecheck"], decision = "allow")
30+
prefix_rule(pattern = ["npm", "run", "build"], decision = "allow")
31+
prefix_rule(pattern = ["npm", "run", "test"], decision = "allow")
32+
prefix_rule(pattern = ["npm", "run", "test:watch"], decision = "allow")
33+
prefix_rule(pattern = ["npm", "run", "test:verbose"], decision = "allow")
34+
prefix_rule(pattern = ["npm", "run", "test:coverage"], decision = "allow")
35+
prefix_rule(pattern = ["npm", "run", "lint"], decision = "allow")
36+
prefix_rule(pattern = ["npm", "run", "format"], decision = "allow")
37+
prefix_rule(pattern = ["bun", "test"], decision = "allow")
38+
39+
# External network and containers
40+
prefix_rule(pattern = ["curl"], decision = "prompt", justification = "Ask before external network fetches.")
41+
prefix_rule(pattern = ["Invoke-WebRequest"], decision = "prompt", justification = "Ask before external network fetches.")
42+
prefix_rule(pattern = ["docker", "pull"], decision = "prompt", justification = "Ask before downloading Docker images.")
43+
prefix_rule(pattern = ["docker", "run"], decision = "prompt", justification = "Ask before running Docker containers.")

.devcontainer/devcontainer.json

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// .devcontainer/devcontainer.json v. 1.0.2
1+
// .devcontainer/devcontainer.json v. 1.0.4
22

33
// This file defines the development container configuration for a Matterbridge Plugin.
44

@@ -47,7 +47,9 @@ Dev containers have networking limitations depending on the host OS and Docker s
4747
"source=${localWorkspaceFolderBasename}-plugins,target=/home/node/Matterbridge,type=volume",
4848
"source=${localWorkspaceFolderBasename}-storage,target=/home/node/.matterbridge,type=volume",
4949
"source=${localWorkspaceFolderBasename}-cert,target=/home/node/.mattercert,type=volume",
50-
"source=matterbridge,target=/matterbridge,type=volume"
50+
"source=matterbridge,target=/matterbridge,type=volume",
51+
"source=claude-code,target=/home/node/.claude,type=volume",
52+
"source=codex,target=/home/node/.codex,type=volume"
5153
],
5254
// Create the matterbridge Docker network if it doesn't exist, then pull the base image (runs on the HOST before the container starts).
5355
"initializeCommand": "(docker network inspect matterbridge || docker network create matterbridge) && docker pull mcr.microsoft.com/devcontainers/javascript-node:24-trixie",
@@ -72,41 +74,40 @@ Dev containers have networking limitations depending on the host OS and Docker s
7274
"extensions": [
7375
"typescriptteam.native-preview",
7476
"oxc.oxc-vscode",
77+
"vitest.explorer",
78+
"firsttris.vscode-jest-runner",
7579
"ms-azuretools.vscode-containers",
76-
"dbaeumer.vscode-eslint",
77-
"esbenp.prettier-vscode",
7880
"anthropic.claude-code",
7981
"github.copilot-chat",
8082
"github.vscode-github-actions",
81-
"github.vscode-pull-request-github"
83+
"github.vscode-pull-request-github",
84+
"openai.chatgpt"
8285
],
8386
// Settings for the VS Code environment
8487
"settings": {
8588
// Default .vscode/settings.json
86-
"eslint.enable": true,
87-
"prettier.enable": true,
88-
"oxc.enable": false,
89+
"eslint.enable": false,
90+
"prettier.enable": false,
91+
"oxc.enable": true,
8992
"js/ts.experimental.useTsgo": true,
9093
"js/ts.tsdk.promptToUseWorkspaceVersion": false,
94+
"jestrunner.enableESM": true,
95+
"jestrunner.codeLens": ["run", "watch", "coverage", "debug"],
9196
"terminal.integrated.scrollback": 10000,
9297
"editor.tabSize": 2,
9398
"editor.rulers": [180],
94-
"editor.formatOnSave": true,
95-
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
96-
"[javascript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" },
97-
"[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" },
98-
"[json]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" },
99-
"[jsonc]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" },
100-
"[markdown]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" },
99+
"editor.formatOnSave": false,
100+
"editor.defaultFormatter": "oxc.oxc-vscode",
101+
"[javascript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
102+
"[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
103+
"[json]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
104+
"[jsonc]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
105+
"[markdown]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
106+
"editor.formatOnSaveMode": "file",
101107
"editor.codeActionsOnSave": {
102-
"source.fixAll.eslint": "explicit"
108+
"source.format.oxc": "always",
109+
"source.fixAll.oxc": "always"
103110
},
104-
"eslint.format.enable": true,
105-
"eslint.options": {
106-
"cache": true,
107-
"cacheLocation": ".cache/.eslintcache"
108-
},
109-
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "json", "jsonc", "markdown"],
110111
"diffEditor.ignoreTrimWhitespace": false,
111112
"files.eol": "\n",
112113
"files.insertFinalNewline": true,
@@ -126,6 +127,7 @@ Dev containers have networking limitations depending on the host OS and Docker s
126127
},
127128
"git.autofetch": true,
128129
"git.autoRepositoryDetection": false,
130+
// VS Code's Copilot Chat extension settings
129131
"chat.useCustomizationsInParentRepositories": false,
130132
"chat.useClaudeMdFile": true,
131133
"chat.instructionsFilesLocations": {

0 commit comments

Comments
 (0)