 					description: description
 					name: hookName
 	
-	# } Output the hook information.			
+	# } Output the hook information.
 	alphabetical = Object.keys(hookInformation).sort()
 	for filename in alphabetical
 		hooks = hookInformation[filename]
+		
+		# } Top-level list: filenames
+		markdown += """
+
+[#{filename}](./#{filename.replace /(coffee|js)/, 'html'})
+
+"""
+		
+		# } Second-level list: hook names and descriptions.
+		for {name, description} in hooks
+			
+			packages = packagesImplementingHook name
+			
+			markdown += """
+
+* ## `#{name}`
+
+\t### #{description}
+
+"""
+			
+			continue if packages.length is 0
+				
+			for package_ in packages
+
+				{filename} = packageInformation[type][package_.name]
+				markdown += """
+
+\t* <h4><a href="./#{
+	filename.replace /\.(js|coffee)$/, '.html'
+}\#implementshook#{
+	name.toLowerCase()
+}">#{
+	package_.name
+} (#{
+	package_.type
+})</a></h4>
+
+"""
+			
+	fs.writeFileSync "documentation/hooks.md", markdown
+
+# Generate TODO documentation.
+generateHookDocumentation = do ->
+	
+	markdown = """
+
+# TODO list
+
+Shrub -- like any project -- always presents a path for improvement. This is
+a dynamically generated listing of TODO items, each with a line of code
+context.
+
+
+"""
+	
+	todoInformation = {}
+	for filename, {lines} of sources
+		
+		for line, index in lines
+			
+			# } Find the comment with the TODO, and retrieve the description.
+			if matches = line.match /^\#\s(?:}\s+)?`TODO`:\s(.*)$/
+			
+				# } Look ahead until we hit an empty line; all following lines
+				# } until then are the TODO description.
+				description = matches[1] + ' '
+				lookaheadIndex = index + 1
+				while lookaheadLine = lines[lookaheadIndex]
+					break unless (matches = lookaheadLine.match /^\#\s(.*)$/)?
+					
+					description += matches[1].trim() + ' '
+					
+					lookaheadIndex += 1
+				
+				# Look ahead until we find a non-comment. We'll use this as
+				# context for the TODO item.
+				while lines[lookaheadIndex].match /^(\#|$)/
+					lookaheadIndex += 1
+				
+				(todoInformation[filename] ?= []).push
+					
+					context: lines[lookaheadIndex]
+					description: description
+				
+	# } Output the TODO information.
+	alphabetical = Object.keys(todoInformation).sort()
+	for filename in alphabetical
+		todos = todoInformation[filename]
 		
 		# } Top-level list: filenames
 		markdown += "* ##[#{

		raw: raw
		lines: lines
		commentLines: commentLines
	
# Generate hook documentation.
generateHookDocumentation = do ->

			filename.replace /(coffee|js)/, 'html'
		}):\n\n"
		
		# } Second-level list: TODO descriptions.
		for {context, description} in todos
			
