coa 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. package/.npmignore +6 -0
  2. package/.nyc_output/1f2a0db5a6d6559149db56d397f47cfc.json +1 -0
  3. package/.nyc_output/75b82d38f2186df930141082076e11c6.json +1 -0
  4. package/.travis.yml +9 -0
  5. package/GNUmakefile +34 -0
  6. package/README.md +1 -19
  7. package/coverage/base.css +212 -0
  8. package/coverage/coa/index.html +93 -0
  9. package/coverage/coa/index.js.html +68 -0
  10. package/coverage/coa/lib/arg.js.html +239 -0
  11. package/coverage/coa/lib/cmd.js.html +1556 -0
  12. package/coverage/coa/lib/coaobject.js.html +365 -0
  13. package/coverage/coa/lib/coaparam.js.html +440 -0
  14. package/coverage/coa/lib/color.js.html +131 -0
  15. package/coverage/coa/lib/completion.js.html +593 -0
  16. package/coverage/coa/lib/index.html +197 -0
  17. package/coverage/coa/lib/index.js.html +107 -0
  18. package/coverage/coa/lib/opt.js.html +524 -0
  19. package/coverage/coa/lib/shell.js.html +107 -0
  20. package/coverage/index.html +106 -0
  21. package/coverage/prettify.css +1 -0
  22. package/coverage/prettify.js +1 -0
  23. package/coverage/sort-arrow-sprite.png +0 -0
  24. package/coverage/sorter.js +158 -0
  25. package/index.js +1 -1
  26. package/lib/arg.js +161 -44
  27. package/lib/cmd.js +547 -434
  28. package/lib/color.js +22 -19
  29. package/lib/completion.js +119 -161
  30. package/lib/index.js +10 -14
  31. package/lib/opt.js +313 -130
  32. package/lib/shell.js +13 -13
  33. package/package.json +14 -19
  34. package/qq.js +17 -0
  35. package/src/arg.coffee +130 -0
  36. package/src/cmd.coffee +456 -0
  37. package/src/color.coffee +25 -0
  38. package/src/completion.coffee +156 -0
  39. package/src/index.coffee +5 -0
  40. package/src/opt.coffee +243 -0
  41. package/src/shell.coffee +10 -0
  42. package/test/coa.js +496 -0
  43. package/test/mocha.opts +2 -0
  44. package/test/shell-test.js +60 -0
  45. package/tests/api-h.js +9 -0
  46. package/tests/h.js +6 -0
  47. package/LICENSE +0 -21
  48. package/lib/coaobject.js +0 -100
  49. package/lib/coaparam.js +0 -125
package/src/cmd.coffee ADDED
@@ -0,0 +1,456 @@
1
+ UTIL = require 'util'
2
+ PATH = require 'path'
3
+ Color = require('./color').Color
4
+ Q = require('q')
5
+
6
+ #inspect = require('eyes').inspector { maxLength: 99999, stream: process.stderr }
7
+
8
+ ###*
9
+ Command
10
+
11
+ Top level entity. Commands may have options and arguments.
12
+ @namespace
13
+ @class Presents command
14
+ ###
15
+ exports.Cmd = class Cmd
16
+
17
+ ###*
18
+ @constructs
19
+ @param {COA.Cmd} [cmd] parent command
20
+ ###
21
+ constructor: (cmd) ->
22
+ if this not instanceof Cmd
23
+ return new Cmd cmd
24
+
25
+ @_parent cmd
26
+
27
+ @_cmds = []
28
+ @_cmdsByName = {}
29
+
30
+ @_opts = []
31
+ @_optsByKey = {}
32
+
33
+ @_args = []
34
+
35
+ @_ext = false
36
+
37
+ @get: (propertyName, func) ->
38
+ Object.defineProperty @::, propertyName,
39
+ configurable: true
40
+ enumerable: true
41
+ get: func
42
+
43
+ ###*
44
+ Returns object containing all its subcommands as methods
45
+ to use from other programs.
46
+ @returns {Object}
47
+ ###
48
+ @get 'api', () ->
49
+ if not @_api
50
+ @_api = => @invoke.apply @, arguments
51
+ for c of @_cmdsByName
52
+ do (c) =>
53
+ @_api[c] = @_cmdsByName[c].api
54
+ @_api
55
+
56
+ _parent: (cmd) ->
57
+ @_cmd = cmd or this
58
+ if cmd
59
+ cmd._cmds.push @
60
+ if @_name then @_cmd._cmdsByName[@_name] = @
61
+ @
62
+
63
+ ###*
64
+ Set a canonical command identifier to be used anywhere in the API.
65
+ @param {String} _name command name
66
+ @returns {COA.Cmd} this instance (for chainability)
67
+ ###
68
+ name: (@_name) ->
69
+ if @_cmd isnt @ then @_cmd._cmdsByName[_name] = @
70
+ @
71
+
72
+ ###*
73
+ Set a long description for command to be used anywhere in text messages.
74
+ @param {String} _title command title
75
+ @returns {COA.Cmd} this instance (for chainability)
76
+ ###
77
+ title: (@_title) -> @
78
+
79
+ ###*
80
+ Create new or add existing subcommand for current command.
81
+ @param {COA.Cmd} [cmd] existing command instance
82
+ @returns {COA.Cmd} new subcommand instance
83
+ ###
84
+ cmd: (cmd) ->
85
+ if cmd then cmd._parent @
86
+ else new Cmd @
87
+
88
+ ###*
89
+ Create option for current command.
90
+ @returns {COA.Opt} new option instance
91
+ ###
92
+ opt: -> new (require('./opt').Opt) @
93
+
94
+ ###*
95
+ Create argument for current command.
96
+ @returns {COA.Opt} new argument instance
97
+ ###
98
+ arg: -> new (require('./arg').Arg) @
99
+
100
+ ###*
101
+ Add (or set) action for current command.
102
+ @param {Function} act action function,
103
+ invoked in the context of command instance
104
+ and has the parameters:
105
+ - {Object} opts parsed options
106
+ - {Array} args parsed arguments
107
+ - {Object} res actions result accumulator
108
+ It can return rejected promise by Cmd.reject (in case of error)
109
+ or any other value treated as result.
110
+ @param {Boolean} [force=false] flag for set action instead add to existings
111
+ @returns {COA.Cmd} this instance (for chainability)
112
+ ###
113
+ act: (act, force) ->
114
+ return @ unless act
115
+
116
+ if not force and @_act
117
+ @_act.push act
118
+ else
119
+ @_act = [act]
120
+
121
+ @
122
+
123
+ ###*
124
+ Set custom additional completion for current command.
125
+ @param {Function} completion generation function,
126
+ invoked in the context of command instance.
127
+ Accepts parameters:
128
+ - {Object} opts completion options
129
+ It can return promise or any other value treated as result.
130
+ @returns {COA.Cmd} this instance (for chainability)
131
+ ###
132
+ comp: (@_comp) -> @
133
+
134
+ ###*
135
+ Apply function with arguments in context of command instance.
136
+ @param {Function} fn
137
+ @param {Array} args
138
+ @returns {COA.Cmd} this instance (for chainability)
139
+ ###
140
+ apply: (fn, args...) ->
141
+ fn.apply this, args
142
+ @
143
+
144
+ ###*
145
+ Make command "helpful", i.e. add -h --help flags for print usage.
146
+ @returns {COA.Cmd} this instance (for chainability)
147
+ ###
148
+ helpful: ->
149
+ @opt()
150
+ .name('help').title('Help')
151
+ .short('h').long('help')
152
+ .flag()
153
+ .only()
154
+ .act ->
155
+ return @usage()
156
+ .end()
157
+
158
+ ###*
159
+ Adds shell completion to command, adds "completion" subcommand,
160
+ that makes all the magic.
161
+ Must be called only on root command.
162
+ @returns {COA.Cmd} this instance (for chainability)
163
+ ###
164
+ completable: ->
165
+ @cmd()
166
+ .name('completion')
167
+ .apply(require './completion')
168
+ .end()
169
+
170
+ ###*
171
+ Allow command to be extendable by external node.js modules.
172
+ @param {String} [pattern] Pattern of node.js module to find subcommands at.
173
+ @returns {COA.Cmd} this instance (for chainability)
174
+ ###
175
+ extendable: (pattern) ->
176
+ @_ext = pattern or true
177
+ @
178
+
179
+ _exit: (msg, code) ->
180
+ process.once 'exit', ->
181
+ if msg then console.error msg
182
+ process.exit code or 0
183
+
184
+ ###*
185
+ Build full usage text for current command instance.
186
+ @returns {String} usage text
187
+ ###
188
+ usage: ->
189
+ res = []
190
+
191
+ if @_title then res.push @_fullTitle()
192
+
193
+ res.push('', 'Usage:')
194
+
195
+ if @_cmds.length then res.push(['', '',
196
+ Color('lred', @_fullName()),
197
+ Color('lblue', 'COMMAND'),
198
+ Color('lgreen', '[OPTIONS]'),
199
+ Color('lpurple', '[ARGS]')].join ' ')
200
+
201
+ if @_opts.length + @_args.length then res.push(['', '',
202
+ Color('lred', @_fullName()),
203
+ Color('lgreen', '[OPTIONS]'),
204
+ Color('lpurple', '[ARGS]')].join ' ')
205
+
206
+ res.push(
207
+ @_usages(@_cmds, 'Commands'),
208
+ @_usages(@_opts, 'Options'),
209
+ @_usages(@_args, 'Arguments'))
210
+
211
+ res.join '\n'
212
+
213
+ _usage: ->
214
+ Color('lblue', @_name) + ' : ' + @_title
215
+
216
+ _usages: (os, title) ->
217
+ unless os.length then return
218
+ res = ['', title + ':']
219
+ for o in os
220
+ res.push ' ' + o._usage()
221
+ res.join '\n'
222
+
223
+ _fullTitle: ->
224
+ (if @_cmd is this then '' else @_cmd._fullTitle() + '\n') + @_title
225
+
226
+ _fullName: ->
227
+ (if this._cmd is this then '' else @_cmd._fullName() + ' ') + PATH.basename(@_name)
228
+
229
+ _ejectOpt: (opts, opt) ->
230
+ if (pos = opts.indexOf(opt)) >= 0
231
+ if opts[pos]._arr
232
+ opts[pos]
233
+ else
234
+ opts.splice(pos, 1)[0]
235
+
236
+ _checkRequired: (opts, args) ->
237
+ if not (@_opts.filter (o) -> o._only and o._name of opts).length
238
+ all = @_opts.concat @_args
239
+ while i = all.shift()
240
+ if i._req and i._checkParsed opts, args
241
+ return @reject i._requiredText()
242
+
243
+ _parseCmd: (argv, unparsed = []) ->
244
+ argv = argv.concat()
245
+ optSeen = false
246
+ while i = argv.shift()
247
+ if not i.indexOf '-'
248
+ optSeen = true
249
+ if not optSeen and /^\w[\w-_]*$/.test(i)
250
+ cmd = @_cmdsByName[i]
251
+
252
+ if not cmd and @_ext
253
+ # construct package name to require
254
+ if typeof @_ext is 'string'
255
+ if ~@_ext.indexOf('%s')
256
+ # use formatted string
257
+ pkg = UTIL.format(@_ext, i)
258
+ else
259
+ # just append subcommand name to the prefix
260
+ pkg = @_ext + i
261
+ else if @_ext is true
262
+ # use default scheme: <command>-<subcommand>-<subcommand> and so on
263
+ pkg = i
264
+ c = @
265
+ loop
266
+ pkg = c._name + '-' + pkg
267
+ if c._cmd is c then break
268
+ c = c._cmd
269
+
270
+ try
271
+ cmdDesc = require(pkg)
272
+ catch e
273
+
274
+ if cmdDesc
275
+ if typeof cmdDesc == 'function'
276
+ # set create subcommand, set its name and apply imported function
277
+ @cmd()
278
+ .name(i)
279
+ .apply(cmdDesc)
280
+ .end()
281
+ else if typeof cmdDesc == 'object'
282
+ # register subcommand
283
+ @cmd(cmdDesc)
284
+ # set command name
285
+ cmdDesc.name(i)
286
+ else
287
+ throw new Error 'Error: Unsupported command declaration type, ' +
288
+ 'should be function or COA.Cmd() object'
289
+ cmd = @_cmdsByName[i]
290
+ if cmd
291
+ return cmd._parseCmd argv, unparsed
292
+
293
+ unparsed.push i
294
+
295
+ { cmd: @, argv: unparsed }
296
+
297
+ _parseOptsAndArgs: (argv) ->
298
+ opts = {}
299
+ args = {}
300
+
301
+ nonParsedOpts = @_opts.concat()
302
+ nonParsedArgs = @_args.concat()
303
+
304
+ while i = argv.shift()
305
+ # opt
306
+ if i isnt '--' and not i.indexOf '-'
307
+
308
+ if m = i.match /^(--\w[\w-_]*)=(.*)$/
309
+ i = m[1]
310
+
311
+ # suppress 'unknown argument' error for flag options with values
312
+ if not @_optsByKey[i]._flag
313
+ argv.unshift m[2]
314
+
315
+ if opt = @_ejectOpt nonParsedOpts, @_optsByKey[i]
316
+ if Q.isRejected(res = opt._parse argv, opts)
317
+ return res
318
+ else
319
+ return @reject "Unknown option: #{ i }"
320
+
321
+ # arg
322
+ else
323
+ if i is '--'
324
+ i = argv.splice(0)
325
+
326
+ i = if Array.isArray(i) then i else [i]
327
+
328
+ while a = i.shift()
329
+ if arg = nonParsedArgs.shift()
330
+ if arg._arr then nonParsedArgs.unshift arg
331
+ if Q.isRejected(res = arg._parse a, args)
332
+ return res
333
+ else
334
+ return @reject "Unknown argument: #{ a }"
335
+
336
+ # set defaults
337
+ {
338
+ opts: @_setDefaults(opts, nonParsedOpts),
339
+ args: @_setDefaults(args, nonParsedArgs)
340
+ }
341
+
342
+ _setDefaults: (params, desc) ->
343
+ for i in desc
344
+ if i._name not of params and '_def' of i
345
+ i._saveVal params, i._def
346
+ params
347
+
348
+ _processParams: (params, desc) ->
349
+ notExists = []
350
+ for i in desc
351
+ n = i._name
352
+ if n not of params
353
+ notExists.push i
354
+ continue
355
+
356
+ vals = params[n]
357
+ delete params[n]
358
+ if not Array.isArray vals
359
+ vals = [vals]
360
+
361
+ for v in vals
362
+ if Q.isRejected(res = i._saveVal(params, v))
363
+ return res
364
+
365
+ # set defaults
366
+ @_setDefaults params, notExists
367
+
368
+ _parseArr: (argv) ->
369
+ Q.when @_parseCmd(argv), (p) ->
370
+ Q.when p.cmd._parseOptsAndArgs(p.argv), (r) ->
371
+ { cmd: p.cmd, opts: r.opts, args: r.args }
372
+
373
+ _do: (input) ->
374
+ Q.when input, (input) =>
375
+ cmd = input.cmd
376
+ [@_checkRequired].concat(cmd._act or []).reduce(
377
+ (res, act) ->
378
+ Q.when res, (res) ->
379
+ act.call(
380
+ cmd
381
+ input.opts
382
+ input.args
383
+ res)
384
+ undefined
385
+ )
386
+
387
+ ###*
388
+ Parse arguments from simple format like NodeJS process.argv
389
+ and run ahead current program, i.e. call process.exit when all actions done.
390
+ @param {Array} argv
391
+ @returns {COA.Cmd} this instance (for chainability)
392
+ ###
393
+ run: (argv = process.argv.slice(2)) ->
394
+ cb = (code) => (res) =>
395
+ if res
396
+ @_exit res.stack ? res.toString(), res.exitCode ? code
397
+ else
398
+ @_exit()
399
+ Q.when(@do(argv), cb(0), cb(1)).done()
400
+ @
401
+
402
+ ###*
403
+ Convenient function to run command from tests.
404
+ @param {Array} argv
405
+ @returns {Q.Promise}
406
+ ###
407
+ do: (argv) ->
408
+ @_do(@_parseArr argv || [])
409
+
410
+ ###*
411
+ Invoke specified (or current) command using provided
412
+ options and arguments.
413
+ @param {String|Array} cmds subcommand to invoke (optional)
414
+ @param {Object} opts command options (optional)
415
+ @param {Object} args command arguments (optional)
416
+ @returns {Q.Promise}
417
+ ###
418
+ invoke: (cmds = [], opts = {}, args = {}) ->
419
+ if typeof cmds == 'string'
420
+ cmds = cmds.split(' ')
421
+
422
+ if arguments.length < 3
423
+ if not Array.isArray cmds
424
+ args = opts
425
+ opts = cmds
426
+ cmds = []
427
+
428
+ Q.when @_parseCmd(cmds), (p) =>
429
+ if p.argv.length
430
+ return @reject "Unknown command: " + cmds.join ' '
431
+
432
+ Q.all([@_processParams(opts, @_opts), @_processParams(args, @_args)])
433
+ .spread (opts, args) =>
434
+ @_do({ cmd: p.cmd, opts: opts, args: args })
435
+ # catch fails from .only() options
436
+ .fail (res) =>
437
+ if res and res.exitCode is 0
438
+ res.toString()
439
+ else
440
+ @reject(res)
441
+
442
+ ###*
443
+ Return reject of actions results promise with error code.
444
+ Use in .act() for return with error.
445
+ @param {Object} reject reason
446
+ You can customize toString() method and exitCode property
447
+ of reason object.
448
+ @returns {Q.promise} rejected promise
449
+ ###
450
+ reject: (reason) -> Q.reject(reason)
451
+
452
+ ###*
453
+ Finish chain for current subcommand and return parent command instance.
454
+ @returns {COA.Cmd} parent command
455
+ ###
456
+ end: -> @_cmd
@@ -0,0 +1,25 @@
1
+ colors =
2
+ black: '30'
3
+ dgray: '1;30'
4
+ red: '31'
5
+ lred: '1;31'
6
+ green: '32'
7
+ lgreen: '1;32'
8
+ brown: '33'
9
+ yellow: '1;33'
10
+ blue: '34'
11
+ lblue: '1;34'
12
+ purple: '35'
13
+ lpurple: '1;35'
14
+ cyan: '36'
15
+ lcyan: '1;36'
16
+ lgray: '37'
17
+ white: '1;37'
18
+
19
+ exports.Color = (c, str) ->
20
+ # Use \x1B instead of \033 because of CoffeeScript 1.3.x compilation error
21
+ [
22
+ '\x1B[', colors[c], 'm'
23
+ str
24
+ '\x1B[m'
25
+ ].join ''
@@ -0,0 +1,156 @@
1
+ ###*
2
+ Most of the code adopted from the npm package shell completion code.
3
+ See https://github.com/isaacs/npm/blob/master/lib/completion.js
4
+ ###
5
+
6
+ Q = require 'q'
7
+ escape = require('./shell').escape
8
+ unescape = require('./shell').unescape
9
+
10
+ module.exports = ->
11
+ @title('Shell completion')
12
+ .helpful()
13
+ .arg()
14
+ .name('raw')
15
+ .title('Completion words')
16
+ .arr()
17
+ .end()
18
+ .act (opts, args) ->
19
+ if process.platform == 'win32'
20
+ e = new Error 'shell completion not supported on windows'
21
+ e.code = 'ENOTSUP'
22
+ e.errno = require('constants').ENOTSUP
23
+ return @reject(e)
24
+
25
+ # if the COMP_* isn't in the env, then just dump the script
26
+ if !process.env.COMP_CWORD? or !process.env.COMP_LINE? or !process.env.COMP_POINT?
27
+ return dumpScript(@_cmd._name)
28
+
29
+ console.error 'COMP_LINE: %s', process.env.COMP_LINE
30
+ console.error 'COMP_CWORD: %s', process.env.COMP_CWORD
31
+ console.error 'COMP_POINT: %s', process.env.COMP_POINT
32
+ console.error 'args: %j', args.raw
33
+
34
+ # completion opts
35
+ opts = getOpts args.raw
36
+
37
+ # cmd
38
+ { cmd, argv } = @_cmd._parseCmd opts.partialWords
39
+ Q.when complete(cmd, opts), (compls) ->
40
+ console.error 'filtered: %j', compls
41
+ console.log compls.map(escape).join('\n')
42
+
43
+
44
+ dumpScript = (name) ->
45
+ fs = require 'fs'
46
+ path = require 'path'
47
+ defer = Q.defer()
48
+
49
+ fs.readFile path.resolve(__dirname, 'completion.sh'), 'utf8', (err, d) ->
50
+ if err then return defer.reject err
51
+ d = d.replace(/{{cmd}}/g, path.basename name).replace(/^\#\!.*?\n/, '')
52
+
53
+ onError = (err) ->
54
+ # Darwin is a real dick sometimes.
55
+ #
56
+ # This is necessary because the "source" or "." program in
57
+ # bash on OS X closes its file argument before reading
58
+ # from it, meaning that you get exactly 1 write, which will
59
+ # work most of the time, and will always raise an EPIPE.
60
+ #
61
+ # Really, one should not be tossing away EPIPE errors, or any
62
+ # errors, so casually. But, without this, `. <(cmd completion)`
63
+ # can never ever work on OS X.
64
+ if err.errno == require('constants').EPIPE
65
+ process.stdout.removeListener 'error', onError
66
+ defer.resolve()
67
+ else
68
+ defer.reject(err)
69
+
70
+ process.stdout.on 'error', onError
71
+ process.stdout.write d, -> defer.resolve()
72
+
73
+ defer.promise
74
+
75
+
76
+ getOpts = (argv) ->
77
+ # get the partial line and partial word, if the point isn't at the end
78
+ # ie, tabbing at: cmd foo b|ar
79
+ line = process.env.COMP_LINE
80
+ w = +process.env.COMP_CWORD
81
+ point = +process.env.COMP_POINT
82
+ words = argv.map unescape
83
+ word = words[w]
84
+ partialLine = line.substr 0, point
85
+ partialWords = words.slice 0, w
86
+
87
+ # figure out where in that last word the point is
88
+ partialWord = argv[w] or ''
89
+ i = partialWord.length
90
+ while partialWord.substr(0, i) isnt partialLine.substr(-1 * i) and i > 0
91
+ i--
92
+ partialWord = unescape partialWord.substr 0, i
93
+ if partialWord then partialWords.push partialWord
94
+
95
+ {
96
+ line: line
97
+ w: w
98
+ point: point
99
+ words: words
100
+ word: word
101
+ partialLine: partialLine
102
+ partialWords: partialWords
103
+ partialWord: partialWord
104
+ }
105
+
106
+
107
+ complete = (cmd, opts) ->
108
+ compls = []
109
+
110
+ # complete on cmds
111
+ if opts.partialWord.indexOf('-')
112
+ compls = Object.keys(cmd._cmdsByName)
113
+ # Complete on required opts without '-' in last partial word
114
+ # (if required not already specified)
115
+ #
116
+ # Commented out because of uselessness:
117
+ # -b, --block suggest results in '-' on cmd line;
118
+ # next completion suggest all options, because of '-'
119
+ #.concat Object.keys(cmd._optsByKey).filter (v) -> cmd._optsByKey[v]._req
120
+ else
121
+ # complete on opt values: --opt=| case
122
+ if m = opts.partialWord.match /^(--\w[\w-_]*)=(.*)$/
123
+ optWord = m[1]
124
+ optPrefix = optWord + '='
125
+ else
126
+ # complete on opts
127
+ # don't complete on opts in case of --opt=val completion
128
+ # TODO: don't complete on opts in case of unknown arg after commands
129
+ # TODO: complete only on opts with arr() or not already used
130
+ # TODO: complete only on full opts?
131
+ compls = Object.keys cmd._optsByKey
132
+
133
+ # complete on opt values: next arg case
134
+ if not (o = opts.partialWords[opts.w - 1]).indexOf '-'
135
+ optWord = o
136
+
137
+ # complete on opt values: completion
138
+ if optWord and opt = cmd._optsByKey[optWord]
139
+ if not opt._flag and opt._comp
140
+ compls = Q.join compls, Q.when opt._comp(opts), (c, o) ->
141
+ c.concat o.map (v) -> (optPrefix or '') + v
142
+
143
+ # TODO: complete on args values (context aware, custom completion?)
144
+
145
+ # custom completion on cmds
146
+ if cmd._comp
147
+ compls = Q.join compls, Q.when(cmd._comp(opts)), (c, o) ->
148
+ c.concat o
149
+
150
+ # TODO: context aware custom completion on cmds, opts and args
151
+ # (can depend on already entered values, especially options)
152
+
153
+ Q.when compls, (compls) ->
154
+ console.error 'partialWord: %s', opts.partialWord
155
+ console.error 'compls: %j', compls
156
+ compls.filter (c) -> c.indexOf(opts.partialWord) is 0
@@ -0,0 +1,5 @@
1
+ exports.Cmd = require('./cmd').Cmd
2
+ exports.Opt = require('./cmd').Opt
3
+ exports.Arg = require('./cmd').Arg
4
+ exports.shell = require('./shell')
5
+ exports.require = require;