Flesh out the README.
diff --git a/README.md b/README.md
index 4e7484c..0f59c66 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,119 @@
 A library for testing subprocesses.
+
+This exposes a [`TestProcess`][TestProcess] class that wraps `dart:io`'s
+[`Process`][Process] class and makes it easy to read standard output
+line-by-line. `TestProcess` works the same as `Process` in many ways, but there
+are a few major differences.
+
+[TestProcess]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess-class.html
+[Process]: https://api.dartlang.org/stable/latest/dart-io/Process-class.html
+
+## Standard Output
+
+`Process.stdout` and `Process.stderr` are binary streams, which is the most
+general API but isn't the most helpful when working with a program that produces
+plain text. Instead, [`TestProcess.stdout`][stdout] and
+[`TestProcess.stderr`][stderr] emit a string for each line of output the process
+produces. What's more, they're [`StreamQueue`][StreamQueue]s, which means
+they provide a *pull-based API*. For example:
+
+[stdout]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/stdout.html
+[stderr]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/stderr.html
+[StreamQueue]: https://www.dartdocs.org/documentation/async/latest/async/StreamQueue-class.html
+
+```dart
+import 'package:test/test.dart';
+import 'package:test_process/test_process.dart';
+
+void main() {
+  test("pub get gets dependencies", () async {
+    // TestProcess.start() works just like Process.start() from dart:io.
+    var process = await TestProcess.start("pub", ["get"]);
+
+    // StreamQueue.next returns the next line emitted on standard out.
+    var firstLine = await process.stdout.next;
+    expect(firstLine, equals("Resolving dependencies..."));
+
+    // Each call to StreamQueue.next moves one line further.
+    String next;
+    do {
+      next = await process.stdout.next;
+    } while (next != "Got dependencies!");
+
+    // Assert that the process exits with code 0.
+    await process.shouldExit(0);
+  });
+}
+```
+
+The `test` package's [stream matchers][] have built-in support for
+`StreamQueues`, which makes them perfect for making assertions about a process's
+output. We can use this to clean up the previous example:
+
+[stream matchers]: https://github.com/dart-lang/test#stream-matchers
+
+```dart
+import 'package:test/test.dart';
+import 'package:test_process/test_process.dart';
+
+void main() {
+  test("pub get gets dependencies", () async {
+    var process = await TestProcess.start("pub", ["get"]);
+
+    // Each stream matcher will consume as many lines as it matches from a
+    // StreamQueue, and no more, so it's safe to use them in sequence.
+    await expectLater(process.stdout, emits("Resolving dependencies..."));
+
+    // The emitsThrough matcher matches and consumes any number of lines, as
+    // long as they end with one matching the argument.
+    await expectLater(process.stdout, emitsThrough("Got dependencies!"));
+
+    await process.shouldExit(0);
+  });
+}
+```
+
+If you want to access the standard output streams without consuming any values
+from the queues, you can use the [`stdoutStream()`][stdoutStream] and
+[`stderrStream()`][stderrStream] methods. Each time you call one of these, it
+produces an entirely new stream that replays the corresponding output stream
+from the beginning, regardless of what's already been produced by `stdout`,
+`stderr`, or other calls to the stream method.
+
+[stdoutStream]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/stdoutStream.html
+[stderrStream]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/stderrStream.html
+
+## Signals and Termination
+
+The way signaling works is different from `dart:io` as well. `TestProcess` still
+has a [`kill()`][kill] method, but it defaults to `SIGKILL` on Mac OS and Linux
+to ensure (as best as possible) that processes die without leaving behind
+zombies. If you want to send a particular signal (which is unsupported on
+Windows), you can do so by explicitly calling [`signal()`][signal].
+
+[kill]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/kill.html
+[signal]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/signal.html
+
+In addition to [`exitCode`][exitCode], which works the same as in `dart:io`,
+`TestProcess` also adds a new method named [`shouldExit()`][shouldExit]. This is
+lets tests wait for a process to exit, and (if desired) assert what particular
+exit code it produced.
+
+[exitCode]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/exitCode.html
+[shouldExit]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/shouldExit.html
+
+## Debugging Output
+
+When a test using `TestProcess` fails, it will print all the output produced by
+that process. This makes it much easier to figure out what went wrong and why.
+The debugging output uses a header based on the process's invocation by
+default, but you can pass in custom `description` parameters to
+[`TestProcess.start()`][start] to control the headers.
+
+[start]: https://www.dartdocs.org/documentation/test_process/latest/test_process/TestProcess/start.html
+
+`TestProcess` will also produce debugging output as the test runs if you pass
+`forwardStdio: true` to `TestProcess.start()`. This can be particularly useful
+when you're using an interactive debugger and you want to figure out what a
+process is doing before the test finishes and the normal debugging output is
+printed.