AI Skills
Comment Code Skill
I've been using this skill for a while now and it's one of the more genuinely useful ones I've put together. Drop it on a file you've been meaning to document and it not only writes the comments but forces you to confront whether the naming and structure actually makes sense — which, more often than not, it doesn't.
Using in Claude Code
Save this skill to ~/.claude/skills/comment-code.md. Invoke it in any session with /comment-code.
Using in Kiro
Copy the content into .kiro/steering/comment-code.md in your project. Kiro will pick it up as persistent steering context and apply it when you ask for documentation or annotations.
Skill Definition
---
name: comment-code
description: Add JSDoc-style comments to public methods, public variables, statics, and classes in ColdFusion (Lucee 6) and TypeScript. Comments are written from the business domain perspective — not mechanical code descriptions. Invoke when the user asks to comment, document, or annotate code.
allowed-tools: Read, Edit
---
# Comment Code
Add JSDoc-style documentation to public APIs in ColdFusion (Lucee 6) and TypeScript.
## When to Use
- User asks to comment, document, or annotate a file or code selection
- User asks to add JSDoc / inline docs
- User asks to audit a file for missing public-API documentation
## Core Philosophy
**Document the domain, not the mechanics.** A reader can already see `calculateTax(amount, rate)` — the comment must explain *which* tax, under what jurisdiction, and what business constraints apply; not that it "calculates tax."
| Bad | Good |
|---|---|
| Gets a user by ID | Retrieves the authenticated session owner. Returns `null` for deactivated accounts |
| Sends email | Queues a transactional email via SES. Does not send synchronously — check `EmailQueue` for delivery status |
| Validates the form | Checks that the ABN passes the ATO checksum algorithm and is registered as active |
Never restate the signature. Every word must add something the type and name cannot.
## What to Comment
**Do comment:**
- Public classes and components — their domain responsibility and key invariants
- Public methods and functions
- Public static members
- Exported constants with business significance (rate limits, magic thresholds, status codes)
**Do not comment:**
- Private or internal members (unless the algorithm is genuinely non-obvious — e.g. a non-standard formula)
- Self-evident getters/setters with no constraint
- Test code
## Rules
### Summary line
- First line is one sentence, imperative mood: "Submits", "Validates", "Calculates" — not "This method calculates"
- Describes the domain outcome, not the implementation
- No trailing period (JSDoc convention)
### `@param` / `@return`
- Only document params where there is a constraint, format requirement, or non-obvious behaviour
- Describe domain meaning of return values, not just their type
- Bad: `@param userId — The user ID` / `@returns string`
- Good: `@param userId — UUID from the \`users\` table; must belong to the requesting tenant` / `@returns Signed JWT valid for 15 minutes, or \`null\` if the user lacks the required role`
### `@throws`
- Name the business condition, not just the exception class
- `@throws {AuthorisationError} when the requesting user does not own the resource`
- `@throws {PaymentDeclinedException} when the gateway declines due to insufficient funds`
### `@example`
- Include for utility functions, shared helpers, or any API where correct call-site usage isn't obvious
- One realistic example is enough; keep it minimal
### `@deprecated`
- Always include the migration path
- `@deprecated — use \`paymentService.charge()\` instead`
### Do not use
- `@author`, `@version`, `@since` — git is the source of truth
- `@type` in TypeScript — the compiler already knows
- Redundant `@description` when the summary line covers it
## TypeScript
Use standard JSDoc blocks (`/** */`) immediately above the export:
```typescript
/**
* Authorises a refund against the original transaction
*
* Partial refunds are supported; attempting to refund more than the original
* charge throws a RefundLimitError rather than partially succeeding.
*
* @param transactionId - Stripe payment intent ID from the original charge
* @param amountCents - Must not exceed the captured amount on the transaction
* @returns Updated transaction record reflecting the refund status
* @throws {RefundLimitError} when amountCents exceeds the original charge
* @example
* const tx = await refundTransaction('pi_abc123', 500);
*/
export async function refundTransaction(
transactionId: string,
amountCents: number
): Promise<Transaction> { ... }
````
**Class docs** describe domain responsibility and key collaborators:
```typescript
/**
* Manages subscription lifecycle for Pro and Enterprise plans
*
* Coordinates between inbound Stripe webhooks and the internal entitlements
* system. Does not handle billing retry logic — that lives in BillingRetryJob.
*/
export class SubscriptionService { ... }
```
**Exported constants** with business meaning:
```typescript
/** Maximum failed login attempts before the account is locked for 30 minutes */
export const MAX_AUTH_ATTEMPTS = 5;
```
## ColdFusion (Lucee 6, script syntax only)
Use `/** */` blocks above component and function declarations. Always `output=false` on every function.
```cfscript
/**
* Processes a payment authorisation request via the payment gateway
*
* Idempotent — safe to retry on network failure using the same idempotencyKey.
* Does not capture; call capturePayment() once goods are dispatched.
*
* @param {struct} order - Order struct with lineItems, currency, and buyerRef
* @param {string} idempotencyKey - Caller-supplied key to prevent double charges
* @return {struct} Gateway response containing authCode, status, and gatewayRef
* @throws PaymentDeclinedException when the gateway declines (check response.declineCode)
*/
public struct function authorisePayment(
required struct order,
required string idempotencyKey
) output=false { ... }
```
Component-level doc block:
```cfscript
/**
* Handles outbound dispatch for transactional and marketing emails
*
* All sends are queued asynchronously via SES. Inspect pending sends
* through EmailQueueService — do not assume delivery is immediate.
*/
component accessors=true { ... }
```
Static/shared scope constants with business meaning:
```cfscript
/** Grace period in days after subscription expiry before access is revoked */
variables.SUBSCRIPTION_GRACE_DAYS = 7;
```
## Workflow
1. Read the file to identify all public members lacking documentation
2. For each undocumented member, read enough surrounding context to understand its domain role — check callers, related types, or component usage
3. If the domain intent is genuinely ambiguous, ask before guessing
4. Write the comment — domain first, implementation never
5. Do not modify any code, only add or update doc blocks
6. Do not add comments to private members unless specifically asked