text: module.exports = runScript
  
  var lifecycle = require('./utils/lifecycle.js')
  var npm = require('./npm.js')
  var path = require('path')
  var readJson = require('read-package-json')
  var log = require('npmlog')
  var chain = require('slide').chain
  var usage = require('./utils/usage')
  var output = require('./utils/output.js')
  
  runScript.usage = usage(
    'run-script',
    'npm run-script <command> [-- <args>...]'
  )
  
  runScript.completion = function (opts, cb) {
    // see if there's already a package specified.
    var argv = opts.conf.argv.remain
  
    if (argv.length >= 4) return cb()
  
    if (argv.length === 3) {
      // either specified a script locally, in which case, done,
      // or a package, in which case, complete against its scripts
      var json = path.join(npm.localPrefix, 'package.json')
      return readJson(json, function (er, d) {
        if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
        if (er) d = {}
        var scripts = Object.keys(d.scripts || {})
        console.error('local scripts', scripts)
        if (scripts.indexOf(argv[2]) !== -1) return cb()
        // ok, try to find out which package it was, then
        var pref = npm.config.get('global') ? npm.config.get('prefix')
          : npm.localPrefix
        var pkgDir = path.resolve(pref, 'node_modules', argv[2], 'package.json')
        readJson(pkgDir, function (er, d) {
          if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
          if (er) d = {}
          var scripts = Object.keys(d.scripts || {})
          return cb(null, scripts)
        })
      })
    }
  
    readJson(path.join(npm.localPrefix, 'package.json'), function (er, d) {
      if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
      d = d || {}
      cb(null, Object.keys(d.scripts || {}))
    })
  }
  
  function runScript (args, cb) {
    if (!args.length) return list(cb)
  
    var pkgdir = npm.localPrefix
    var cmd = args.shift()
  
    readJson(path.resolve(pkgdir, 'package.json'), function (er, d) {
      if (er) return cb(er)
      run(d, pkgdir, cmd, args, cb)
    })
  }
  
  function list (cb) {
    var json = path.join(npm.localPrefix, 'package.json')
    var cmdList = [
      'publish',
      'install',
      'uninstall',
      'test',
      'stop',
      'start',
      'restart',
      'version'
    ].reduce(function (l, p) {
      return l.concat(['pre' + p, p, 'post' + p])
    }, [])
    return readJson(json, function (er, d) {
      if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
      if (er) d = {}
      var allScripts = Object.keys(d.scripts || {})
      var scripts = []
      var runScripts = []
      allScripts.forEach(function (script) {
        if (cmdList.indexOf(script) !== -1) scripts.push(script)
        else runScripts.push(script)
      })
  
      if (log.level === 'silent') {
        return cb(null, allScripts)
      }
  
      if (npm.config.get('json')) {
        output(JSON.stringify(d.scripts || {}, null, 2))
        return cb(null, allScripts)
      }
  
      if (npm.config.get('parseable')) {
        allScripts.forEach(function (script) {
          output(script + ':' + d.scripts[script])
        })
        return cb(null, allScripts)
      }
  
      var s = '\n    '
      var prefix = '  '
      if (scripts.length) {
        output('Lifecycle scripts included in %s:', d.name)
      }
      scripts.forEach(function (script) {
        output(prefix + script + s + d.scripts[script])
      })
      if (!scripts.length && runScripts.length) {
        output('Scripts available in %s via `npm run-script`:', d.name)
      } else if (runScripts.length) {
        output('\navailable via `npm run-script`:')
      }
      runScripts.forEach(function (script) {
        output(prefix + script + s + d.scripts[script])
      })
      return cb(null, allScripts)
    })
  }
  
  function run (pkg, wd, cmd, args, cb) {
    if (!pkg.scripts) pkg.scripts = {}
  
    var cmds
    if (cmd === 'restart' && !pkg.scripts.restart) {
      cmds = [
        'prestop', 'stop', 'poststop',
        'restart',
        'prestart', 'start', 'poststart'
      ]
    } else {
      if (!pkg.scripts[cmd]) {
        if (cmd === 'test') {
          pkg.scripts.test = 'echo \'Error: no test specified\''
        } else if (cmd === 'env') {
          if (process.platform === 'win32') {
            log.verbose('run-script using default platform env: SET (Windows)')
            pkg.scripts[cmd] = 'SET'
          } else {
            log.verbose('run-script using default platform env: env (Unix)')
            pkg.scripts[cmd] = 'env'
          }
        } else if (npm.config.get('if-present')) {
          return cb(null)
        } else {
          return cb(new Error('missing script: ' + cmd))
        }
      }
      cmds = [cmd]
    }
  
    if (!cmd.match(/^(pre|post)/)) {
      cmds = ['pre' + cmd].concat(cmds).concat('post' + cmd)
    }
  
    log.verbose('run-script', cmds)
    chain(cmds.map(function (c) {
      // pass cli arguments after -- to script.
      if (pkg.scripts[c] && c === cmd) {
        pkg.scripts[c] = pkg.scripts[c] + joinArgs(args)
      }
  
      // when running scripts explicitly, assume that they're trusted.
      return [lifecycle, pkg, c, wd, { unsafePerm: true }]
    }), cb)
  }
  
  // join arguments after '--' and pass them to script,
  // handle special characters such as ', ", ' '.
  function joinArgs (args) {
    var joinedArgs = ''
    args.forEach(function (arg) {
      joinedArgs += ' "' + arg.replace(/"/g, '\\"') + '"'
    })
    return joinedArgs
  }
  