diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -95,6 +95,8 @@
     'ArcanistJSHintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSHintLinterTestCase.php',
     'ArcanistJSONLintLinter' => 'lint/linter/ArcanistJSONLintLinter.php',
     'ArcanistJSONLintLinterTestCase' => 'lint/linter/__tests__/ArcanistJSONLintLinterTestCase.php',
+    'ArcanistJscsLinter' => 'lint/linter/ArcanistJscsLinter.php',
+    'ArcanistJscsLinterTestCase' => 'lint/linter/__tests__/ArcanistJscsLinterTestCase.php',
     'ArcanistLandWorkflow' => 'workflow/ArcanistLandWorkflow.php',
     'ArcanistLesscLinter' => 'lint/linter/ArcanistLesscLinter.php',
     'ArcanistLesscLinterTestCase' => 'lint/linter/__tests__/ArcanistLesscLinterTestCase.php',
@@ -273,6 +275,8 @@
     'ArcanistJSHintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
     'ArcanistJSONLintLinter' => 'ArcanistExternalLinter',
     'ArcanistJSONLintLinterTestCase' => 'ArcanistArcanistLinterTestCase',
+    'ArcanistJscsLinter' => 'ArcanistExternalLinter',
+    'ArcanistJscsLinterTestCase' => 'ArcanistArcanistLinterTestCase',
     'ArcanistLandWorkflow' => 'ArcanistBaseWorkflow',
     'ArcanistLesscLinter' => 'ArcanistExternalLinter',
     'ArcanistLesscLinterTestCase' => 'ArcanistArcanistLinterTestCase',
diff --git a/src/lint/linter/ArcanistJscsLinter.php b/src/lint/linter/ArcanistJscsLinter.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/ArcanistJscsLinter.php
@@ -0,0 +1,154 @@
+<?php
+
+final class ArcanistJscsLinter extends ArcanistExternalLinter {
+
+  private $config;
+  private $preset;
+
+  public function getInfoName() {
+    return 'JSCS';
+  }
+
+  public function getInfoURI() {
+    return 'https://github.com/mdevils/node-jscs';
+  }
+
+  public function getInfoDescription() {
+    return pht('Use `jscs` to detect issues with Javascript source files.');
+  }
+
+  public function getLinterName() {
+    return 'JSCS';
+  }
+
+  public function getLinterConfigurationName() {
+    return 'jscs';
+  }
+
+  public function getDefaultBinary() {
+    return 'jscs';
+  }
+
+  public function getVersion() {
+    list($stdout) = execx('%C --version', $this->getExecutableCommand());
+
+    $matches = array();
+    $regex = '/^(?P<version>\d+\.\d+\.\d+)$/';
+    if (preg_match($regex, $stdout, $matches)) {
+      return $matches['version'];
+    } else {
+      return false;
+    }
+  }
+
+  public function getInstallInstructions() {
+    return pht('Install JSCS using `npm install -g jscs`.');
+  }
+
+  public function shouldExpectCommandErrors() {
+    return true;
+  }
+
+  protected function getMandatoryFlags() {
+    $options = array();
+
+    $options[] = '--reporter=checkstyle';
+    $options[] = '--no-colors';
+
+    if ($this->config) {
+      $options[] = '--config='.$this->config;
+    }
+
+    if ($this->preset) {
+      $options[] = '--preset='.$this->preset;
+    }
+
+    return $options;
+  }
+
+  public function getLinterConfigurationOptions() {
+    $options = array(
+      'jscs.config' => array(
+        'type' => 'optional string',
+        'help' => pht('Pass in a custom jscsrc file path.'),
+      ),
+      'jscs.preset' => array(
+        'type' => 'optional string',
+        'help' => pht('Custom preset.'),
+      ),
+    );
+
+    return $options + parent::getLinterConfigurationOptions();
+  }
+
+  public function setLinterConfigurationValue($key, $value) {
+    switch ($key) {
+      case 'jscs.config':
+        $this->config = $value;
+        return;
+
+      case 'jscs.preset':
+        $this->preset = $value;
+        return;
+    }
+
+    return parent::setLinterConfigurationValue($key, $value);
+  }
+
+  protected function parseLinterOutput($path, $err, $stdout, $stderr) {
+    $report_dom = new DOMDocument();
+    $ok = @$report_dom->loadXML($stdout);
+
+    if (!$ok) {
+      return false;
+    }
+
+    $messages = array();
+    foreach ($report_dom->getElementsByTagName('file') as $file) {
+      foreach ($file->getElementsByTagName('error') as $error) {
+        $message = new ArcanistLintMessage();
+        $message->setPath($path);
+        $message->setLine($error->getAttribute('line'));
+        $message->setChar($error->getAttribute('column'));
+        $message->setCode('JSCS');
+        $message->setDescription($error->getAttribute('message'));
+
+        switch ($error->getAttribute('severity')) {
+          case 'error':
+            $message->setSeverity(ArcanistLintSeverity::SEVERITY_ERROR);
+            break;
+
+          case 'warning':
+            $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+            break;
+
+          default:
+            $message->setSeverity(ArcanistLintSeverity::SEVERITY_ADVICE);
+            break;
+        }
+
+        $messages[] = $message;
+      }
+    }
+
+    if ($err && !$messages) {
+      return false;
+    }
+
+    return $messages;
+  }
+
+  protected function getLintCodeFromLinterConfigurationKey($code) {
+
+    // NOTE: We can't figure out which rule generated each message, so we
+    // can not customize severities.
+    //
+    // See https://github.com/mdevils/node-jscs/issues/224
+
+    throw new Exception(
+      pht(
+        "JSCS does not currently support custom severity levels, because ".
+        "rules can't be identified from messages in output."));
+  }
+
+}
diff --git a/src/lint/linter/__tests__/ArcanistJscsLinterTestCase.php b/src/lint/linter/__tests__/ArcanistJscsLinterTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/ArcanistJscsLinterTestCase.php
@@ -0,0 +1,17 @@
+<?php
+
+final class ArcanistJscsLinterTestCase extends ArcanistArcanistLinterTestCase {
+
+  public function testJscsLinter() {
+    // NOTE: JSCS will only lint files with a `*.js` extension.
+    //
+    // See https://github.com/mdevils/node-jscs/issues/346
+    $this->assertTrue(true);
+    return;
+
+    $this->executeTestsInDirectory(
+      dirname(__FILE__).'/jscs/',
+      new ArcanistJscsLinter());
+  }
+
+}
diff --git a/src/lint/linter/__tests__/jscs/curly-brace.lint-test b/src/lint/linter/__tests__/jscs/curly-brace.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/jscs/curly-brace.lint-test
@@ -0,0 +1,12 @@
+function foo() {
+  if (true) return 'foo'; else return 'bar';
+}
+~~~~~~~~~~
+error:2:
+~~~~~~~~~~
+~~~~~~~~~~
+{
+  "config": {
+    "jscs.preset": "jquery"
+  }
+}