 
 """
 	
-	packageInformation = {}
-	for filename, {commentLines} of sources
-		
-		# } Get the type and name of this package (if any).
-		packageTypeAndName = ///
-
-^(?:
-	(server)\/packages\/([^/]+)
-	|
-	(client)\/modules\/packages\/([^/]+)
-)
-
-///
-		
-		continue unless (matches = filename.match packageTypeAndName)?
-		
-		type = matches[1] ? matches[3]
-		name = matches[2] ? matches[4]
-		
-		packageInformation[type] ?= {}
-		
-		# } Only do this once for each package.
-		continue if packageInformation[type][name]?
-		
-		packageInformation[type][name] =
-			
-			description: ''
-			filename: filename
-		
-		# } Nothing to do if there are no comments.
-		continue unless commentLines.length > 0
-		
-		# } Jump to the second comment.
-		index = 0
-		index += 1 while commentLines[index] is ''
-		continue if index is commentLines.length
-		index += 2
-		continue if index is commentLines.length
-		
-		# } Get everything until the comment ends as the package description.
-		description = ''
-		while (lookaheadLine = commentLines[index]) isnt '' and lookaheadLine?
-			matches = lookaheadLine.match /^\#\s(.*)$/
-			description += matches[1].trim() + ' '
-			
-			index += 1
-			
-		continue if '' is description = description.trim()
-		packageInformation[type][name].description = description
+	for type, package_ of packageInformation
+		for name, {filename} of package_
+			{commentLines} = sources[filename]
+	
+			# } Nothing to do if there are no comments.
+			continue unless commentLines.length > 0
+			
+			# } Jump to the second comment.
+			index = 0
+			index += 1 while commentLines[index] is ''
+			continue if index is commentLines.length
+			index += 2
+			continue if index is commentLines.length
+			
+			# } Get everything until the comment ends as the package
+			# } description.
+			description = ''
+			while true
+				break if (lookaheadLine = commentLines[index]) is ''
+				break unless lookaheadLine?
+
+				matches = lookaheadLine.match /^\#\s(.*)$/
+				description += matches[1].trim() + ' '
+				
+				index += 1
+			
+			packageInformation[type][name].description = description.trim()
 			
 	# } Output the package information.
 	for type in ['client', 'server']

		
		# Your code goes here...

A dynamically generated listing of hooks follows.


"""
	
	hookInformation = {}
	for filename, {commentLines} of sources

					description: description
					name: hookName
	
	# } 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 += "* ##[#{
