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
@@ -1019,7 +1019,7 @@
     'ArcanistBrowseRevisionURIHardpointLoader' => 'ArcanistBrowseURIHardpointLoader',
     'ArcanistBrowseURIHardpointLoader' => 'ArcanistHardpointLoader',
     'ArcanistBrowseURIRef' => 'ArcanistRef',
-    'ArcanistBrowseWorkflow' => 'ArcanistWorkflow',
+    'ArcanistBrowseWorkflow' => 'ArcanistArcWorkflow',
     'ArcanistBuildPlanRef' => 'Phobject',
     'ArcanistBuildRef' => 'Phobject',
     'ArcanistBundle' => 'Phobject',
@@ -1137,7 +1137,7 @@
     'ArcanistExternalLinterTestCase' => 'ArcanistLinterTestCase',
     'ArcanistExtractUseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
     'ArcanistExtractUseXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
-    'ArcanistFeatureWorkflow' => 'ArcanistWorkflow',
+    'ArcanistFeatureWorkflow' => 'ArcanistArcWorkflow',
     'ArcanistFileConfigurationSource' => 'ArcanistFilesystemConfigurationSource',
     'ArcanistFileDataRef' => 'Phobject',
     'ArcanistFileUploader' => 'Phobject',
diff --git a/src/browse/workflow/ArcanistBrowseWorkflow.php b/src/browse/workflow/ArcanistBrowseWorkflow.php
--- a/src/browse/workflow/ArcanistBrowseWorkflow.php
+++ b/src/browse/workflow/ArcanistBrowseWorkflow.php
@@ -3,70 +3,62 @@
 /**
  * Browse files or objects in the Phabricator web interface.
  */
-final class ArcanistBrowseWorkflow extends ArcanistWorkflow {
+final class ArcanistBrowseWorkflow
+  extends ArcanistArcWorkflow {
 
   public function getWorkflowName() {
     return 'browse';
   }
 
-  public function getCommandSynopses() {
-    return phutil_console_format(<<<EOTEXT
-      **browse** [__options__] __path__ ...
-      **browse** [__options__] __object__ ...
-EOTEXT
-      );
-  }
+  public function getWorkflowInformation() {
+    $help = pht(<<<EOTEXT
+Open a file or object (like a task or revision) in a local web browser.
 
-  public function getCommandHelp() {
-    return phutil_console_format(<<<EOTEXT
-          Supports: git, hg, svn
-          Open a file or object (like a task or revision) in your web browser.
+  $ arc browse README   # Open a file in Diffusion.
+  $ arc browse T123     # View a task.
+  $ arc browse HEAD     # View a symbolic commit.
 
-            $ arc browse README   # Open a file in Diffusion.
-            $ arc browse T123     # View a task.
-            $ arc browse HEAD     # View a symbolic commit.
+To choose a browser binary to invoke, use:
 
-          Set the 'browser' value using 'arc set-config' to select a browser. If
-          no browser is set, the command will try to guess which browser to use.
+  $ arc set-config browser __browser-binary__
+
+If no browser is set, the command will try to guess which browser to use.
 EOTEXT
       );
+
+    return $this->newWorkflowInformation()
+      ->setSynopsis(pht('Open a file or object in a local web browser.'))
+      ->addExample('**browse** [options] -- __target__ ...')
+      ->addExample('**browse** -- __file-name__')
+      ->addExample('**browse** -- __object-name__')
+      ->setHelp($help);
   }
 
-  public function getArguments() {
+  public function getWorkflowArguments() {
     return array(
-      'branch' => array(
-        'param' => 'branch_name',
-        'help' => pht(
-          'Default branch name to view on server. Defaults to "%s".',
-          'master'),
-      ),
-      'types' => array(
-        'param' => 'types',
-        'aliases' => array('type'),
-        'help' => pht(
-          'Parse arguments with particular types.'),
-      ),
-      'force' => array(
-        'help' => pht(
-          '(DEPRECATED) Obsolete, use "--types path" instead.'),
-      ),
-      '*' => 'targets',
+      $this->newWorkflowArgument('branch')
+        ->setParameter('branch-name')
+        ->setHelp(
+          pht(
+            'Default branch name to view on server. Defaults to "%s".',
+            'master')),
+      $this->newWorkflowArgument('types')
+        ->setParameter('type-list')
+        ->setHelp(
+          pht(
+            'Force targets to be interpreted as naming particular types of '.
+            'resources.')),
+      $this->newWorkflowArgument('force')
+        ->setHelp(
+          pht(
+            '(DEPRECATED) Obsolete, use "--types path" instead.')),
+      $this->newWorkflowArgument('targets')
+        ->setIsPathArgument(true)
+        ->setWildcard(true),
     );
   }
 
-  public function desiresWorkingCopy() {
-    return true;
-  }
-
-  public function desiresRepositoryAPI() {
-    return true;
-  }
-
-  public function run() {
-    $conduit = $this->getConduitEngine();
-
-    $console = PhutilConsole::getConsole();
-
+  public function runWorkflow() {
     $targets = $this->getArgument('targets');
     $targets = array_fuse($targets);
 
diff --git a/src/workflow/ArcanistFeatureWorkflow.php b/src/workflow/ArcanistFeatureWorkflow.php
--- a/src/workflow/ArcanistFeatureWorkflow.php
+++ b/src/workflow/ArcanistFeatureWorkflow.php
@@ -1,11 +1,7 @@
 <?php
 
-/**
- * Displays user's Git branches or Mercurial bookmarks.
- *
- * @concrete-extensible
- */
-class ArcanistFeatureWorkflow extends ArcanistWorkflow {
+class ArcanistFeatureWorkflow
+  extends ArcanistArcWorkflow {
 
   private $branches;
 
@@ -13,62 +9,51 @@
     return 'feature';
   }
 
-  public function getCommandSynopses() {
-    return phutil_console_format(<<<EOTEXT
-      **feature** [__options__]
-      **feature** __name__ [__start__]
-EOTEXT
-      );
-  }
-
-  public function getCommandHelp() {
-    return phutil_console_format(<<<EOTEXT
-          Supports: git, hg
-          A wrapper on 'git branch' or 'hg bookmark'.
-
-          Without __name__, it lists the available branches and their revision
-          status.
-
-          With __name__, it creates or checks out a branch. If the branch
-          __name__ doesn't exist and is in format D123 then the branch of
-          revision D123 is checked out. Use __start__ to specify where the new
-          branch will start. Use 'arc.feature.start.default' to set the default
-          feature start location.
-EOTEXT
-      );
-  }
-
-  public function requiresRepositoryAPI() {
-    return true;
-  }
-
-  public function getArguments() {
+  public function getWorkflowArguments() {
     return array(
-      'view-all' => array(
-        'help' => pht('Include closed and abandoned revisions.'),
-      ),
-      'by-status' => array(
-        'help' => pht('Sort branches by status instead of time.'),
-      ),
-      'output' => array(
-        'param' => 'format',
-        'support' => array(
-          'json',
-        ),
-        'help' => pht(
-          "With '%s', show features in machine-readable JSON format.",
-          'json'),
-      ),
-      '*' => 'branch',
+      $this->newWorkflowArgument('view-all')
+        ->setHelp(pht('Include closed and abandoned revisions.')),
+      $this->newWorkflowArgument('by-status')
+        ->setParameter('status')
+        ->setHelp(pht('Sort branches by status instead of time.')),
+      $this->newWorkflowArgument('output')
+        ->setParameter('format')
+        ->setHelp(
+          pht(
+            'With "json", show features in machine-readable JSON format.')),
+      $this->newWorkflowArgument('branch')
+        ->setWildcard(true),
     );
   }
 
-  public function getSupportedRevisionControlSystems() {
-    return array('git', 'hg');
+  public function getWorkflowInformation() {
+    return $this->newWorkflowInformation()
+      ->setSynopsis(pht('Wrapper on "git branch" or "hg bookmark".'))
+      ->addExample(pht('**feature** [__options__]'))
+      ->addExample(pht('**feature** __name__ [__start__]'))
+      ->setHelp(
+        pht(<<<EOHELP
+A wrapper on 'git branch' or 'hg bookmark'.
+
+Without __name__, it lists the available branches and their revision status.
+
+With __name__, it creates or checks out a branch. If the branch __name__
+doesn't exist and is in format D123 then the branch of revision D123 is
+checked out. Use __start__ to specify where the new branch will start. Use
+'arc.feature.start.default' to set the default feature start location.
+EOHELP
+          ));
   }
 
-  public function run() {
+  public function runWorkflow() {
+    $working_copy = $this->getWorkingCopy();
+
     $repository_api = $this->getRepositoryAPI();
+    if (!$repository_api) {
+      throw new PhutilArgumentUsageException(
+        pht(
+          'This command must be run in a Git or Mercurial working copy.'));
+    }
 
     $names = $this->getArgument('branch');
     if ($names) {
diff --git a/src/workflow/ArcanistWorkflow.php b/src/workflow/ArcanistWorkflow.php
--- a/src/workflow/ArcanistWorkflow.php
+++ b/src/workflow/ArcanistWorkflow.php
@@ -336,6 +336,16 @@
   }
 
   final public function getConfigFromAnySource($key) {
+    $source_list = $this->getConfigurationSourceList();
+    if ($source_list) {
+      $value_list = $source_list->getStorageValueList($key);
+      if ($value_list) {
+        return last($value_list)->getValue();
+      }
+
+      return null;
+    }
+
     return $this->configurationManager->getConfigFromAnySource($key);
   }
 
@@ -884,6 +894,14 @@
   }
 
   final public function getWorkingCopy() {
+    $configuration_engine = $this->getConfigurationEngine();
+    if ($configuration_engine) {
+      $working_copy = $configuration_engine->getWorkingCopy();
+      $working_path = $working_copy->getWorkingDirectory();
+
+      return ArcanistWorkingCopyIdentity::newFromPath($working_path);
+    }
+
     $working_copy = $this->getConfigurationManager()->getWorkingCopyIdentity();
     if (!$working_copy) {
       $workflow = get_class($this);
@@ -911,6 +929,12 @@
   }
 
   final public function getRepositoryAPI() {
+    $configuration_engine = $this->getConfigurationEngine();
+    if ($configuration_engine) {
+      $working_copy = $configuration_engine->getWorkingCopy();
+      return $working_copy->getRepositoryAPI();
+    }
+
     if (!$this->repositoryAPI) {
       $workflow = get_class($this);
       throw new Exception(
@@ -2232,7 +2256,7 @@
       $query->setRepositoryRef($repository_ref);
     }
 
-    $working_copy = $this->getConfigurationManager()->getWorkingCopyIdentity();
+    $working_copy = $this->getWorkingCopy();
     if ($working_copy) {
       $working_ref = $this->newWorkingCopyStateRef();
       $query->setWorkingCopyRef($working_ref);
@@ -2242,12 +2266,17 @@
   }
 
   final public function getRepositoryRef() {
-    if (!$this->getConfigurationManager()->getWorkingCopyIdentity()) {
-      return null;
-    }
+    $configuration_engine = $this->getConfigurationEngine();
+    if ($configuration_engine) {
+      // This is a toolset workflow and can always build a repository ref.
+    } else {
+      if (!$this->getConfigurationManager()->getWorkingCopyIdentity()) {
+        return null;
+      }
 
-    if (!$this->repositoryAPI) {
-      return null;
+      if (!$this->repositoryAPI) {
+        return null;
+      }
     }
 
     if (!$this->repositoryRef) {