@rmrdeveloper/sideroom-pi 5.0.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.
- package/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/pi-extension.js +1175 -0
- package/package.json +70 -0
- package/skills/sideroom-critic/SKILL.md +32 -0
- package/skills/sideroom-grilling/SKILL.md +81 -0
- package/skills/sideroom-spec/SKILL.md +38 -0
- package/skills/sideroom-transcribe-audio/SKILL.md +38 -0
- package/skills/sideroom-transcribe-audio/scripts/transcribe.py +40 -0
- package/src/assets/agents/sideroom-code-reviewer.md +30 -0
- package/src/assets/agents/sideroom-fixer.md +37 -0
- package/src/assets/agents/sideroom-implementer.md +39 -0
- package/src/assets/agents/sideroom-planner.md +31 -0
- package/src/assets/agents/sideroom-verifier.md +32 -0
- package/src/assets/artifacts/GUIDELINES_TEMPLATE.md +463 -0
- package/src/assets/artifacts/guidelines/java.md +211 -0
- package/src/assets/artifacts/guidelines/javascript.md +235 -0
- package/src/assets/artifacts/guidelines/php-laravel.md +210 -0
- package/src/assets/artifacts/guidelines/python.md +227 -0
- package/src/assets/artifacts/guidelines/typescript.md +231 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# TypeScript Coding Guidelines
|
|
2
|
+
|
|
3
|
+
This is the TypeScript version of `GUIDELINES_TEMPLATE.md`. It preserves the
|
|
4
|
+
same principles, decisions, and example scenarios, expressed with strict
|
|
5
|
+
TypeScript. The shared baseline remains mandatory.
|
|
6
|
+
|
|
7
|
+
## Quick reference
|
|
8
|
+
|
|
9
|
+
| Do | Don't |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| Early return on bad input | Pyramid `if/else` nesting |
|
|
12
|
+
| Explicit error, fail now | Fallbacks that hide a broken invariant |
|
|
13
|
+
| One responsibility per unit | Validate, transform, persist, and notify together |
|
|
14
|
+
| Extract repeated domain logic | Abstract before a second real use |
|
|
15
|
+
| Ship the simplest solution | Add speculative layers or configuration |
|
|
16
|
+
| Return new values | Mutate caller-owned input |
|
|
17
|
+
| Validate once at the edge | Re-validate the same contract in every layer |
|
|
18
|
+
| Use named types and constants | Scatter magic literals |
|
|
19
|
+
|
|
20
|
+
## Principles
|
|
21
|
+
|
|
22
|
+
### Guard clauses
|
|
23
|
+
|
|
24
|
+
Keep the same discount scenario flat and typed.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// Bad
|
|
28
|
+
function getDiscount(user?: { active: boolean; subscriber: boolean }): number {
|
|
29
|
+
if (user) { if (user.active) { if (user.subscriber) return 0.2; } }
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Good
|
|
34
|
+
function getDiscount(user?: { active: boolean; subscriber: boolean }): number {
|
|
35
|
+
if (!user) return 0;
|
|
36
|
+
if (!user.active) return 0;
|
|
37
|
+
if (!user.subscriber) return 0;
|
|
38
|
+
return 0.2;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Fail fast
|
|
43
|
+
|
|
44
|
+
Never substitute a default zone for an impossible domain key.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const zones: Record<string, { baseCost: number }> = { local: { baseCost: 5 } };
|
|
48
|
+
function shippingCost(zoneId: string): number {
|
|
49
|
+
const zone = zones[zoneId];
|
|
50
|
+
if (zone === undefined) throw new Error(`Unknown zoneId: ${zoneId}`);
|
|
51
|
+
return zone.baseCost;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### SRP
|
|
56
|
+
|
|
57
|
+
Split validation, normalization, persistence, and notification.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
function normalizeEmail(email: string): string { return email.trim().toLowerCase(); }
|
|
61
|
+
async function createUser(email: string, users: UserRepository, mailer: Mailer): Promise<void> {
|
|
62
|
+
if (email.length === 0) throw new Error('email required');
|
|
63
|
+
const user = await users.insert({ email: normalizeEmail(email) });
|
|
64
|
+
await mailer.send(user.email, 'welcome');
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### DRY
|
|
69
|
+
|
|
70
|
+
Extract only repeated domain behavior. One formatting call is not a reusable
|
|
71
|
+
abstraction; three identical ticket-status normalizations are.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
type TicketStatus = 'open' | 'closed';
|
|
75
|
+
function normalizeTicketStatus(value: string): TicketStatus {
|
|
76
|
+
if (value === 'open' || value === 'closed') return value;
|
|
77
|
+
throw new Error(`Unknown ticket status: ${value}`);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### KISS
|
|
82
|
+
|
|
83
|
+
Keep the price and invoice scenarios limited to today's requirement.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
function formatPrice(value: number): string { return `$${value.toFixed(2)}`; }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### YAGNI (You Aren't Gonna Need It)
|
|
90
|
+
|
|
91
|
+
Build the invoice requirement that exists today; do not add recurrence,
|
|
92
|
+
multi-currency branches, or a future configuration object.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
function createInvoice(order: { total: number; items: readonly string[] }) {
|
|
96
|
+
return { total: order.total, items: order.items };
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Do not add locale strategies, recurring options, or multi-currency branches
|
|
101
|
+
without a requirement.
|
|
102
|
+
|
|
103
|
+
### Composition over inheritance
|
|
104
|
+
|
|
105
|
+
Compose only needed behavior instead of inheriting unrelated methods.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const canBark = { makeSound: () => 'Woof' };
|
|
109
|
+
const canFly = { fly: () => 'Flying' };
|
|
110
|
+
const dog = { ...canBark };
|
|
111
|
+
const bird = { ...canBark, ...canFly };
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Law of Demeter
|
|
115
|
+
|
|
116
|
+
Ask the immediate collaborator for a city name instead of reaching through a
|
|
117
|
+
deep object graph.
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
interface User { cityName(): string; }
|
|
121
|
+
function getCityName(user: User): string { return user.cityName(); }
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Command Query Separation
|
|
125
|
+
|
|
126
|
+
Keep reading the next identifier separate from incrementing it.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
function peekNextId(counter: { value: number }): number { return counter.value + 1; }
|
|
130
|
+
function incrementCounter(counter: { value: number }): void { counter.value += 1; }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Explicit error handling
|
|
134
|
+
|
|
135
|
+
Add context and retain the cause; never return `null` merely because a request
|
|
136
|
+
failed.
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
async function loadUser(id: string, api: Api): Promise<User> {
|
|
140
|
+
try { return await api.getUser(id); }
|
|
141
|
+
catch (error) { throw new Error(`Failed to load user ${id}`, { cause: error }); }
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Immutability by default
|
|
146
|
+
|
|
147
|
+
Use readonly data and make the cart scenario return a replacement value.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
interface Cart { readonly items: readonly string[]; }
|
|
151
|
+
function addItem(cart: Cart, item: string): Cart {
|
|
152
|
+
return { ...cart, items: [...cart.items, item] };
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Null and undefined handling
|
|
157
|
+
|
|
158
|
+
Use one documented convention and preserve legitimate zero values.
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
function getDiscount(user?: { plan?: { discount: number } }): number {
|
|
162
|
+
return user?.plan?.discount ?? 0;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Testability as a design constraint
|
|
167
|
+
|
|
168
|
+
Inject time, APIs, and repositories rather than reaching for globals.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
function isSubscriptionExpired(expiresAt: Date, now: Date): boolean {
|
|
172
|
+
return expiresAt < now;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Dependency direction
|
|
177
|
+
|
|
178
|
+
Domain calculations receive domain data, not a concrete SQL connection.
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
function calculateInvoiceTotal(invoice: { items: readonly { price: number }[] }): number {
|
|
182
|
+
return invoice.items.reduce((sum, item) => sum + item.price, 0);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Clear names
|
|
187
|
+
|
|
188
|
+
Use `activeSubscriptions`, `remainingDays`, and `externalId`, never `data`,
|
|
189
|
+
`temp`, or `x`.
|
|
190
|
+
|
|
191
|
+
### Comments
|
|
192
|
+
|
|
193
|
+
Comments explain a non-obvious trade-off or hazard, not the next line of code.
|
|
194
|
+
A TODO needs a tracked issue.
|
|
195
|
+
|
|
196
|
+
### No magic strings
|
|
197
|
+
|
|
198
|
+
Name order states once and reuse their contract.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
const ORDER_STATUS = { pendingPayment: 'pending_payment', paid: 'paid' } as const;
|
|
202
|
+
if (order.status === ORDER_STATUS.pendingPayment) await requestPayment(order);
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### SOLID
|
|
206
|
+
|
|
207
|
+
Use ports where variation is real; do not couple a welcome-email use case to
|
|
208
|
+
SMTP.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
interface Mailer { send(email: string, template: string): Promise<void>; }
|
|
212
|
+
class SendWelcomeEmail {
|
|
213
|
+
constructor(private readonly mailer: Mailer) {}
|
|
214
|
+
execute(email: string): Promise<void> { return this.mailer.send(email, 'welcome'); }
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Validate once
|
|
219
|
+
|
|
220
|
+
Parse the transport payload at the boundary into a typed command. Services
|
|
221
|
+
trust that command rather than repeating the same required, type, and range
|
|
222
|
+
checks. Keep one schema or validator per body/query.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
interface UpdateProfile { readonly email: string; }
|
|
226
|
+
function updateProfile(input: UpdateProfile, users: UserRepository): Promise<void> {
|
|
227
|
+
return users.update(input);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
When in doubt: **fail fast, keep it flat, keep it small.**
|