-
Notifications
You must be signed in to change notification settings - Fork 1
3. Anatomy of a Skill
The main components of a skill are:
-
index.js
, the main entrypoint - the
intents
folder, defining intent metadata like command structure and examples - the
actions
folder, containing the actual action code for each of de defined intents
Should export a function that receives an empty skill object, updates its metadata and returns it.
module.exports = skill => skill
.can('multiply numbers')
You can also define slots or aliases at skill level, which will make them global to all intents within that skill.
Each file here represents a single intent. Each intent should export a function that receives an empty intent object, updates its metadata and returns it.
module.exports = intent => intent.ns('multiply')
.can('multiply numbers')
.examples([
'whats 182 times 56',
'calculate 410 x 54',
])
.command([
'~[whats] @[number_one] ~[times] @[number_two]',
], { train: 100, test: 10 })
.slot('number_one', { numberPattern: true })
.slot('number_two', { numberPattern: true })
.alias('whats', ['whats', 'what is', 'calculate', 'calc', 'do'])
.alias('times', ['times', 'x', 'multiplied by']);
Significant intent methods:
-
can
: Use to provide one or more utterances describing intent functionality - this will help Niles understand when users ask for topical help and provide support. You can also use aliases here, but the first string provided should be "clean" as it will be used to describe the intent in the global help response. -
examples
: Use to provide usage examples that will be displayed by help intents. -
command
: Use to provide one or more command formats, using references to slots and aliases. You can also define how many training and test utterances to generate; the default values are 200 for training and 20 for testing, but you should experiment and adjust those as needed for your specific case. Keep in mind that if you don't provide sufficient format variation but keep the default numbers, you might see a generation error on build, telling you that you've requested more utterances than can be generated. -
slot
: Use this to define information you need extracted from the commands. The second argument to this can be an array explicitly listing string options (you can also use aliases here), or an object specifying which patterns the data falls into. Available patterns to apply:
Pattern | Description |
---|---|
wordPattern |
random 3-10 letter string |
filePathPattern |
random file paths |
fileExtensionPattern |
random file extensions |
fileNamePattern |
random filenames |
textContentPattern |
random words within quotes |
userPattern |
linux users pattern |
timePattern |
random datetime formats |
numberPattern |
random int or float number |
-
alias
: Use this to define synonyms (you can also reference aliases within aliases). There's also a set of global, predefined aliases you can use anywhere:
Alias | Description |
---|---|
~[_letter] |
random single lowercase letter |
~[_digit] |
random single digit |
~[_bla] |
random 3-5 letter string |
~[_word] |
random 3-10 letter string |
~[_dir] |
"dir", "directory" or "folder" |
~[_extension] |
random file extension |
~[_time_interval] |
"seconds", "minutes", "hours", "days", "months" or "years" |
~[_time_interval#singular] |
"second", "minute", "hour", "day", "month", "year" |
~[_day] |
random weekday, short or long format |
~[_month] |
random month, short or long format |
~[_year] |
random year |
Each file here represents a single action, associated to a single intent by file name. Each action should export a single function, receiving context and params and executing the actual intent functionality.
module.exports = (params, ctx) => {
const { logger } = ctx;
const { chalk } = logger;
let { number_one: numberOne, number_two: numberTwo } = params;
if (!numberOne || !numberTwo) {
// Maybe read them from the user with ctx.logger.inquirer instead
throw new Error('I\'m sorry, I couldn\'t understand the numbers to multiply.');
}
numberOne = parseFloat(numberOne.value.trim());
numberTwo = parseFloat(numberTwo.value.trim());
logger.log(`That would be ${chalk.blueBright((numberOne * numberTwo).toFixed(2))}, sir.`);
};
Params are sent as an object, each key within representing an identified slot by its defined name. Each slot itself is an object with three properties: entity
(the slot name again), value
and confidence
.
The context is a collection of tools and utilities assisting with skill development. Here's a breakdown of relevant context objects/methods:
Key | Description |
---|---|
homePath |
Path to the home directory for Niles. |
shell |
Shelljs instance you can use to run shell commands. |
shell.execAsync(cmd) |
Convenient method to run command as promise. |
shell.parseTree(output) |
Use this to convert tree-like ASCII structures to JSON objects. |
axios |
Axios instance you can use to make http requests. |
logger |
Use this instead of console to print output. |
logger.inquirer |
Inquirer.js instance you can use to require user input. |
logger.chalk |
Chalk instance you can use for styled output. |
logger.table |
Table instance you can use to create ASCII tables. |
logger.table.borderless(data, padding = 2, config = {}) |
Convenient method to generate simple, compact tables. |
logger.table.template(data, template = 'ramac', config = {}) |
Convenient method to create templated tables - see table docs. |
analytics.track(event, properties = {}) |
Use this to track relevant events. |
db.get(name) |
Use this to retrieve a saved value (namespaced by skill). |
db.save(name, value) |
Use this to save a value locally (namespaced by skill). |
session.intent |
Use this to access the current intent object directly. |