Skip to main content

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