add a sky_tools init command to create a new project
diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart
new file mode 100644
index 0000000..c077124
--- /dev/null
+++ b/packages/flutter_tools/bin/sky_tools.dart
@@ -0,0 +1,47 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:sky_tools/src/common.dart';
+import 'package:sky_tools/src/init.dart';
+
+void main(List<String> args) {
+  Map<String, CommandHandler> handlers = {};
+
+  ArgParser parser = new ArgParser();
+  parser.addSeparator('options:');
+  parser.addFlag('help',
+      abbr: 'h', negatable: false, help: 'Display this help message.');
+  parser.addSeparator('commands:');
+
+  CommandHandler handler = new InitCommandHandler();
+  parser.addCommand(handler.name, handler.parser);
+  handlers[handler.name] = handler;
+
+  ArgResults results = parser.parse(args);
+
+  if (results['help']) {
+    _printUsage(parser, handlers);
+  } else if (results.command != null) {
+    handlers[results.command.name].processArgResults(results.command);
+  } else {
+    _printUsage(parser, handlers, 'No command specified.');
+    exit(1);
+  }
+}
+
+void _printUsage(ArgParser parser, Map<String, CommandHandler> handlers,
+    [String message]) {
+  if (message != null) {
+    print('${message}\n');
+  }
+  print('usage: sky_tools <command> [arguments]');
+  print('');
+  print(parser.usage);
+  handlers.forEach((String command, CommandHandler handler) {
+    print('  ${command.padRight(10)} ${handler.description}');
+  });
+}
diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart
new file mode 100644
index 0000000..ef5d5fc
--- /dev/null
+++ b/packages/flutter_tools/lib/src/common.dart
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:args/args.dart';
+
+abstract class CommandHandler {
+  final String name;
+  final String description;
+
+  CommandHandler(this.name, this.description);
+
+  ArgParser get parser;
+
+  void processArgResults(ArgResults results);
+
+  void printUsage([String message]) {
+    if (message != null) {
+      print('${message}\n');
+    }
+    print('usage: sky_tools ${name} [arguments]');
+    print('');
+    print(parser.usage);
+  }
+
+  String toString() => name;
+}
diff --git a/packages/flutter_tools/lib/src/init.dart b/packages/flutter_tools/lib/src/init.dart
new file mode 100644
index 0000000..f331bce
--- /dev/null
+++ b/packages/flutter_tools/lib/src/init.dart
@@ -0,0 +1,162 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library sky_tools.init;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:path/path.dart' as p;
+
+import 'common.dart';
+
+class InitCommandHandler extends CommandHandler {
+  InitCommandHandler() : super('init', 'Create a new sky project.');
+
+  ArgParser get parser {
+    ArgParser parser = new ArgParser();
+    parser.addFlag('help',
+        abbr: 'h', negatable: false, help: 'Display this help message.');
+    // parser.addOption('template',
+    //     abbr: 't',
+    //     help: "The template to generate; either sky-simple or sky-full.");
+    parser.addOption('out', abbr: 'o', help: 'The output directory.');
+    parser.addFlag('pub', defaultsTo: true,
+        help: 'Whether to run pub after the project has been created.');
+    return parser;
+  }
+
+  Future processArgResults(ArgResults results) async {
+    if (results['help']) {
+      printUsage();
+      return new Future.value();
+    }
+
+    if (!results.wasParsed('out')) {
+      printUsage('No option specified for the output directory.');
+      return new Future.value();
+    }
+
+    Directory out = new Directory(results['out']);
+
+    // TODO: confirm overwrite with user
+    //if (out.existsSync())
+
+    new SkySimpleTemplate().generateInto(out);
+
+    print('');
+
+    String message = 'All done! To run your application:\n'
+        'cd ${out.path}\n'
+        './packages/sky/sky_tool start';
+
+    if (results['pub']) {
+      print("Running pub get...");
+      Process process = await Process.start('pub', ['get'], workingDirectory: out.path);
+      stdout.addStream(process.stdout);
+      stderr.addStream(process.stderr);
+      int code = await process.exitCode;
+      if (code == 0) {
+        print('\n${message}');
+      }
+    } else {
+      print(message);
+    }
+  }
+}
+
+abstract class Template {
+  final String name;
+  final String description;
+
+  Map<String, String> files = {};
+
+  Template(this.name, this.description);
+
+  void generateInto(Directory dir) {
+    String projectName = _normalizeProjectName(p.basename(dir.path));
+    print('Creating ${p.basename(projectName)}...');
+    dir.createSync(recursive: true);
+
+    files.forEach((String path, String contents) {
+      contents = contents
+          .replaceAll('{{projectName}}', projectName)
+          .replaceAll('{{description}}', description);
+      File file = new File(p.join(dir.path, path));
+      file.parent.createSync();
+      file.writeAsStringSync(contents);
+      print(file.path);
+    });
+  }
+
+  String toString() => name;
+}
+
+class SkySimpleTemplate extends Template {
+  SkySimpleTemplate() : super('sky-simple', 'A minimal Sky project.') {
+    files['.gitignore']= _gitignore;
+    files['pubspec.yaml']= _pubspec;
+    files['README.md']= _readme;
+    files['lib/main.dart']= _libMain;
+  }
+}
+
+String _normalizeProjectName(String name) {
+  name = name.replaceAll('-', '_').replaceAll(' ', '_');
+  // Strip any extension (like .dart).
+  if (name.contains('.')) {
+    name = name.substring(0, name.indexOf('.'));
+  }
+  return name;
+}
+
+const _gitignore = r'''
+.DS_Store
+.idea
+.packages
+.pub/
+build/
+packages
+pubspec.lock
+''';
+
+const _readme = r'''
+# {{projectName}}
+
+{{description}}
+
+## Getting Started
+
+For help getting started, view our online
+[readme](https://github.com/domokit/sky_engine/blob/master/sky/packages/sky/README.md).
+''';
+
+const _pubspec = r'''
+name: {{projectName}}
+description: {{description}}
+dependencies:
+  sky: any
+  sky_tools: any
+''';
+
+const _libMain = r'''
+import 'package:sky/widgets.dart';
+
+class HelloWorldApp extends App {
+  Widget build() {
+    return new Scaffold(
+      toolbar: new ToolBar(center: new Text("Demo")),
+      body: new Material(child: new Center(child: new Text("Hello world!"))),
+      floatingActionButton: new FloatingActionButton(
+        child: new Icon(type: 'content/add', size: 24)
+      )
+    );
+  }
+}
+
+void main() {
+  runApp(new HelloWorldApp());
+}
+''';
diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml
index 2d84548..8915f9e 100644
--- a/packages/flutter_tools/pubspec.yaml
+++ b/packages/flutter_tools/pubspec.yaml
@@ -9,5 +9,12 @@
 
 dependencies:
   args: ^0.13.0
+  path: ^1.3.0
   shelf: ^0.6.2
   shelf_static: ^0.2.3
+
+dev_dependencies:
+  test: ^0.12.0
+
+executable:
+  sky_tools:
diff --git a/packages/flutter_tools/test/init_test.dart b/packages/flutter_tools/test/init_test.dart
new file mode 100644
index 0000000..66340eb
--- /dev/null
+++ b/packages/flutter_tools/test/init_test.dart
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:args/args.dart';
+import 'package:path/path.dart' as p;
+import 'package:sky_tools/src/init.dart';
+import 'package:test/test.dart';
+
+main() => defineTests();
+
+defineTests() {
+  group('', () {
+    Directory temp;
+
+    setUp(() {
+      temp = Directory.systemTemp.createTempSync('sky_tools');
+    });
+
+    tearDown(() {
+      temp.deleteSync(recursive: true);
+    });
+
+    // Verify that we create a project that os well-formed.
+    test('init sky-simple', () async {
+      InitCommandHandler handler = new InitCommandHandler();
+      _MockArgResults results = new _MockArgResults();
+      results.values['help'] = false;
+      results.values['pub'] = true;
+      results.values['out'] = temp.path;
+      await handler.processArgResults(results);
+      String path = p.join(temp.path, 'lib/main.dart');
+      print(path);
+      expect(new File(path).existsSync(), true);
+      ProcessResult exec = Process.runSync('dartanalyzer', [path],
+          workingDirectory: temp.path);
+      expect(exec.exitCode, 0);
+    });
+  });
+}
+
+class _MockArgResults implements ArgResults {
+  Map values = {};
+  operator [](String name) => values[name];
+  List<String> get arguments => null;
+  ArgResults get command => null;
+  String get name => null;
+  Iterable<String> get options => values.keys;
+  List<String> get rest => null;
+  bool wasParsed(String name) => values.containsKey(name);
+}
diff --git a/packages/flutter_tools/tool/travis.sh b/packages/flutter_tools/tool/travis.sh
index f7b4013..c435e02 100755
--- a/packages/flutter_tools/tool/travis.sh
+++ b/packages/flutter_tools/tool/travis.sh
@@ -10,4 +10,5 @@
 # Verify that the libraries are error free.
 dartanalyzer --fatal-warnings \
   bin/build_sky_apk.dart \
-  bin/sky_server.dart
+  bin/sky_server.dart \
+  bin/sky_tools.dart