when-words
Human-friendly date and time formatting as pure functions. Converts timestamps to relative phrases ("3 hours ago"), duration strings ("2 hours, 30 minutes"), parses duration input ("2h30m" to seconds), generates contextual date labels ("Yesterday"), and formats smart date ranges ("January 15 -- February 15, 2024").
Design principles
- Pure functions only — every function takes explicit timestamps or values; no function ever accesses the system clock.
- UTC throughout — all calendar math uses UTC. No timezone handling.
- No inter-node dependencies — each node is a standalone leaf function.
- Clarity over performance — reference code prioritizes readability.
Node graph
All 5 nodes are leaf nodes with no inter-node dependencies:
time-ago (standalone)
duration (standalone)
parse-duration (standalone)
human-date (standalone)
date-range (standalone)
Node table
| Node | Function | Purpose | Tests |
|---|---|---|---|
time-ago | timeAgo(timestamp, reference) | Relative time string ("3 hours ago", "in 2 days") | 35 |
duration | duration(seconds, options?) | Format seconds as human duration ("2 hours, 30 minutes") | 22 |
parse-duration | parseDuration(input) | Parse duration string to seconds ("2h30m" to 9000) | 24 |
human-date | humanDate(timestamp, reference) | Contextual date label ("Yesterday", "March 5") | 18 |
date-range | dateRange(start, end) | Smart date range ("January 15--22, 2024") | 9 |
Total: 5 nodes, ~108 tests, 100% line and function coverage.
Subset extraction
Every node is independent — any single node or combination works alone without pulling in other nodes. Common subsets:
- time-ago only — relative timestamps for feeds, notifications
- duration + parse-duration — bidirectional duration formatting
- human-date + date-range — calendar/scheduling UI labels
- all — full library
Input format
The skill accepts $ARGUMENTS in this format:
<nodes> [--lang <language>]
<nodes>— Space-separated node names, orallfor the complete library. Valid names:time-ago,duration,parse-duration,human-date,date-range.--lang <language>— Target language (default:typescript). Supported:python,rust,go,typescript.
Examples:
time-ago --lang pythonduration parse-duration --lang rustall --lang go
Translation workflow
- Read this file for overview and node selection
- Read
nodes/<name>/spec.mdfor behavioral spec and test vectors - Read
nodes/<name>/to-<lang>.mdfor language-specific translation hints - Consult
reference/src/<name>.tsonly if the spec is ambiguous
Generated Code Documentation
Every public function, class, type, and interface in generated code must have idiomatic doc comments in the target language's standard format:
| Language | Format |
|---|---|
| TypeScript | JSDoc (/** */) with @param, @returns |
| Python | Google-style docstrings with Args, Returns, Raises |
| Kotlin/Java | KDoc/JavaDoc (/** */) with @param, @return, @throws |
| C# | XML doc comments (///) with <summary>, <param>, <returns> |
| Go | GoDoc comments (starting with the function/type name) |
| Rust | /// doc comments with # Arguments, # Returns, # Errors |
| C++ | Doxygen (/** or ///) with @brief, @param, @return |
| Swift | DocC (///) with - Parameters:, - Returns:, - Throws: |
Doc comments should describe what the function does, its parameters, return value, and error conditions. Derive content from the node spec — do not invent behavior not in the spec.
Each generated file must include a provenance header as the first comment, in the file's idiomatic comment style:
Generated by {agent} using {model}
From special:when-words (https://github.com/caryden/special)
Node: {node-name}
Replace {agent}, {model}, and {node-name} with actual values. The
provenance trace makes generated code traceable back to the skill and model
that produced it.
Error handling
durationthrows on negative inputparseDurationthrows on: empty string, unrecognized input, negative values, bare numbers without units, unrecognized unit namestimeAgo,humanDate,dateRangeare total functions (no error cases)dateRangeauto-swaps if start > end
Reference info
- Language: TypeScript (Bun runtime)
- Coverage: 100% line and function coverage
- Test runner:
bun test - No external dependencies
