@polderlabs/bizar 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -0
  3. package/cli/audit.mjs +144 -0
  4. package/cli/banner.mjs +41 -0
  5. package/cli/bin.mjs +186 -0
  6. package/cli/copy.mjs +508 -0
  7. package/cli/export.mjs +87 -0
  8. package/cli/init.mjs +147 -0
  9. package/cli/install.mjs +390 -0
  10. package/cli/plan-templates.mjs +523 -0
  11. package/cli/plan.mjs +2087 -0
  12. package/cli/prompts.mjs +163 -0
  13. package/cli/update.mjs +273 -0
  14. package/cli/utils.mjs +153 -0
  15. package/config/AGENTS.md +282 -0
  16. package/config/agents/baldr.md +148 -0
  17. package/config/agents/forseti.md +112 -0
  18. package/config/agents/frigg.md +101 -0
  19. package/config/agents/heimdall.md +157 -0
  20. package/config/agents/hermod.md +144 -0
  21. package/config/agents/mimir.md +115 -0
  22. package/config/agents/odin.md +309 -0
  23. package/config/agents/quick.md +78 -0
  24. package/config/agents/semble-search.md +44 -0
  25. package/config/agents/thor.md +97 -0
  26. package/config/agents/tyr.md +96 -0
  27. package/config/agents/vidarr.md +100 -0
  28. package/config/agents/vor.md +140 -0
  29. package/config/commands/audit.md +1 -0
  30. package/config/commands/explain.md +1 -0
  31. package/config/commands/init.md +1 -0
  32. package/config/commands/learn.md +1 -0
  33. package/config/commands/pr-review.md +1 -0
  34. package/config/commands/tailscale-serve.md +96 -0
  35. package/config/hooks/README.md +29 -0
  36. package/config/hooks/post-tool-use.md +16 -0
  37. package/config/hooks/pre-tool-use.md +16 -0
  38. package/config/opencode.json +52 -0
  39. package/config/opencode.json.template +52 -0
  40. package/config/rules/general.md +8 -0
  41. package/config/rules/git.md +11 -0
  42. package/config/rules/javascript.md +10 -0
  43. package/config/rules/python.md +10 -0
  44. package/config/rules/testing.md +10 -0
  45. package/config/skills/bizar/README.md +9 -0
  46. package/config/skills/bizar/SKILL.md +187 -0
  47. package/config/skills/cpp-coding-standards/README.md +28 -0
  48. package/config/skills/cpp-coding-standards/SKILL.md +634 -0
  49. package/config/skills/cpp-coding-standards/agents/openai.yaml +4 -0
  50. package/config/skills/cpp-coding-standards/references/concurrency.md +320 -0
  51. package/config/skills/cpp-coding-standards/references/error-handling.md +229 -0
  52. package/config/skills/cpp-coding-standards/references/memory-safety.md +216 -0
  53. package/config/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
  54. package/config/skills/cpp-coding-standards/references/review-checklist.md +96 -0
  55. package/config/skills/cpp-testing/README.md +28 -0
  56. package/config/skills/cpp-testing/SKILL.md +304 -0
  57. package/config/skills/cpp-testing/agents/openai.yaml +4 -0
  58. package/config/skills/cpp-testing/references/coverage.md +370 -0
  59. package/config/skills/cpp-testing/references/framework-compare.md +175 -0
  60. package/config/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
  61. package/config/skills/cpp-testing/references/mocking.md +364 -0
  62. package/config/skills/cpp-testing/references/tdd-workflow.md +308 -0
  63. package/config/skills/embedded-esp-idf/README.md +41 -0
  64. package/config/skills/embedded-esp-idf/SKILL.md +439 -0
  65. package/config/skills/embedded-esp-idf/agents/openai.yaml +4 -0
  66. package/config/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
  67. package/config/skills/embedded-esp-idf/references/host-tests.md +164 -0
  68. package/config/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
  69. package/config/skills/embedded-esp-idf/references/kconfig.md +159 -0
  70. package/config/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
  71. package/config/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
  72. package/config/skills/embedded-esp-idf/references/nvs.md +121 -0
  73. package/config/skills/embedded-esp-idf/references/packed-structs.md +192 -0
  74. package/config/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
  75. package/config/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
  76. package/config/skills/self-improvement/SKILL.md +64 -0
  77. package/package.json +47 -0
  78. package/templates/plan/htmx.min.js +1 -0
  79. package/templates/plan/library/bug-investigation.mdx +79 -0
  80. package/templates/plan/library/decision-record.mdx +71 -0
  81. package/templates/plan/library/feature-design.mdx +92 -0
  82. package/templates/plan/meta.json.template +8 -0
  83. package/templates/plan/plan.canvas.template +1711 -0
  84. package/templates/plan/plan.html.template +937 -0
  85. package/templates/plan/plan.mdx.template +46 -0
@@ -0,0 +1,216 @@
1
+ # Memory Safety in Modern C++
2
+
3
+ This reference covers RAII, smart pointers, and the Rule of Five/Zero.
4
+
5
+ ## RAII — Resource Acquisition Is Initialization
6
+
7
+ Every resource (heap memory, file handle, mutex, socket, etc.) must be owned by an object whose destructor performs cleanup. This guarantees cleanup even when exceptions are thrown.
8
+
9
+ ```cpp
10
+ // GOOD — file closes automatically at scope exit
11
+ void read_file(const std::string& path) {
12
+ std::ifstream in(path);
13
+ std::string line;
14
+ while (std::getline(in, line)) {
15
+ process(line);
16
+ }
17
+ } // destructor closes file, no matter how we exit
18
+
19
+ // BAD — manual cleanup, error-prone
20
+ void read_file_bad(const std::string& path) {
21
+ std::ifstream* in = new std::ifstream(path);
22
+ std::string line;
23
+ while (std::getline(*in, line)) { // throws? leaks *in
24
+ process(line);
25
+ }
26
+ delete in;
27
+ }
28
+ ```
29
+
30
+ ### Common RAII Types in the Standard Library
31
+
32
+ | Type | Resource |
33
+ |---|---|
34
+ | `std::unique_ptr` | Heap memory (single owner) |
35
+ | `std::shared_ptr` | Heap memory (shared owner) |
36
+ | `std::ifstream` / `std::ofstream` | File handles |
37
+ | `std::lock_guard` / `std::scoped_lock` | Mutex lock |
38
+ | `std::jthread` (C++20) | Thread join on destruction |
39
+
40
+ ## Smart Pointers
41
+
42
+ ### `std::unique_ptr<T>`
43
+
44
+ Exclusive ownership. The default choice. Zero overhead over raw pointer.
45
+
46
+ ```cpp
47
+ #include <memory>
48
+
49
+ // Creation
50
+ auto p1 = std::make_unique<int>(42);
51
+ auto p2 = std::make_unique<std::vector<int>>(10, 0);
52
+
53
+ // Custom deleter (rare — only when type needs special cleanup)
54
+ auto file_deleter = [](FILE* f) { if (f) fclose(f); };
55
+ std::unique_ptr<FILE, decltype(file_deleter)> fp(fopen("data.txt", "r"), file_deleter);
56
+
57
+ // Transfer ownership
58
+ std::unique_ptr<int> source = std::make_unique<int>(1);
59
+ std::unique_ptr<int> dest = std::move(source); // source is now nullptr
60
+ assert(!source);
61
+ assert(*dest == 1);
62
+
63
+ // Release the raw pointer (rare — only for C interop)
64
+ int* raw = p1.release();
65
+ delete raw; // you now own it — avoid this
66
+ ```
67
+
68
+ ### `std::shared_ptr<T>`
69
+
70
+ Shared ownership via reference counting. Use **only** when ownership genuinely must be shared. Prefer `unique_ptr` otherwise.
71
+
72
+ ```cpp
73
+ auto shared = std::make_shared<int>(100);
74
+
75
+ // Copy increases ref count
76
+ auto shared2 = shared; // ref count = 2
77
+
78
+ // shared_ptr can be created from unique_ptr
79
+ auto unique = std::make_unique<double>(3.14);
80
+ std::shared_ptr<double> from_unique = std::move(unique);
81
+
82
+ // Weak pointer — breaks cycles
83
+ std::weak_ptr<int> weak = shared;
84
+ if (auto locked = weak.lock()) {
85
+ // use *locked safely
86
+ } // locked goes out of scope, ref count unchanged
87
+
88
+ // Custom deleter — stored separately from the control block
89
+ auto deleter = [](FILE* f) { fclose(f); };
90
+ std::shared_ptr<FILE> f(fopen("x.txt", "r"), deleter);
91
+ ```
92
+
93
+ **Performance notes:**
94
+ - `shared_ptr` is twice the size of a raw pointer (control block + pointer)
95
+ - Atomic reference count increments/decrements on copy/destroy
96
+ - `make_shared` allocates the object and control block in one allocation (preferred)
97
+
98
+ ### `std::weak_ptr<T>`
99
+
100
+ Non-owning observer of a `shared_ptr`. Use to break reference cycles (A owns B, B observes A without owning).
101
+
102
+ ```cpp
103
+ class Observer;
104
+ class Subject {
105
+ std::vector<std::weak_ptr<Observer>> observers_;
106
+ public:
107
+ void notify() {
108
+ for (auto& w : observers_) {
109
+ if (auto o = w.lock()) o->on_update();
110
+ }
111
+ }
112
+ };
113
+ ```
114
+
115
+ ## Rule of Five
116
+
117
+ If you define **any one** of the five special member functions below, you likely need to define **all five**:
118
+
119
+ 1. Destructor
120
+ 2. Copy constructor
121
+ 3. Copy assignment operator
122
+ 4. Move constructor
123
+ 5. Move assignment operator
124
+
125
+ ```cpp
126
+ class Buffer {
127
+ int* data_ = nullptr;
128
+ size_t size_ = 0;
129
+ public:
130
+ explicit Buffer(size_t n) : data_(new int[n]), size_(n) {}
131
+
132
+ ~Buffer() { delete[] data_; }
133
+
134
+ // Copy constructor
135
+ Buffer(const Buffer& o) : data_(new int[o.size_]), size_(o.size_) {
136
+ std::copy(o.data_, o.data_ + o.size_, data_);
137
+ }
138
+
139
+ // Copy assignment operator
140
+ Buffer& operator=(const Buffer& o) {
141
+ if (this != &o) {
142
+ delete[] data_;
143
+ data_ = new int[o.size_];
144
+ size_ = o.size_;
145
+ std::copy(o.data_, o.data_ + o.size_, data_);
146
+ }
147
+ return *this;
148
+ }
149
+
150
+ // Move constructor
151
+ Buffer(Buffer&& o) noexcept : data_(std::exchange(o.data_, nullptr)), size_(std::exchange(o.size_, 0)) {}
152
+
153
+ // Move assignment operator
154
+ Buffer& operator=(Buffer&& o) noexcept {
155
+ if (this != &o) {
156
+ delete[] data_;
157
+ data_ = std::exchange(o.data_, nullptr);
158
+ size_ = std::exchange(o.size_, 0);
159
+ }
160
+ return *this;
161
+ }
162
+ };
163
+ ```
164
+
165
+ ## Rule of Zero
166
+
167
+ Prefer **not** defining any special members and letting the compiler generate them correctly. Use standard library containers and smart pointers to manage resources.
168
+
169
+ ```cpp
170
+ // Rule of Zero — no manual resource management needed
171
+ class Point {
172
+ double x_ = 0, y_ = 0;
173
+ public:
174
+ Point() = default;
175
+ Point(double x, double y) : x_(x), y_(y) {}
176
+ double distance_to(const Point& o) const {
177
+ double dx = x_ - o.x_, dy = y_ - o.y_;
178
+ return std::sqrt(dx*dx + dy*dy);
179
+ }
180
+ };
181
+ // Compiler-generated copy/move/dtor are all correct here
182
+ ```
183
+
184
+ ## Common Mistakes
185
+
186
+ ```cpp
187
+ // WRONG: Shared ownership where unique ownership suffices
188
+ auto widget = std::make_shared<Widget>();
189
+ process(std::shared_ptr<Widget>(widget)); // unnecessary ref count bump
190
+
191
+ // WRONG: Memory leak via raw new in container
192
+ std::vector<int*> v;
193
+ v.push_back(new int(1)); // who deletes these?
194
+
195
+ // RIGHT: Smart pointers in containers
196
+ std::vector<std::unique_ptr<int>> v;
197
+ v.push_back(std::make_unique<int>(1)); // auto-deleted
198
+
199
+ // WRONG: Missing virtual destructor in base class
200
+ struct Base {
201
+ virtual void foo() {}
202
+ // Missing virtual destructor — deleting through base pointer is UB
203
+ };
204
+ struct Derived : Base {
205
+ std::vector<int> data;
206
+ };
207
+
208
+ Base* b = new Derived;
209
+ delete b; // UB: Derived's destructor not called
210
+
211
+ // RIGHT: Virtual destructor
212
+ struct Base {
213
+ virtual ~Base() = default; // polymorphic bases need virtual dtor
214
+ virtual void foo() {}
215
+ };
216
+ ```
@@ -0,0 +1,282 @@
1
+ # Modern C++17/20 Idioms
2
+
3
+ This reference covers the most impactful modern C++ features with minimal examples.
4
+
5
+ ## Structured Bindings
6
+
7
+ Unpack tuples, pairs, structs, and arrays into named variables.
8
+
9
+ ```cpp
10
+ #include <tuple>
11
+ #include <map>
12
+
13
+ // Pair/tuple
14
+ auto [key, value] = std::pair{1, std::string("hello")};
15
+
16
+ // Array
17
+ auto [a, b, c] = std::array{1, 2, 3};
18
+
19
+ // Struct — works if all members are public
20
+ struct Point { double x, y, z; };
21
+ Point p{1.0, 2.0, 3.0};
22
+ auto [px, py, pz] = p;
23
+
24
+ // Map iteration
25
+ std::map<std::string, int> ages{{"Alice", 30}, {"Bob", 25}};
26
+ for (const auto& [name, age] : ages) {
27
+ std::cout << name << ": " << age << '\n';
28
+ }
29
+
30
+ // Multiple return values
31
+ std::tuple<int, std::string, double> compute() { return {42, "answer", 3.14}; }
32
+ auto [code, label, score] = compute();
33
+ ```
34
+
35
+ ## `if constexpr`
36
+
37
+ Compile-time conditional code execution — the unselected branch is not instantiated.
38
+
39
+ ```cpp
40
+ #include <type_traits>
41
+
42
+ template <typename T>
43
+ auto inspect(const T& val) {
44
+ if constexpr (std::is_integral_v<T>) {
45
+ return "integer";
46
+ } else if constexpr (std::is_floating_point_v<T>) {
47
+ return "floating-point";
48
+ } else {
49
+ return "other";
50
+ }
51
+ }
52
+
53
+ // Works where SFINAE would require complex enable_if
54
+ template <typename T>
55
+ T doubled(T x) {
56
+ if constexpr (std::is_integral_v<T>) {
57
+ return x + x; // valid only for integral T
58
+ } else {
59
+ return x * 2.0; // valid only for floating-point T
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## `std::optional`
65
+
66
+ Represents a value that may or may not be present. Alternative to pointer-returning APIs.
67
+
68
+ ```cpp
69
+ #include <optional>
70
+
71
+ // Return optional
72
+ std::optional<int> find_index(const std::vector<int>& v, int target) {
73
+ for (auto it = v.begin(); it != v.end(); ++it) {
74
+ if (*it == target)
75
+ return {static_cast<int>(std::distance(v.begin(), it))};
76
+ }
77
+ return std::nullopt;
78
+ }
79
+
80
+ // Check and use
81
+ if (auto idx = find_index(vec, 42)) {
82
+ std::cout << "Found at " << *idx << '\n';
83
+ } else {
84
+ std::cout << "Not found\n";
85
+ }
86
+
87
+ // Transform an optional without unwrapping
88
+ std::optional<std::string> name = /* ... */;
89
+ auto greeting = name.transform([](const std::string& n) { return "Hello, " + n; });
90
+ ```
91
+
92
+ ## `std::string_view`
93
+
94
+ Non-owning view into a string. Use for read-only string operations without copying.
95
+
96
+ ```cpp
97
+ #include <string_view>
98
+
99
+ // Function takes a view — no ownership, no allocation
100
+ bool ends_with(std::string_view s, std::string_view suffix) {
101
+ return s.size() >= suffix.size() && s.substr(s.size() - suffix.size()) == suffix;
102
+ }
103
+
104
+ // Calling with different string types — no conversion/copy
105
+ std::string s = "filename.txt";
106
+ std::string_view sv = s;
107
+ ends_with(sv, ".txt"); // string_view
108
+ ends_with("literal", ".txt"); // const char* converts to string_view
109
+ ends_with(sv, std::string(".log")); // string converts to string_view
110
+
111
+ // AVOID: storing string_view as a class member — it doesn't own the data
112
+ // (only safe if another owning string keeps the data alive)
113
+ ```
114
+
115
+ ## `std::variant` (C++17)
116
+
117
+ Type-safe union — an object that holds one of several specified types.
118
+
119
+ ```cpp
120
+ #include <variant>
121
+
122
+ std::variant<int, std::string, double> v = 42;
123
+
124
+ // Check and access
125
+ if (std::holds_alternative<int>(v)) {
126
+ int i = std::get<int>(v);
127
+ }
128
+
129
+ // Visitor pattern
130
+ std::visit([](const auto& val) {
131
+ std::cout << val << '\n';
132
+ }, v);
133
+
134
+ // Overload helper (C++17 doesn't have std::visit overload; use this pattern)
135
+ template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
136
+ template <class... Ts> overload(Ts...) -> overload<Ts...>;
137
+
138
+ std::visit(overload{
139
+ [](int i) { std::cout << "int: " << i << '\n'; },
140
+ [](const std::string& s) { std::cout << "string: " << s << '\n'; },
141
+ [](double d) { std::cout << "double: " << d << '\n'; }
142
+ }, v);
143
+ ```
144
+
145
+ ## `std::any` (C++17)
146
+
147
+ Holds any single value of any type. Use when the set of possible types is open-ended.
148
+
149
+ ```cpp
150
+ #include <any>
151
+
152
+ std::any a = 42;
153
+ a = std::string("hello");
154
+ a = 3.14;
155
+
156
+ if (a.type() == typeid(double)) {
157
+ double d = std::any_cast<double>(a);
158
+ }
159
+
160
+ // Safer: check before cast
161
+ if (auto* pd = std::any_cast<double>(&a)) {
162
+ std::cout << *pd << '\n';
163
+ }
164
+ ```
165
+
166
+ ## Ranges (C++20)
167
+
168
+ ```cpp
169
+ #include <ranges>
170
+ #include <algorithm>
171
+
172
+ std::vector<int> v = {1, 2, 3, 4, 5, 6};
173
+
174
+ // Filter and transform lazily — no new vector allocation
175
+ auto evens = v | std::views::filter([](int x) { return x % 2 == 0; });
176
+ auto doubled = evens | std::views::transform([](int x) { return x * 2; });
177
+
178
+ for (int x : doubled) std::cout << x << ' '; // 4 8 12
179
+
180
+ // Range-based sort (C++20 — simpler than iterators)
181
+ std::ranges::sort(v);
182
+
183
+ // Find
184
+ auto it = std::ranges::find(v, 3);
185
+
186
+ // All / any / none
187
+ bool has_negative = std::ranges::any_of(v, [](int x) { return x < 0; });
188
+
189
+ // Projections — sort by member without lambda boilerplate
190
+ struct Employee { std::string name; int level; };
191
+ std::vector<Employee> emps{{"Bob", 3}, {"Alice", 1}, {"Carol", 2}};
192
+ std::ranges::sort(emps, {}, &Employee::level); // sort by .level
193
+ ```
194
+
195
+ ## Concepts (C++20)
196
+
197
+ Explicit, named constraints on template parameters. Replace SFINAE with readable syntax.
198
+
199
+ ```cpp
200
+ #include <concepts>
201
+ #include <type_traits>
202
+
203
+ // Define a concept
204
+ template <typename T>
205
+ concept Numeric = std::integral<T> || std::floating_point<T>;
206
+
207
+ // Use in template parameters
208
+ template <Numeric T>
209
+ T add(T a, T b) { return a + b; }
210
+
211
+ static_assert(add(1, 2) == 3);
212
+ static_assert(add(1.5, 2.5) == 4.0);
213
+ // add("a", "b") — fails to instantiate: const char* is not Numeric
214
+
215
+ // Built-in concepts
216
+ static_assert(std::ranges::range<std::vector<int>>);
217
+ static_assert(std::copyable<int>);
218
+
219
+ // Combining concepts
220
+ template <typename T>
221
+ concept Comparable = requires(T a, T b) {
222
+ { a < b } -> std::convertible_to<bool>;
223
+ { a == b } -> std::convertible_to<bool>;
224
+ };
225
+ ```
226
+
227
+ ## `std::format` (C++20)
228
+
229
+ Type-safe, Python-style string formatting.
230
+
231
+ ```cpp
232
+ #include <format>
233
+ #include <chrono>
234
+
235
+ // Instead of printf/sprintf/cout combos
236
+ std::string s = std::format("{} + {} = {}", 1, 2, 3); // "1 + 2 = 3"
237
+ std::string t = std::format("{:>10}", 42); // right-aligned, width 10
238
+ std::string u = std::format("{:.2f}", 3.14159); // "3.14"
239
+ std::string v = std::format("{:02x}", 255); // "ff"
240
+
241
+ // Named arguments
242
+ std::cout << std::format("Hello, {name}! You are {age} years old.\n",
243
+ std::format_args::arg("name", "Alice"), /* arg */);
244
+ ```
245
+
246
+ ## `std::span` (C++20)
247
+
248
+ Non-owning view into a contiguous sequence — replacement for pointer+length pairs.
249
+
250
+ ```cpp
251
+ #include <span>
252
+
253
+ // Instead of (T* ptr, size_t count)
254
+ void print_all(std::span<const int> data) {
255
+ for (int x : data) std::cout << x << ' ';
256
+ }
257
+
258
+ std::vector<int> vec{1, 2, 3};
259
+ std::array<int, 3> arr{4, 5, 6};
260
+ int raw[] = {7, 8, 9};
261
+
262
+ print_all(vec); // works
263
+ print_all(arr); // works
264
+ print_all(raw); // works — decays to span<int, 3>
265
+ print_all({raw, 2}); // explicit length
266
+ ```
267
+
268
+ ## `[[likely]]` / `[[unlikely]]` (C++20)
269
+
270
+ Branch prediction hints for performance-critical paths.
271
+
272
+ ```cpp
273
+ bool process(int x) {
274
+ if (x > 0) [[likely]] {
275
+ // compiler may rearrange code for the common case
276
+ return do_positive(x);
277
+ }
278
+ [[unlikely]] {
279
+ return do_non_positive(x);
280
+ }
281
+ }
282
+ ```
@@ -0,0 +1,96 @@
1
+ # C++ Code Review Checklist
2
+
3
+ Use this checklist when reviewing C++ pull requests or before committing.
4
+
5
+ ## Memory Safety
6
+
7
+ - [ ] No raw `new` or `delete` — use `std::unique_ptr` / `std::make_unique`
8
+ - [ ] No owning raw pointers as class members (prefer `std::unique_ptr`)
9
+ - [ ] Shared ownership genuinely needed? Consider if `unique_ptr` suffices
10
+ - [ ] `shared_ptr` cycles broken with `weak_ptr`?
11
+ - [ ] Virtual destructor declared on any polymorphic base class
12
+ - [ ] All special member functions (Rule of Five) declared if needed
13
+ - [ ] No use-after-free: references/pointers outlive the data they refer to
14
+ - [ ] Arrays allocated with `new Type[n]` are `delete[]`d (or better, use `vector`)
15
+
16
+ ## RAII and Resource Management
17
+
18
+ - [ ] File handles, mutexes, sockets use RAII wrappers (destructor cleans up)
19
+ - [ ] `std::lock_guard` / `std::scoped_lock` used for mutex locking — not raw `.lock()/.unlock()`
20
+ - [ ] Every `std::thread` has `.join()` or `.detach()` called before destruction
21
+ - [ ] Prefer `std::jthread` (C++20) for automatic stop and join
22
+
23
+ ## Const Correctness
24
+
25
+ - [ ] Member functions that don't modify state are marked `const`
26
+ - [ ] Non-modifying parameters passed by `const&` (or by value if cheap-to-copy)
27
+ - [ ] `const` variables used where data doesn't change after init
28
+ - [ ] `const_iterator` used when iteration doesn't modify
29
+
30
+ ## Modern C++ Usage
31
+
32
+ - [ ] Uses C++17/20 features where appropriate (structured bindings, `if constexpr`, `std::optional`, `string_view`)
33
+ - [ ] No C-style casts — uses `static_cast`, `reinterpret_cast`, `const_cast`
34
+ - [ ] `[[nodiscard]]` on functions returning values that must not be ignored
35
+ - [ ] `override` used on all virtual method overrides
36
+ - [ ] `noexcept` used correctly (only on functions that truly cannot throw)
37
+ - [ ] `std::make_unique` / `std::make_shared` used instead of direct `new`
38
+
39
+ ## Headers and Includes
40
+
41
+ - [ ] No `using namespace std;` in header files
42
+ - [ ] No unused `#include` directives
43
+ - [ ] Include order: related header first, then standard library, then external libs
44
+ - [ ] Header is self-contained (includes everything it uses)
45
+
46
+ ## Containers and Types
47
+
48
+ - [ ] `std::vector` preferred over raw arrays for dynamic sequences
49
+ - [ ] `std::vector::reserve()` called when size is known ahead of time
50
+ - [ ] `std::array` used for fixed-size compile-time-known arrays
51
+ - [ ] `std::string_view` used for read-only string params (no ownership)
52
+ - [ ] `std::optional` used instead of sentinel values (e.g., `-1`, `nullptr`) for optional values
53
+ - [ ] No `std::endl` in hot paths (flushes the stream)
54
+
55
+ ## Error Handling
56
+
57
+ - [ ] Expected failures (not found, invalid input) handled without exceptions
58
+ - [ ] Exceptions used only for truly exceptional/unrecoverable conditions
59
+ - [ ] No exception thrown from destructors (use `noexcept` and `std::terminate` instead if needed)
60
+ - [ ] Exception safety level appropriate for the function (at minimum: no resource leaks)
61
+ - [ ] `std::expected<T, E>` (C++23) or error codes used in library/API code
62
+
63
+ ## Concurrency
64
+
65
+ - [ ] Shared mutable state protected by `std::mutex` / `std::lock_guard`
66
+ - [ ] No data races on shared variables
67
+ - [ ] `std::atomic` used for simple shared counters and flags
68
+ - [ ] Thread lifecycle managed correctly (`join()`/`detach()` or `jthread`)
69
+ - [ ] No passing of references to locals into detached threads
70
+
71
+ ## Performance Considerations
72
+
73
+ - [ ] No unnecessary copies (pass by `const&`, use `std::move`, prefer move over copy)
74
+ - [ ] No hidden allocations in hot paths (e.g., `s + "text"` inside a loop)
75
+ - [ ] Vector `reserve()` before known-size loops
76
+ - [ ] Emplacement used when inserting into containers: `vec.emplace_back(args...)` over `vec.push_back(Type(args...))`
77
+
78
+ ## Initialization
79
+
80
+ - [ ] All variables initialized before use
81
+ - [ ] Member variables initialized in initializer list or with default member initializers
82
+ - [ ] No use of default-initialized built-in types without assignment
83
+
84
+ ## Code Quality
85
+
86
+ - [ ] No `goto` statements
87
+ - [ ] No macros for anything that can be a `constexpr`, `inline`, or template
88
+ - [ ] Template code in headers uses explicit `typename T` or `class T`
89
+ - [ ] No hardcoded magic numbers — use named constants or `constexpr`
90
+ - [ ] `auto` used appropriately (not hiding intent)
91
+
92
+ ## Testing
93
+
94
+ - [ ] New code has corresponding unit tests
95
+ - [ ] Edge cases covered: empty containers, zero/negative values, boundary conditions
96
+ - [ ] No commented-out test code committed
@@ -0,0 +1,28 @@
1
+ # C++ Testing Skill
2
+
3
+ C++ testing patterns for opencode. Covers unit, integration, and host-side tests, framework selection (GoogleTest / Catch2 / doctest), mocking strategies, TDD workflow, and the 80% coverage gate.
4
+
5
+ ## What it provides
6
+
7
+ - **SKILL.md** — framework selection + 5 deep-dive references
8
+ - **references/framework-compare.md** — GoogleTest vs Catch2 vs doctest with code samples
9
+ - **references/host-test-for-embedded.md** — pure-CMake host test for firmware `.cpp` without `idf.py`/FreeRTOS
10
+ - **references/mocking.md** — abstract interfaces, link-time seam, `std::function` injection, gmock
11
+ - **references/tdd-workflow.md** — red-green-refactor in C++
12
+ - **references/coverage.md** — gcov/lcov, 80% gate on `main/` and `components/`
13
+
14
+ ## When it triggers
15
+
16
+ - Writing a new C++ unit or host test
17
+ - Choosing a test framework
18
+ - Setting up a host-side test binary that links firmware code without ESP-IDF
19
+ - Running a test-gate before merging
20
+ - Applying TDD to firmware policy/payload modules
21
+
22
+ ## Manual install
23
+
24
+ ```bash
25
+ cp -R SKILL.md references ~/.opencode/skills/cpp-testing/
26
+ ```
27
+
28
+ The BizarHarness installer can also install this automatically — select the **C++ testing** component.