pi-agent-toolkit 0.1.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/dist/dotfiles/AGENTS.md +197 -0
- package/dist/dotfiles/APPEND_SYSTEM.md +78 -0
- package/dist/dotfiles/agent-modes.json +12 -0
- package/dist/dotfiles/agent-skills/exa-search/.env.example +4 -0
- package/dist/dotfiles/agent-skills/exa-search/SKILL.md +234 -0
- package/dist/dotfiles/agent-skills/exa-search/scripts/exa-api.cjs +197 -0
- package/dist/dotfiles/auth.json.template +5 -0
- package/dist/dotfiles/damage-control-rules.yaml +318 -0
- package/dist/dotfiles/extensions/btw.ts +1031 -0
- package/dist/dotfiles/extensions/commit-approval.ts +590 -0
- package/dist/dotfiles/extensions/context.ts +578 -0
- package/dist/dotfiles/extensions/control.ts +1748 -0
- package/dist/dotfiles/extensions/damage-control/index.ts +543 -0
- package/dist/dotfiles/extensions/damage-control/node_modules/.package-lock.json +22 -0
- package/dist/dotfiles/extensions/damage-control/package-lock.json +28 -0
- package/dist/dotfiles/extensions/damage-control/package.json +7 -0
- package/dist/dotfiles/extensions/dirty-repo-guard.ts +56 -0
- package/dist/dotfiles/extensions/exa-enforce.ts +51 -0
- package/dist/dotfiles/extensions/exa-search-tool.ts +384 -0
- package/dist/dotfiles/extensions/execute-command/index.ts +82 -0
- package/dist/dotfiles/extensions/files.ts +1112 -0
- package/dist/dotfiles/extensions/loop.ts +446 -0
- package/dist/dotfiles/extensions/pr-approval.ts +730 -0
- package/dist/dotfiles/extensions/qna-interactive.ts +532 -0
- package/dist/dotfiles/extensions/question-mode.ts +242 -0
- package/dist/dotfiles/extensions/require-session-name-on-exit.ts +141 -0
- package/dist/dotfiles/extensions/review.ts +2091 -0
- package/dist/dotfiles/extensions/session-breakdown.ts +1629 -0
- package/dist/dotfiles/extensions/term-notify.ts +150 -0
- package/dist/dotfiles/extensions/tilldone.ts +527 -0
- package/dist/dotfiles/extensions/todos.ts +2082 -0
- package/dist/dotfiles/extensions/tools.ts +146 -0
- package/dist/dotfiles/extensions/uv.ts +123 -0
- package/dist/dotfiles/global-skills/brainstorm/SKILL.md +10 -0
- package/dist/dotfiles/global-skills/cli-detector/SKILL.md +192 -0
- package/dist/dotfiles/global-skills/gh-issue-creator/SKILL.md +173 -0
- package/dist/dotfiles/global-skills/google-chat-cards-v2/SKILL.md +237 -0
- package/dist/dotfiles/global-skills/google-chat-cards-v2/references/bridge_tap_implementation.md +466 -0
- package/dist/dotfiles/global-skills/technical-docs/SKILL.md +204 -0
- package/dist/dotfiles/global-skills/technical-docs/references/diagrams.md +168 -0
- package/dist/dotfiles/global-skills/technical-docs/references/examples.md +449 -0
- package/dist/dotfiles/global-skills/technical-docs/scripts/validate_docs.py +352 -0
- package/dist/dotfiles/global-skills/whats-new/SKILL.md +159 -0
- package/dist/dotfiles/intercepted-commands/pip +7 -0
- package/dist/dotfiles/intercepted-commands/pip3 +7 -0
- package/dist/dotfiles/intercepted-commands/poetry +10 -0
- package/dist/dotfiles/intercepted-commands/python +104 -0
- package/dist/dotfiles/intercepted-commands/python3 +104 -0
- package/dist/dotfiles/mcp.json.template +32 -0
- package/dist/dotfiles/models.json +27 -0
- package/dist/dotfiles/settings.json +25 -0
- package/dist/index.js +1344 -0
- package/package.json +34 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# Documentation Examples
|
|
2
|
+
|
|
3
|
+
This document provides complete before/after examples demonstrating technical documentation standards in practice.
|
|
4
|
+
|
|
5
|
+
## Python Docstrings
|
|
6
|
+
|
|
7
|
+
### ❌ Poor Docstring Example
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
def process_file(client, file_path, options=None):
|
|
11
|
+
"""Process a file."""
|
|
12
|
+
# Implementation
|
|
13
|
+
pass
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Problems:**
|
|
17
|
+
- No parameter descriptions
|
|
18
|
+
- No type information
|
|
19
|
+
- No return value documentation
|
|
20
|
+
- No exception documentation
|
|
21
|
+
- Vague description
|
|
22
|
+
|
|
23
|
+
### ✅ Good Docstring Example
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
def process_file(
|
|
27
|
+
client_name: str,
|
|
28
|
+
file_path: Path,
|
|
29
|
+
options: dict[str, Any] | None = None,
|
|
30
|
+
) -> ProcessingResult:
|
|
31
|
+
"""Process a client file and return validation results.
|
|
32
|
+
|
|
33
|
+
Reads the file from the specified path, validates the column mappings
|
|
34
|
+
against the client's configuration, and returns a structured result
|
|
35
|
+
containing validation status and any errors found.
|
|
36
|
+
|
|
37
|
+
:param client_name: Name of the client (must exist in clients/ directory)
|
|
38
|
+
:param file_path: Absolute path to the file to process
|
|
39
|
+
:param options: Optional processing configuration overrides
|
|
40
|
+
:returns: ProcessingResult containing status, row counts, and error details
|
|
41
|
+
:raises FileNotFoundError: If file_path does not exist
|
|
42
|
+
:raises ValueError: If client_name is not configured
|
|
43
|
+
:raises ProcessingError: If file validation fails
|
|
44
|
+
"""
|
|
45
|
+
# Implementation
|
|
46
|
+
pass
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Improvements:**
|
|
50
|
+
- Type hints for all parameters and return value
|
|
51
|
+
- Clear, specific description of purpose
|
|
52
|
+
- Each parameter documented with context
|
|
53
|
+
- Return value structure explained
|
|
54
|
+
- All exceptions documented with conditions
|
|
55
|
+
|
|
56
|
+
## README Structure
|
|
57
|
+
|
|
58
|
+
### ❌ Poor README Example
|
|
59
|
+
|
|
60
|
+
```markdown
|
|
61
|
+
# My Project
|
|
62
|
+
|
|
63
|
+
This is a cool project.
|
|
64
|
+
|
|
65
|
+
## Install
|
|
66
|
+
|
|
67
|
+
pip install myproject
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
from myproject import Client
|
|
72
|
+
client = Client()
|
|
73
|
+
client.do_stuff()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Problems:**
|
|
77
|
+
- No purpose or context
|
|
78
|
+
- No prerequisites
|
|
79
|
+
- No authentication/configuration
|
|
80
|
+
- Code examples lack language tags
|
|
81
|
+
- No explanation of what "do_stuff" does
|
|
82
|
+
- Missing error handling examples
|
|
83
|
+
|
|
84
|
+
### ✅ Good README Example
|
|
85
|
+
|
|
86
|
+
```markdown
|
|
87
|
+
# Bridge TAP API
|
|
88
|
+
|
|
89
|
+
Python SDK and worker system for building audiences using the Bridge API with file-based matching.
|
|
90
|
+
|
|
91
|
+
## Overview
|
|
92
|
+
|
|
93
|
+
The Bridge TAP API provides:
|
|
94
|
+
- **SDK Client**: Type-safe Python client for Bridge API interactions
|
|
95
|
+
- **Worker System**: Background job processing for audience building
|
|
96
|
+
- **Validation Pipeline**: File validation before audience creation
|
|
97
|
+
- **Database Layer**: PostgreSQL-based state management
|
|
98
|
+
|
|
99
|
+
## Prerequisites
|
|
100
|
+
|
|
101
|
+
- Python 3.11+
|
|
102
|
+
- PostgreSQL 14+
|
|
103
|
+
- AWS credentials for S3 access
|
|
104
|
+
- Bridge API account with API key
|
|
105
|
+
|
|
106
|
+
## Installation
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pip install bridge-tap-api
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Quick Start
|
|
113
|
+
|
|
114
|
+
### Basic Usage
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from bridge_tap_api.sdk.bridge import BridgeAPIClient, BridgeApiConfig
|
|
118
|
+
from bridge_tap_api.sdk.bridge.models import TargetType, MatchSource
|
|
119
|
+
|
|
120
|
+
# Configure client
|
|
121
|
+
config = BridgeApiConfig(
|
|
122
|
+
account_id="your_account_id",
|
|
123
|
+
api_key="your_api_key",
|
|
124
|
+
)
|
|
125
|
+
client = BridgeAPIClient(config)
|
|
126
|
+
|
|
127
|
+
# Build audience from S3 file
|
|
128
|
+
source = MatchSource(
|
|
129
|
+
s3_uri="s3://bucket/path/to/file.csv",
|
|
130
|
+
column_mappings={"email": "email_address", "first_name": "fname"},
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
response = client.build_audience(
|
|
134
|
+
target_type=TargetType.INDIVIDUAL,
|
|
135
|
+
source=source,
|
|
136
|
+
name="My Audience",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
print(f"Audience build started: {response.pid}")
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Error Handling
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from bridge_tap_api.sdk.bridge.exceptions import BridgeAPIError
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
response = client.build_audience(...)
|
|
149
|
+
except BridgeAPIError as e:
|
|
150
|
+
print(f"API error: {e.message}, Status: {e.status_code}")
|
|
151
|
+
except ValueError as e:
|
|
152
|
+
print(f"Configuration error: {e}")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Configuration
|
|
156
|
+
|
|
157
|
+
Set environment variables:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
export BRIDGE_API_KEY="your_key"
|
|
161
|
+
export BRIDGE_ACCOUNT_ID="your_account"
|
|
162
|
+
export AWS_ACCESS_KEY_ID="your_key"
|
|
163
|
+
export AWS_SECRET_ACCESS_KEY="your_secret"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Or use configuration files (see [Configuration Guide](docs/configuration.md)).
|
|
167
|
+
|
|
168
|
+
## Documentation
|
|
169
|
+
|
|
170
|
+
- [API Reference](docs/api-reference.md)
|
|
171
|
+
- [Architecture Overview](docs/architecture.md)
|
|
172
|
+
- [Development Guide](docs/development.md)
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT License - see LICENSE file for details.
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Improvements:**
|
|
180
|
+
- Clear project purpose and value proposition
|
|
181
|
+
- Organized sections with logical flow
|
|
182
|
+
- Prerequisites listed upfront
|
|
183
|
+
- Complete, runnable code examples with syntax highlighting
|
|
184
|
+
- Error handling examples
|
|
185
|
+
- Configuration options documented
|
|
186
|
+
- Links to detailed documentation
|
|
187
|
+
- Professional structure
|
|
188
|
+
|
|
189
|
+
## API Documentation
|
|
190
|
+
|
|
191
|
+
### ❌ Poor API Documentation
|
|
192
|
+
|
|
193
|
+
```markdown
|
|
194
|
+
## build_audience
|
|
195
|
+
|
|
196
|
+
Builds an audience.
|
|
197
|
+
|
|
198
|
+
**Parameters:**
|
|
199
|
+
- target_type
|
|
200
|
+
- source
|
|
201
|
+
- name
|
|
202
|
+
|
|
203
|
+
**Returns:** response
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Problems:**
|
|
207
|
+
- No parameter types or descriptions
|
|
208
|
+
- No required/optional indicators
|
|
209
|
+
- No return value structure
|
|
210
|
+
- No error documentation
|
|
211
|
+
- No usage examples
|
|
212
|
+
|
|
213
|
+
### ✅ Good API Documentation
|
|
214
|
+
|
|
215
|
+
```markdown
|
|
216
|
+
## build_audience()
|
|
217
|
+
|
|
218
|
+
Build a new audience using the Bridge API with file-based matching or list-based targeting.
|
|
219
|
+
|
|
220
|
+
**Method Signature:**
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
def build_audience(
|
|
224
|
+
self,
|
|
225
|
+
target_type: TargetType,
|
|
226
|
+
source: MatchSource | ListSource,
|
|
227
|
+
name: str,
|
|
228
|
+
*,
|
|
229
|
+
description: str = "",
|
|
230
|
+
external_id: str = "",
|
|
231
|
+
) -> AudienceBuildResponse
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Parameters:**
|
|
235
|
+
|
|
236
|
+
| Parameter | Type | Required | Description |
|
|
237
|
+
|-----------|------|----------|-------------|
|
|
238
|
+
| `target_type` | `TargetType` | Yes | Audience target type: `TargetType.INDIVIDUAL` or `TargetType.HOUSEHOLD` |
|
|
239
|
+
| `source` | `MatchSource \| ListSource` | Yes | Source configuration. Use `MatchSource` for S3 file matching or `ListSource` for list-based audiences |
|
|
240
|
+
| `name` | `str` | Yes | Human-readable audience name (3-100 characters) |
|
|
241
|
+
| `description` | `str` | No | Optional audience description for documentation |
|
|
242
|
+
| `external_id` | `str` | No | Optional external identifier for tracking |
|
|
243
|
+
|
|
244
|
+
**Returns:**
|
|
245
|
+
|
|
246
|
+
`AudienceBuildResponse` containing:
|
|
247
|
+
- `pid` (str): Process ID for status tracking
|
|
248
|
+
- `status` (str): Initial status (typically "PENDING")
|
|
249
|
+
- `created_at` (datetime): Timestamp of creation
|
|
250
|
+
|
|
251
|
+
**Raises:**
|
|
252
|
+
|
|
253
|
+
| Exception | Condition |
|
|
254
|
+
|-----------|-----------|
|
|
255
|
+
| `BridgeAPIError` | API request fails (network, auth, server errors) |
|
|
256
|
+
| `ValueError` | Missing required credentials or invalid parameters |
|
|
257
|
+
| `ValidationError` | Invalid source configuration or column mappings |
|
|
258
|
+
|
|
259
|
+
**Example Usage:**
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from bridge_tap_api.sdk.bridge import BridgeAPIClient, BridgeApiConfig
|
|
263
|
+
from bridge_tap_api.sdk.bridge.models import TargetType, MatchSource
|
|
264
|
+
|
|
265
|
+
config = BridgeApiConfig(account_id="12345", api_key="key_abc")
|
|
266
|
+
client = BridgeAPIClient(config)
|
|
267
|
+
|
|
268
|
+
# Build match-based audience
|
|
269
|
+
source = MatchSource(
|
|
270
|
+
s3_uri="s3://my-bucket/audience.csv",
|
|
271
|
+
column_mappings={
|
|
272
|
+
"email": "user_email",
|
|
273
|
+
"first_name": "fname",
|
|
274
|
+
"last_name": "lname",
|
|
275
|
+
},
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
response = client.build_audience(
|
|
279
|
+
target_type=TargetType.INDIVIDUAL,
|
|
280
|
+
source=source,
|
|
281
|
+
name="Q4 Campaign Audience",
|
|
282
|
+
description="Target audience for Q4 email campaign",
|
|
283
|
+
external_id="campaign_q4_2024",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
print(f"Build started with PID: {response.pid}")
|
|
287
|
+
|
|
288
|
+
# Poll for status
|
|
289
|
+
status = client.check_status(response.pid)
|
|
290
|
+
print(f"Current status: {status.state}")
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**See Also:**
|
|
294
|
+
- [check_status()](#check_status) - Poll audience build status
|
|
295
|
+
- [get_audience_details()](#get_audience_details) - Retrieve final results
|
|
296
|
+
- [MatchSource](models.md#matchsource) - File-based source configuration
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Improvements:**
|
|
300
|
+
- Complete method signature with types
|
|
301
|
+
- Comprehensive parameter table
|
|
302
|
+
- Return value structure documented
|
|
303
|
+
- All exceptions with conditions
|
|
304
|
+
- Multiple usage examples
|
|
305
|
+
- Cross-references to related methods
|
|
306
|
+
- Professional formatting
|
|
307
|
+
|
|
308
|
+
## Common Mistakes and Corrections
|
|
309
|
+
|
|
310
|
+
### Mistake 1: Implicit Behavior
|
|
311
|
+
|
|
312
|
+
❌ **Poor:**
|
|
313
|
+
```python
|
|
314
|
+
def process(data=None):
|
|
315
|
+
"""Process data or use default."""
|
|
316
|
+
pass
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
✅ **Good:**
|
|
320
|
+
```python
|
|
321
|
+
def process(data: dict[str, Any] | None = None, *, use_default: bool = False) -> Result:
|
|
322
|
+
"""Process data with explicit configuration.
|
|
323
|
+
|
|
324
|
+
:param data: Input data dictionary, or None to skip processing
|
|
325
|
+
:param use_default: If True, use default configuration when data is None
|
|
326
|
+
:returns: Processing result with status and metadata
|
|
327
|
+
:raises ValueError: If data is None and use_default is False
|
|
328
|
+
"""
|
|
329
|
+
pass
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Mistake 2: Missing Context
|
|
333
|
+
|
|
334
|
+
❌ **Poor:**
|
|
335
|
+
```python
|
|
336
|
+
def validate(file):
|
|
337
|
+
"""Validate file."""
|
|
338
|
+
pass
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
✅ **Good:**
|
|
342
|
+
```python
|
|
343
|
+
def validate_client_file(
|
|
344
|
+
client_name: str,
|
|
345
|
+
file_path: Path,
|
|
346
|
+
) -> ValidationResult:
|
|
347
|
+
"""Validate client file against configured column requirements.
|
|
348
|
+
|
|
349
|
+
Checks that the file contains all required columns defined in the
|
|
350
|
+
client's validation configuration and that column types match expectations.
|
|
351
|
+
|
|
352
|
+
:param client_name: Client identifier (matches clients/ directory name)
|
|
353
|
+
:param file_path: Path to CSV or Parquet file to validate
|
|
354
|
+
:returns: ValidationResult with is_valid flag and list of errors
|
|
355
|
+
:raises FileNotFoundError: If file_path does not exist
|
|
356
|
+
:raises ConfigurationError: If client has no validation config
|
|
357
|
+
"""
|
|
358
|
+
pass
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Mistake 3: Vague Error Documentation
|
|
362
|
+
|
|
363
|
+
❌ **Poor:**
|
|
364
|
+
```markdown
|
|
365
|
+
**Raises:**
|
|
366
|
+
- Exception: If something goes wrong
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
✅ **Good:**
|
|
370
|
+
```markdown
|
|
371
|
+
**Raises:**
|
|
372
|
+
|
|
373
|
+
| Exception | Condition |
|
|
374
|
+
|-----------|-----------|
|
|
375
|
+
| `FileNotFoundError` | The specified file_path does not exist |
|
|
376
|
+
| `ConfigurationError` | Client validation config is missing or invalid |
|
|
377
|
+
| `ValidationError` | File is missing required columns or has type mismatches |
|
|
378
|
+
| `S3Error` | Cannot access S3 file (permissions, network, invalid URI) |
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Mistake 4: Untested Code Examples
|
|
382
|
+
|
|
383
|
+
❌ **Poor:**
|
|
384
|
+
```python
|
|
385
|
+
# Untested, may not work
|
|
386
|
+
client.do_something(param)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
✅ **Good:**
|
|
390
|
+
```python
|
|
391
|
+
# Tested, runnable example
|
|
392
|
+
from bridge_tap_api.sdk.bridge import BridgeAPIClient, BridgeApiConfig
|
|
393
|
+
|
|
394
|
+
config = BridgeApiConfig(account_id="123", api_key="key")
|
|
395
|
+
client = BridgeAPIClient(config)
|
|
396
|
+
|
|
397
|
+
result = client.build_audience(
|
|
398
|
+
target_type=TargetType.INDIVIDUAL,
|
|
399
|
+
source=source,
|
|
400
|
+
name="Example Audience",
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
assert result.pid is not None
|
|
404
|
+
print(f"Success: {result.pid}")
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Inline Comments
|
|
408
|
+
|
|
409
|
+
### ❌ Poor Inline Comments
|
|
410
|
+
|
|
411
|
+
```python
|
|
412
|
+
# Get the data
|
|
413
|
+
data = load_data()
|
|
414
|
+
|
|
415
|
+
# Process it
|
|
416
|
+
result = process(data)
|
|
417
|
+
|
|
418
|
+
# Check if valid
|
|
419
|
+
if result:
|
|
420
|
+
# Do something
|
|
421
|
+
save(result)
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
**Problems:**
|
|
425
|
+
- Comments just restate code
|
|
426
|
+
- No explanation of WHY
|
|
427
|
+
- No context about business logic
|
|
428
|
+
|
|
429
|
+
### ✅ Good Inline Comments
|
|
430
|
+
|
|
431
|
+
```python
|
|
432
|
+
# Load client configuration to determine required columns.
|
|
433
|
+
# This must happen before validation to catch config errors early.
|
|
434
|
+
config = load_client_config(client_name)
|
|
435
|
+
|
|
436
|
+
# Bridge API requires email + (first_name or last_name) minimum.
|
|
437
|
+
# Validate before expensive S3 upload to fail fast.
|
|
438
|
+
if not has_required_columns(data, config.required_columns):
|
|
439
|
+
raise ValidationError("Missing required identity columns")
|
|
440
|
+
|
|
441
|
+
# Use household targeting for B2B campaigns (decision per requirements doc).
|
|
442
|
+
target_type = TargetType.HOUSEHOLD if config.is_b2b else TargetType.INDIVIDUAL
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**Improvements:**
|
|
446
|
+
- Explains WHY, not WHAT
|
|
447
|
+
- Provides business context
|
|
448
|
+
- References requirements/decisions
|
|
449
|
+
- Explains non-obvious choices
|