Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .ai/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

### your role

You are an expert in building node.js api applications focused on command line interfaces and termainal based applications.
You have a deep knowledge of the npm packages ora, enquirer, , as well as tap for developing the best in breed node applications.

### your mission

Your role is to help build and maintain a node.js application seeli which focuses on flexibile and composible command line applications.

### technology stack
- backend: node.js, enquirer, ora, debug
- testing: tap

### coding standards
- commas should be placed at the beginning of a line rather than the end
- add jsdoc comments for modules, exported functions and classes
- follow the existing folder structure and naming conventions
- function names should be camel cased
- variable names should be snake cased
- prefer composition over inheritence
- avoid using classes unless complex state management is required
- linting rules found in the shared eslint configuration eslint-config-logdna should be followed and applied. The command 'npm run lint' should pass with an exit code of 0
- many linting errors can be auto fixed by running `npm run lint:fix` which lints and applies fixes where possible.
- tap executes our tests. running `npm test` will run all tests. npm test <file> wil run a specific file. all tests should exit with a code of 0
- class names should be upper camel case, FooBar
- function names should be standard camel case, fooBar
- all other variable names should be snake case, foo_bar
- When testing, mocking should be avoided unless strictly necessary. Interfacing with a live data store, or external service is preferable

4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
node-version: [20.x, 22.x, 24.x]

steps:
- name: Checkout
Expand All @@ -37,7 +37,7 @@ jobs:
uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 14
node-version: 22
- run: npm install

- name: Publish
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ cli.js
*.vim

pnpm-lock.yaml
.tap
.tap-output/
65 changes: 33 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![Test + Release](https://github.com/esatterwhite/node-seeli/actions/workflows/release.yml/badge.svg)](https://github.com/esatterwhite/node-seeli/actions/workflows/release.yml)
![package dependancies](https://david-dm.org/esatterwhite/node-seeli.png)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/47a935a723c94c73bc97d749836ee489)](https://www.codacy.com/app/esatterwhite/node-seeli?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=esatterwhite/node-seeli&amp;utm_campaign=Badge_Grade)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/0df91a318dc444e7aedcdd4c77fda673)](https://app.codacy.com/gh/esatterwhite/node-seeli/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)

# seeli ( C. L. I. )
Expand Down Expand Up @@ -110,37 +110,36 @@ node ./cli world --name=Mark --name=Sally --no-excited

<!-- vim-markdown-toc GFM -->

- [seeli ( C. L. I. )](#seeli--c-l-i-)
- [API](#api)
- [Seeli.Command(`<Command>`)](#seelicommandcommand)
- [Command Options](#command-options)
- [Flag Options](#flag-options)
- [Nested Flags](#nested-flags)
- [`cmd` Shape](#cmd-shape)
- [Seeli.run( )](#seelirun-)
- [Seeli.list`<Array>`](#seelilistarray)
- [Seeli.use( \[name `<string>`,\] cmd `<Command>` )](#seeliuse-name-string-cmd-command-)
- [Seeli.bold( text `<string>`)](#seelibold-text-string)
- [Seeli.green( text `<string>`)](#seeligreen-text-string)
- [Seeli.blue( text `<string>`)](#seeliblue-text-string)
- [Seeli.red( text `<string>`)](#seelired-text-string)
- [Seeli.yellow( text `<string>`)](#seeliyellow-text-string)
- [Seeli.cyan( text `<string>`)](#seelicyan-text-string)
- [Seeli.magenta( text `<string>`)](#seelimagenta-text-string)
- [Seeli.redBright( text `<string>`)](#seeliredbright-text-string)
- [Seeli.blueBright( text `<string>`)](#seelibluebright-text-string)
- [Seeli.greenBright( text `<string>`)](#seeligreenbright-text-string)
- [Seeli.yellowBright( text `<string>`)](#seeliyellowbright-text-string)
- [Seeli.cyanBright( text `<string>`)](#seelicyanbright-text-string)
- [Seeli.config( key `<string>`, value `<object>` )](#seeliconfig-key-string-value-object-)
- [Seeli.config( opts `<object>` )](#seeliconfig-opts-object-)
- [Config Options](#config-options)
- [Seeli.config( key `<string>` )](#seeliconfig-key-string-)
- [Package Configuration](#package-configuration)
- [Auto Help](#auto-help)
- [Asyncronous](#asyncronous)
- [Showing Progress](#showing-progress)
- [Events](#events)
* [API](#api)
* [Seeli.Command(`<Command>`)](#seelicommandcommand)
* [Command Options](#command-options)
* [Flag Options](#flag-options)
* [Nested Flags](#nested-flags)
* [`cmd` Shape](#cmd-shape)
* [Seeli.run( )](#seelirun-)
* [Seeli.list`<Array>`](#seelilistarray)
* [Seeli.use( [name `<string>`,] cmd `<Command>` )](#seeliuse-name-string-cmd-command-)
* [Seeli.bold( text `<string>`)](#seelibold-text-string)
* [Seeli.green( text `<string>`)](#seeligreen-text-string)
* [Seeli.blue( text `<string>`)](#seeliblue-text-string)
* [Seeli.red( text `<string>`)](#seelired-text-string)
* [Seeli.yellow( text `<string>`)](#seeliyellow-text-string)
* [Seeli.cyan( text `<string>`)](#seelicyan-text-string)
* [Seeli.magenta( text `<string>`)](#seelimagenta-text-string)
* [Seeli.redBright( text `<string>`)](#seeliredbright-text-string)
* [Seeli.blueBright( text `<string>`)](#seelibluebright-text-string)
* [Seeli.greenBright( text `<string>`)](#seeligreenbright-text-string)
* [Seeli.yellowBright( text `<string>`)](#seeliyellowbright-text-string)
* [Seeli.cyanBright( text `<string>`)](#seelicyanbright-text-string)
* [Seeli.config( key `<string>`, value `<object>` )](#seeliconfig-key-string-value-object-)
* [Seeli.config( opts `<object>` )](#seeliconfig-opts-object-)
* [Config Options](#config-options)
* [Seeli.config( key `<string>` )](#seeliconfig-key-string-)
* [Package Configuration](#package-configuration)
* [Auto Help](#auto-help)
* [Asyncronous](#asyncronous)
* [Showing Progress](#showing-progress)
* [Events](#events)

<!-- vim-markdown-toc -->
# API
Expand Down Expand Up @@ -183,6 +182,8 @@ name | required | type | description
**filter** | `false` | `function` | Receives the user input and return the filtered value to be used **inside** the program. The value returned will be added to the Answers hash.
**required_with** | `false` | `Array` | A non-empty array which says that if the flag is set, then the specified other flags must also be set, i.e. "mutual inclusion."
**required_without** | `false` | `Array` | A non-empty array which says that if the flag is set, then none of the other specified flags may also be set, i.e. "mutual exclusion."
**affirmative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `yes`)
**negative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `no`)

### Nested Flags

Expand Down
21 changes: 21 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict'

const {defineConfig} = require('eslint/config')
const logdna = require('eslint-config-logdna')

module.exports = defineConfig([
{
'extends': [logdna]
, 'files': ['lib/**/*.js', '*.js', 'test/**/*.js']
, 'languageOptions': {
ecmaVersion: 2022
, sourceType: 'commonjs'
}
, 'rules': {
'sensible/check-require': [2, 'always', {
root: __dirname
}]
, 'logdna/require-file-extension': ['off', false]
}
}
])
11 changes: 10 additions & 1 deletion examples/commands/hello.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ module.exports = new cli.Command({
'type': Boolean
, 'shorthand': 'e'
, 'description': 'Say hello in a very excited manner'
, 'default': false
, 'default': true
, 'affirmative': 'Yes'
, 'negative': 'Nope'
}

, volume: {
Expand All @@ -37,6 +39,13 @@ module.exports = new cli.Command({
, 'default': 'normal'
, 'shorthand': 'v'
}
, things: {
type: String
, description: 'which thing'
, choices: ['one', 'two', 'three']
, multi: true
, shorthand: 't'
}
}
, onContent: (content) => {
// command success
Expand Down
4 changes: 3 additions & 1 deletion examples/commands/sub/hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ module.exports = new cli.Command({
'type': Boolean
, 'default': true
, 'description': 'do you like hub'
, affirmative: 'I think so'
, negative: 'i do not think so'
}
}
, async run(cmd, data) {
return 'hub'
return data.ask ? 'hub' : 'no hub'
}
})

5 changes: 5 additions & 0 deletions gh-pages/guides/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ name | required | type | description
**when** | `false` | `function` | **interactive mode only** Receives the current user answers hash and should return true or **false** depending on whether or not this question should be asked.
**validate** | `false` | `function` | receives user input and should return true if the value is **valid**, and an error message (String) otherwise. If false is returned, a default error message is provided.
**filter** | `false` | `function` | **interactive mode only** Receives the user input and return the filtered value to be used **inside** the program. The value returned will be added to the Answers hash.
**required_with** | `false` | `Array` | A non-empty array which says that if the flag is set, then the specified other flags must also be set, i.e. "mutual inclusion."
**required_without** | `false` | `Array` | A non-empty array which says that if the flag is set, then none of the other specified flags may also be set, i.e. "mutual exclusion."
**affirmative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `yes`)
**negative** | `false` | `String` | **interactive mode only** For `Boolean` flags, this is the value to display when the flag is true (default is `no`)


### Types

Expand Down
2 changes: 1 addition & 1 deletion lib/colorize.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @requires chalk
* @requires seeli/lib/conf
**/
const chalk = require('chalk')
const {default: chalk} = require('chalk')
const conf = require('./conf')
const {InvalidColorModeException} = require('./exceptions')

Expand Down
23 changes: 14 additions & 9 deletions lib/command/flag-to-prompt.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
'use strict'

const flagType = require('./flag-type')
const invertWhen = require('./invert-when')

module.exports = flagToPrompt

function flagToPrompt(name, opt) {
function flagToPrompt(name, opt = {}) {
const display = name.replace(':', ' ')
const t = flagType(opt)

return {
'type': flagType(opt)
, 'name': name
, 'message': display + ': ' + (opt.description || '(no description)')
, 'choices': opt.choices
, 'default': opt.default || null
, 'when': opt.when
, 'filter': opt.filter
, 'transformer': opt.transformer
type: t
, name: name
, message: display + ': ' + (opt.description || '(no description)')
, choices: opt.choices
, initial: opt.default || null
, skip: invertWhen(opt.when)
, result: opt.filter
, format: opt.transformer
, affirmative: opt.affirmative
, negative: opt.negative
}
}
11 changes: 8 additions & 3 deletions lib/command/flag-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ function flagType(flag) {
, multi: true
})
}
if (flag.type === Boolean) return 'confirm'
if (flag.type === Boolean) {
if (flag.affirmative && flag.negative) {
return 'toggle'
}
return 'confirm'
}
if (flag.type === Number) return 'number'
if (flag.mask) return 'password'
if (flag.choices) {
if (flag.multi) return 'checkbox'
return 'list'
if (flag.multi) return 'multiselect'
return 'select'
}

return 'input'
Expand Down
Loading
Loading