@royaltyport/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @royaltyport/cli
2
+
3
+ Command-line interface for Royaltyport. Authenticate, browse projects, and execute commands in project sandboxes.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js >= 18.0.0
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install -g @royaltyport/cli
13
+ ```
14
+
15
+ Or install from source:
16
+
17
+ ```bash
18
+ git clone https://github.com/royaltyport/royaltyport-cli.git
19
+ cd royaltyport-cli
20
+ npm install
21
+ npm link
22
+ ```
23
+
24
+ ## Authentication
25
+
26
+ ### Interactive login
27
+
28
+ ```bash
29
+ royaltyport login
30
+ ```
31
+
32
+ You'll be prompted for your API token (`rp_...`). The token is validated against the API and stored locally.
33
+
34
+ ### Token flag
35
+
36
+ ```bash
37
+ royaltyport login --token rp_your_token_here
38
+ ```
39
+
40
+ ### Environment variables
41
+
42
+ For CI/CD or AI agent integrations, set these environment variables instead of running `login`:
43
+
44
+ | Variable | Description |
45
+ | -------------------- | -------------------------------------------------------- |
46
+ | `ROYALTYPORT_TOKEN` | API token — overrides the stored token |
47
+ | `ROYALTYPORT_API_URL`| Custom API base URL (default: `https://api.royaltyport.com`) |
48
+
49
+ ### Custom API URL
50
+
51
+ ```bash
52
+ royaltyport login --api-url https://your-api-url.com
53
+ ```
54
+
55
+ ### Logout
56
+
57
+ ```bash
58
+ royaltyport logout
59
+ ```
60
+
61
+ ## Commands
62
+
63
+ ### `royaltyport projects`
64
+
65
+ List all accessible projects.
66
+
67
+ ```bash
68
+ royaltyport projects
69
+ ```
70
+
71
+ ```
72
+ ID Name Created
73
+ ───────────────────────────────────── ────────────────── ──────────
74
+ a1b2c3d4-... Record Label Ltd 1/15/2025
75
+ e5f6g7h8-... Publishing Co 3/22/2025
76
+ ```
77
+
78
+ ### `royaltyport project info <project_id>`
79
+
80
+ Display the AGENTS.md from the project sandbox — a filesystem overview with instructions for navigating project data.
81
+
82
+ ```bash
83
+ royaltyport project info a1b2c3d4-e5f6-7890-abcd-ef1234567890
84
+ ```
85
+
86
+ ### `royaltyport project exec <project_id> "<command>"`
87
+
88
+ Execute a single bash command in the project sandbox. Commands run with the sandbox workspace root as the working directory.
89
+
90
+ ```bash
91
+ # List all contract folders
92
+ royaltyport project exec $PROJECT_ID "ls contracts/"
93
+
94
+ # Read project stats
95
+ royaltyport project exec $PROJECT_ID "cat stats.yaml"
96
+
97
+ # Search for an entity by name
98
+ royaltyport project exec $PROJECT_ID "grep -rl 'Sony Music' entities/"
99
+
100
+ # Read a contract's extracted royalties
101
+ royaltyport project exec $PROJECT_ID "cat contracts/contract_123/extracted/royalties.yaml"
102
+ ```
103
+
104
+ stdout and stderr are written to their respective streams, and the process exits with the command's exit code — making it suitable for scripting and AI agent tool use.
105
+
106
+ ## Agent Skill
107
+
108
+ This repo includes a [skills.sh](https://skills.sh/)-compatible skill that teaches AI agents how to use the CLI to explore and query Royaltyport project data.
109
+
110
+ Install it into your agent:
111
+
112
+ ```bash
113
+ npx skills add royaltyport/royaltyport-cli
114
+ ```
115
+
116
+ The skill covers authentication, project discovery, filesystem layout, and common data access patterns — everything an agent needs to send the right `project exec` commands.
117
+
118
+ ## Configuration
119
+
120
+ Credentials and settings are stored at `~/.config/royaltyport/config.json` (managed by [conf](https://github.com/sindresorhus/conf)). Running `royaltyport logout` clears this file.
121
+
122
+ ## License
123
+
124
+ UNLICENSED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from 'commander';
4
+ import { registerLoginCommand } from '../src/commands/login.js';
5
+ import { registerLogoutCommand } from '../src/commands/logout.js';
6
+ import { registerProjectsCommand } from '../src/commands/projects.js';
7
+ import { registerProjectCommand } from '../src/commands/project.js';
8
+
9
+ program
10
+ .name('royaltyport')
11
+ .description('Royaltyport CLI — authenticate, list projects, and execute commands in a sandboxed project filesystem')
12
+ .version('0.1.0');
13
+
14
+ registerLoginCommand(program);
15
+ registerLogoutCommand(program);
16
+ registerProjectsCommand(program);
17
+ registerProjectCommand(program);
18
+
19
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@royaltyport/cli",
3
+ "version": "0.1.0",
4
+ "description": "Royaltyport CLI — authenticate, list projects, and connect to project file systems",
5
+ "type": "module",
6
+ "bin": {
7
+ "royaltyport": "bin/royaltyport.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/royaltyport.js",
11
+ "lint": "eslint"
12
+ },
13
+ "keywords": [
14
+ "royaltyport",
15
+ "cli"
16
+ ],
17
+ "license": "UNLICENSED",
18
+ "engines": {
19
+ "node": ">=18.0.0"
20
+ },
21
+ "dependencies": {
22
+ "chalk": "^5.4.1",
23
+ "commander": "^13.1.0",
24
+ "conf": "^13.1.0",
25
+ "ora": "^8.2.0"
26
+ }
27
+ }
@@ -0,0 +1,164 @@
1
+ ---
2
+ name: royaltyport-sandbox
3
+ description: Access Royaltyport project data via the CLI sandbox. Use when working with music royalty contracts, entities, artists, writers, relations, recordings, compositions, or statements.
4
+ metadata:
5
+ author: royaltyport
6
+ version: "0.1.0"
7
+ ---
8
+
9
+ # Skill: Royaltyport Sandbox
10
+
11
+ Access Royaltyport project data through the CLI. All project data is stored as YAML files in a sandboxed filesystem. You execute bash commands against the sandbox — no SDK or API calls needed.
12
+
13
+ ## Prerequisites
14
+
15
+ Install the CLI and authenticate:
16
+
17
+ ```bash
18
+ npm install -g @royaltyport/cli
19
+ ```
20
+
21
+ Set your API token as an environment variable (preferred for agents):
22
+
23
+ ```bash
24
+ export ROYALTYPORT_TOKEN=rp_your_token_here
25
+ ```
26
+
27
+ Or authenticate interactively:
28
+
29
+ ```bash
30
+ royaltyport login
31
+ ```
32
+
33
+ ## Discovery
34
+
35
+ ### List available projects
36
+
37
+ ```bash
38
+ royaltyport projects
39
+ ```
40
+
41
+ ### Get the full filesystem overview for a project
42
+
43
+ ```bash
44
+ royaltyport project info <project_id>
45
+ ```
46
+
47
+ This prints the project's AGENTS.md — a detailed breakdown of every directory, file type, and YAML field available in the sandbox.
48
+
49
+ ## Executing Commands
50
+
51
+ Run any bash command in the sandbox with `project exec`. Commands run with the workspace root as the working directory. Use relative paths directly.
52
+
53
+ ```bash
54
+ royaltyport project exec <project_id> "<command>"
55
+ ```
56
+
57
+ stdout and stderr stream to their respective outputs. The process exits with the command's exit code.
58
+
59
+ ## Filesystem Layout
60
+
61
+ ```
62
+ stats.yaml # Record counts for all resource types
63
+ contracts/contract_{id}/ # Per-contract data
64
+ uploaded.yaml # File metadata (id, file_name, file_type)
65
+ extracted/ # AI-extracted contract terms
66
+ royalties.yaml, entities.yaml, dates.yaml, control_areas.yaml,
67
+ compensations.yaml, costs.yaml, artists.yaml, writers.yaml,
68
+ recordings.yaml, compositions.yaml, signatures.yaml, splits.yaml,
69
+ accounting_period.yaml, languages.yaml, types.yaml,
70
+ creative_approvals.yaml, targets.yaml, commitments.yaml
71
+ relationships/ # Parent/sibling/child links
72
+ statements.yaml # Linked statements
73
+ entities/entity_{id}/ # metadata.yaml, merged.yaml, relations.yaml, artists.yaml, writers.yaml
74
+ artists/artist_{id}/ # metadata.yaml, merged.yaml, entities.yaml
75
+ writers/writer_{id}/ # metadata.yaml, merged.yaml, entities.yaml
76
+ relations/relation_{id}/ # metadata.yaml, merged.yaml, entities.yaml
77
+ recordings/recording_{id}/ # metadata.yaml, products.yaml, tracks.yaml
78
+ compositions/composition_{id}/ # metadata.yaml, products.yaml, tracks.yaml
79
+ statements/
80
+ statement_{id}/ # metadata.yaml, contracts.yaml
81
+ recordings/statement_{id}/assets.yaml # Recording assets (isrc, upc, matched)
82
+ compositions/statement_{id}/assets.yaml # Composition assets (iswc, work_id, matched)
83
+ ```
84
+
85
+ ## Common Data Access Patterns
86
+
87
+ ### Project overview
88
+
89
+ ```bash
90
+ royaltyport project exec $PROJECT_ID "cat stats.yaml"
91
+ ```
92
+
93
+ ### Find contracts
94
+
95
+ ```bash
96
+ # List all contracts
97
+ royaltyport project exec $PROJECT_ID "ls contracts/"
98
+
99
+ # Find contract by name
100
+ royaltyport project exec $PROJECT_ID "grep -ril 'CONTRACT_NAME' contracts/contract_*/uploaded.yaml"
101
+
102
+ # Find contracts involving an entity
103
+ royaltyport project exec $PROJECT_ID "grep -ril 'ENTITY_NAME' contracts/contract_*/extracted/entities.yaml"
104
+ ```
105
+
106
+ ### Read contract details
107
+
108
+ ```bash
109
+ # File metadata
110
+ royaltyport project exec $PROJECT_ID "cat contracts/contract_{id}/uploaded.yaml"
111
+
112
+ # Extracted terms
113
+ royaltyport project exec $PROJECT_ID "cat contracts/contract_{id}/extracted/royalties.yaml"
114
+ royaltyport project exec $PROJECT_ID "cat contracts/contract_{id}/extracted/splits.yaml"
115
+ royaltyport project exec $PROJECT_ID "ls contracts/contract_{id}/extracted/"
116
+ ```
117
+
118
+ ### Find artists, writers, entities
119
+
120
+ ```bash
121
+ # Search by name
122
+ royaltyport project exec $PROJECT_ID "grep -rl 'SEARCH_TERM' artists/"
123
+ royaltyport project exec $PROJECT_ID "grep -rl 'SEARCH_TERM' writers/"
124
+ royaltyport project exec $PROJECT_ID "grep -rl 'SEARCH_TERM' entities/"
125
+
126
+ # List all artist names
127
+ royaltyport project exec $PROJECT_ID "grep -rh '^name:' artists/artist_*/metadata.yaml | sort"
128
+
129
+ # Read artist details
130
+ royaltyport project exec $PROJECT_ID "cat artists/artist_{id}/metadata.yaml"
131
+ royaltyport project exec $PROJECT_ID "cat artists/artist_{id}/merged.yaml"
132
+ ```
133
+
134
+ ### Find recordings and compositions
135
+
136
+ ```bash
137
+ # Search by name or ISRC/ISWC
138
+ royaltyport project exec $PROJECT_ID "grep -rl 'SEARCH_TERM' recordings/"
139
+ royaltyport project exec $PROJECT_ID "grep -rl 'ISWC_CODE' compositions/"
140
+
141
+ # Read recording details
142
+ royaltyport project exec $PROJECT_ID "cat recordings/recording_{id}/metadata.yaml"
143
+ ```
144
+
145
+ ### Browse statements
146
+
147
+ ```bash
148
+ # List statements
149
+ royaltyport project exec $PROJECT_ID "ls statements/ | head -20"
150
+
151
+ # Read statement metadata
152
+ royaltyport project exec $PROJECT_ID "cat statements/statement_{id}/metadata.yaml"
153
+
154
+ # Read matched recording assets on a statement
155
+ royaltyport project exec $PROJECT_ID "cat statements/recordings/statement_{id}/assets.yaml"
156
+ ```
157
+
158
+ ## Tips
159
+
160
+ - **Always start with `stats.yaml`** to understand how much data the project has.
161
+ - **Use `grep -rl`** for searching across files, `grep -rh` for extracting values.
162
+ - **Check `merged.yaml`** — artists, writers, and entities may have duplicates merged into a root record.
163
+ - **All data is plain-text YAML** — standard Unix tools (`grep`, `cat`, `find`, `wc`, `sort`, `head`) all work.
164
+ - **Contract extractions** under `extracted/` are AI-extracted terms. One YAML file per category.
@@ -0,0 +1,54 @@
1
+ import { createInterface } from 'node:readline/promises';
2
+ import ora from 'ora';
3
+ import { apiGet } from '../lib/api.js';
4
+ import { setToken, setApiUrl, getConfigPath } from '../lib/config.js';
5
+ import { printError, printSuccess, printInfo } from '../lib/output.js';
6
+ import { spinnerColor, dim } from '../lib/theme.js';
7
+
8
+ export function registerLoginCommand(program) {
9
+ program
10
+ .command('login')
11
+ .description('Authenticate with your Royaltyport API token')
12
+ .option('-t, --token <token>', 'API token (rp_...)')
13
+ .option('--api-url <url>', 'Custom API URL (default: https://api.royaltyport.com)')
14
+ .action(async (options) => {
15
+ try {
16
+ let token = options.token;
17
+
18
+ if (!token) {
19
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
20
+ token = await rl.question('Enter your API token (rp_...): ');
21
+ rl.close();
22
+ }
23
+
24
+ token = token.trim();
25
+ if (!token) {
26
+ printError('Token cannot be empty.');
27
+ process.exit(1);
28
+ }
29
+
30
+ if (options.apiUrl) {
31
+ setApiUrl(options.apiUrl);
32
+ }
33
+
34
+ const spinner = ora({ text: 'Validating token...', color: spinnerColor }).start();
35
+
36
+ try {
37
+ const response = await apiGet('/v1/projects', token);
38
+ const projectCount = Array.isArray(response.data) ? response.data.length : 0;
39
+ spinner.succeed(`Authenticated successfully (${projectCount} project${projectCount !== 1 ? 's' : ''} accessible)`);
40
+ } catch (err) {
41
+ spinner.fail('Token validation failed');
42
+ printError(err.message);
43
+ process.exit(1);
44
+ }
45
+
46
+ setToken(token);
47
+ printSuccess('Token saved.');
48
+ printInfo(`Config stored at ${dim(getConfigPath())}`);
49
+ } catch (err) {
50
+ printError(err.message);
51
+ process.exit(1);
52
+ }
53
+ });
54
+ }
@@ -0,0 +1,12 @@
1
+ import { clearConfig } from '../lib/config.js';
2
+ import { printSuccess } from '../lib/output.js';
3
+
4
+ export function registerLogoutCommand(program) {
5
+ program
6
+ .command('logout')
7
+ .description('Clear stored credentials')
8
+ .action(() => {
9
+ clearConfig();
10
+ printSuccess('Logged out. Credentials cleared.');
11
+ });
12
+ }
@@ -0,0 +1,60 @@
1
+ import ora from 'ora';
2
+ import { apiPost, apiGet, requireAuth } from '../lib/api.js';
3
+ import { printError, printInfo } from '../lib/output.js';
4
+ import { warning, spinnerColor } from '../lib/theme.js';
5
+
6
+ export function registerProjectCommand(program) {
7
+ const project = program
8
+ .command('project')
9
+ .description('Use bash commands to explore a project filesystem');
10
+
11
+ project
12
+ .command('info')
13
+ .description('Show the AGENTS.md for a project sandbox (filesystem overview and instructions)')
14
+ .argument('<project_id>', 'Project ID')
15
+ .action(async (projectId) => {
16
+ try {
17
+ requireAuth();
18
+
19
+ const spinner = ora({ text: 'Connecting to sandbox...', color: spinnerColor }).start();
20
+ await apiPost(`/v1/projects/${projectId}/sandbox/connect`, {});
21
+ spinner.text = 'Reading AGENTS.md...';
22
+
23
+ const response = await apiGet(`/v1/projects/${projectId}/sandbox/files?path=${encodeURIComponent('AGENTS.md')}`);
24
+ spinner.stop();
25
+
26
+ if (response.data?.type === 'file' && response.data.content) {
27
+ console.log(response.data.content);
28
+ } else {
29
+ printInfo('No AGENTS.md found in this project sandbox.');
30
+ }
31
+ } catch (err) {
32
+ printError(err.message);
33
+ process.exit(1);
34
+ }
35
+ });
36
+
37
+ project
38
+ .command('exec')
39
+ .description('Execute a bash command in a project sandbox')
40
+ .argument('<project_id>', 'Project ID')
41
+ .argument('<command>', 'Bash command to execute')
42
+ .action(async (projectId, command) => {
43
+ try {
44
+ requireAuth();
45
+
46
+ await apiPost(`/v1/projects/${projectId}/sandbox/connect`, {});
47
+
48
+ const response = await apiPost(`/v1/projects/${projectId}/sandbox/exec`, { command });
49
+ const { stdout, stderr, exitCode } = response.data;
50
+
51
+ if (stdout) process.stdout.write(stdout);
52
+ if (stderr) process.stderr.write(warning(stderr));
53
+
54
+ process.exit(exitCode);
55
+ } catch (err) {
56
+ printError(err.message);
57
+ process.exit(1);
58
+ }
59
+ });
60
+ }
@@ -0,0 +1,36 @@
1
+ import ora from 'ora';
2
+ import { apiGet, requireAuth } from '../lib/api.js';
3
+ import { printTable, printError, printInfo } from '../lib/output.js';
4
+ import { spinnerColor } from '../lib/theme.js';
5
+
6
+ export function registerProjectsCommand(program) {
7
+ program
8
+ .command('projects')
9
+ .description('List available projects')
10
+ .action(async () => {
11
+ try {
12
+ requireAuth();
13
+
14
+ const spinner = ora({ text: 'Fetching projects...', color: spinnerColor }).start();
15
+ const response = await apiGet('/v1/projects');
16
+ spinner.stop();
17
+
18
+ const projects = response.data;
19
+ if (!projects || projects.length === 0) {
20
+ printInfo('No projects found.');
21
+ return;
22
+ }
23
+
24
+ const rows = projects.map((p) => [
25
+ p.id,
26
+ p.name,
27
+ new Date(p.created_at).toLocaleDateString(),
28
+ ]);
29
+
30
+ printTable(['ID', 'Name', 'Created'], rows);
31
+ } catch (err) {
32
+ printError(err.message);
33
+ process.exit(1);
34
+ }
35
+ });
36
+ }
package/src/lib/api.js ADDED
@@ -0,0 +1,79 @@
1
+ import { getToken, getApiUrl } from './config.js';
2
+
3
+ class ApiError extends Error {
4
+ constructor(message, status, body) {
5
+ super(message);
6
+ this.name = 'ApiError';
7
+ this.status = status;
8
+ this.body = body;
9
+ }
10
+ }
11
+
12
+ function buildHeaders(token) {
13
+ return {
14
+ 'Authorization': `Bearer ${token}`,
15
+ 'Content-Type': 'application/json',
16
+ };
17
+ }
18
+
19
+ async function parseResponse(res) {
20
+ const text = await res.text();
21
+ try {
22
+ return JSON.parse(text);
23
+ } catch {
24
+ return { message: text };
25
+ }
26
+ }
27
+
28
+ export function requireAuth() {
29
+ const token = getToken();
30
+ if (!token) {
31
+ throw new ApiError('Not authenticated. Run `royaltyport login` first.', 401);
32
+ }
33
+ return token;
34
+ }
35
+
36
+ export async function apiGet(path, token) {
37
+ const baseUrl = getApiUrl();
38
+ const res = await fetch(`${baseUrl}${path}`, {
39
+ method: 'GET',
40
+ headers: buildHeaders(token || requireAuth()),
41
+ });
42
+ const body = await parseResponse(res);
43
+ if (!res.ok) {
44
+ const msg = body?.error?.message || body?.message || `Request failed with status ${res.status}`;
45
+ throw new ApiError(msg, res.status, body);
46
+ }
47
+ return body;
48
+ }
49
+
50
+ export async function apiPost(path, data, token) {
51
+ const baseUrl = getApiUrl();
52
+ const res = await fetch(`${baseUrl}${path}`, {
53
+ method: 'POST',
54
+ headers: buildHeaders(token || requireAuth()),
55
+ body: JSON.stringify(data),
56
+ });
57
+ const body = await parseResponse(res);
58
+ if (!res.ok) {
59
+ const msg = body?.error?.message || body?.message || `Request failed with status ${res.status}`;
60
+ throw new ApiError(msg, res.status, body);
61
+ }
62
+ return body;
63
+ }
64
+
65
+ export async function apiDelete(path, token) {
66
+ const baseUrl = getApiUrl();
67
+ const res = await fetch(`${baseUrl}${path}`, {
68
+ method: 'DELETE',
69
+ headers: buildHeaders(token || requireAuth()),
70
+ });
71
+ const body = await parseResponse(res);
72
+ if (!res.ok) {
73
+ const msg = body?.error?.message || body?.message || `Request failed with status ${res.status}`;
74
+ throw new ApiError(msg, res.status, body);
75
+ }
76
+ return body;
77
+ }
78
+
79
+ export { ApiError };
@@ -0,0 +1,33 @@
1
+ import Conf from 'conf';
2
+
3
+ const config = new Conf({
4
+ projectName: 'royaltyport',
5
+ schema: {
6
+ token: { type: 'string', default: '' },
7
+ apiUrl: { type: 'string', default: 'https://api.royaltyport.com' },
8
+ },
9
+ });
10
+
11
+ export function getToken() {
12
+ return process.env.ROYALTYPORT_TOKEN || config.get('token');
13
+ }
14
+
15
+ export function setToken(token) {
16
+ config.set('token', token);
17
+ }
18
+
19
+ export function getApiUrl() {
20
+ return process.env.ROYALTYPORT_API_URL || config.get('apiUrl');
21
+ }
22
+
23
+ export function setApiUrl(url) {
24
+ config.set('apiUrl', url);
25
+ }
26
+
27
+ export function clearConfig() {
28
+ config.clear();
29
+ }
30
+
31
+ export function getConfigPath() {
32
+ return config.path;
33
+ }
@@ -0,0 +1,31 @@
1
+ import { brand, brandBold, dim, error, accent } from './theme.js';
2
+
3
+ export function printTable(columns, rows) {
4
+ const widths = columns.map((col, i) => {
5
+ const maxData = rows.reduce((max, row) => Math.max(max, String(row[i] ?? '').length), 0);
6
+ return Math.max(col.length, maxData);
7
+ });
8
+
9
+ const header = columns.map((col, i) => col.padEnd(widths[i])).join(' ');
10
+ const separator = widths.map(w => '─'.repeat(w)).join('──');
11
+
12
+ console.log(brandBold(header));
13
+ console.log(dim(separator));
14
+
15
+ for (const row of rows) {
16
+ const line = row.map((cell, i) => String(cell ?? '').padEnd(widths[i])).join(' ');
17
+ console.log(line);
18
+ }
19
+ }
20
+
21
+ export function printError(message) {
22
+ console.error(error(`Error: ${message}`));
23
+ }
24
+
25
+ export function printSuccess(message) {
26
+ console.log(brand(message));
27
+ }
28
+
29
+ export function printInfo(message) {
30
+ console.log(accent(message));
31
+ }
@@ -0,0 +1,14 @@
1
+ import chalk from 'chalk';
2
+
3
+ export const brand = chalk.green;
4
+ export const accent = chalk.cyan;
5
+ export const dim = chalk.dim;
6
+ export const bold = chalk.bold;
7
+ export const error = chalk.red;
8
+ export const warning = chalk.yellow;
9
+
10
+ export const spinnerColor = 'white';
11
+
12
+ export function brandBold(text) {
13
+ return chalk.bold(text);
14
+ }