Initial sketch of install command and what devices look like.
diff --git a/packages/flutter_tools/bin/sky_tools.dart b/packages/flutter_tools/bin/sky_tools.dart
index e5b165a..db52175 100644
--- a/packages/flutter_tools/bin/sky_tools.dart
+++ b/packages/flutter_tools/bin/sky_tools.dart
@@ -7,6 +7,7 @@
 import 'package:args/args.dart';
 import 'package:sky_tools/src/common.dart';
 import 'package:sky_tools/src/init.dart';
+import 'package:sky_tools/src/install.dart';
 
 void main(List<String> args) {
   Map<String, CommandHandler> handlers = {};
@@ -17,9 +18,13 @@
       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;
+  for (CommandHandler handler in [
+    new InitCommandHandler(),
+    new InstallCommandHandler()
+  ]) {
+    parser.addCommand(handler.name, handler.parser);
+    handlers[handler.name] = handler;
+  }
 
   ArgResults results;
 
@@ -33,7 +38,14 @@
   if (results['help']) {
     _printUsage(parser, handlers);
   } else if (results.command != null) {
-    handlers[results.command.name].processArgResults(results.command);
+    handlers[results.command.name]
+        .processArgResults(results.command)
+        .then((int code) => exit(code))
+        .catchError((e, stack) {
+      print('Error running ' + results.command.name + ': $e');
+      print(stack);
+      exit(2);
+    });
   } else {
     _printUsage(parser, handlers, 'No command specified.');
     exit(1);
diff --git a/packages/flutter_tools/lib/src/common.dart b/packages/flutter_tools/lib/src/common.dart
index 9fc6d19..c3e1ba1 100644
--- a/packages/flutter_tools/lib/src/common.dart
+++ b/packages/flutter_tools/lib/src/common.dart
@@ -14,6 +14,7 @@
 
   ArgParser get parser;
 
+  /// @return 0 for no errors or warnings executing command, 1 for warnings, 2 for errors.
   Future<int> processArgResults(ArgResults results);
 
   void printUsage([String message]) {
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
new file mode 100644
index 0000000..3167abb
--- /dev/null
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -0,0 +1,67 @@
+// 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.device;
+
+abstract class _Device {
+  final String id;
+  static Map<String, _Device> _deviceCache = {};
+
+  factory _Device(String className, [String id = null]) {
+    if (id == null) {
+      if (className == AndroidDevice.className) {
+        id = AndroidDevice.defaultDeviceID;
+      } else {
+        throw 'Attempted to create a Device of unknown type $className';
+      }
+    }
+
+    return _deviceCache.putIfAbsent(id, () {
+      if (className == AndroidDevice.className) {
+        final device = new AndroidDevice._(id);
+        _deviceCache[id] = device;
+        return device;
+      } else {
+        throw 'Attempted to create a Device of unknown type $className';
+      }
+    });
+  }
+
+  _Device._(this.id);
+
+  /// Install an app package on the current device
+  bool installApp(String path);
+
+  /// Check if the current device needs an installation
+  bool needsInstall();
+
+  /// Check if the device is currently connected
+  bool isConnected();
+}
+
+class AndroidDevice extends _Device {
+  static const String className = 'AndroidDevice';
+  static final String defaultDeviceID = 'default';
+
+  factory AndroidDevice([String id = null]) {
+    return new _Device(className, id);
+  }
+
+  AndroidDevice._(id) : super._(id);
+
+  @override
+  bool installApp(String path) {
+    return false;
+  }
+
+  @override
+  bool needsInstall() {
+    return true;
+  }
+
+  @override
+  bool isConnected() {
+    return true;
+  }
+}
diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart
new file mode 100644
index 0000000..1a89dee
--- /dev/null
+++ b/packages/flutter_tools/lib/src/install.dart
@@ -0,0 +1,46 @@
+// 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.install;
+
+import 'dart:async';
+
+import 'package:args/args.dart';
+
+import 'common.dart';
+import 'device.dart';
+
+class InstallCommandHandler extends CommandHandler {
+  InstallCommandHandler()
+      : super('install', 'Install your Sky app on attached devices.');
+
+  @override
+  ArgParser get parser {
+    ArgParser parser = new ArgParser();
+    parser.addFlag('help',
+        abbr: 'h', negatable: false, help: 'Display this help message.');
+    return parser;
+  }
+
+  @override
+  Future<int> processArgResults(ArgResults results) async {
+    if (results['help']) {
+      printUsage();
+      return 0;
+    }
+
+    bool installedSomewhere = false;
+
+    AndroidDevice android = new AndroidDevice();
+    if (android.isConnected()) {
+      installedSomewhere = installedSomewhere || android.installApp('');
+    }
+
+    if (installedSomewhere) {
+      return 0;
+    } else {
+      return 2;
+    }
+  }
+}