@safenest/cli 1.0.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.
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 20
20
+
21
+ - name: Install dependencies
22
+ run: npm install
23
+
24
+ - name: Build
25
+ run: npm run build
26
+
27
+ - name: Check package
28
+ run: npm pack --dry-run
@@ -0,0 +1,29 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Setup Node.js
15
+ uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 20
18
+ registry-url: 'https://registry.npmjs.org'
19
+
20
+ - name: Install dependencies
21
+ run: npm install
22
+
23
+ - name: Build
24
+ run: npm run build
25
+
26
+ - name: Publish to npm
27
+ run: npm publish --access public
28
+ env:
29
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # SafeNest CLI
2
+
3
+ AI-powered child safety analysis from your terminal. Detect bullying, grooming, and unsafe content.
4
+
5
+ ## Installation
6
+
7
+ ### Homebrew (macOS/Linux)
8
+
9
+ ```bash
10
+ brew install safenest/tap/safenest
11
+ ```
12
+
13
+ ### npm
14
+
15
+ ```bash
16
+ npm install -g @safenest/cli
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Login with your API key
23
+ safenest login <your-api-key>
24
+
25
+ # Analyze text for safety
26
+ safenest analyze "Some text to check"
27
+
28
+ # Detect bullying
29
+ safenest detect-bullying "You're so stupid"
30
+
31
+ # Detect unsafe content
32
+ safenest detect-unsafe "I want to hurt myself"
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ ### Authentication
38
+
39
+ ```bash
40
+ safenest login <api-key> # Save your API key
41
+ safenest logout # Remove saved API key
42
+ safenest whoami # Show login status
43
+ ```
44
+
45
+ ### Safety Detection
46
+
47
+ ```bash
48
+ # Quick analysis (bullying + unsafe)
49
+ safenest analyze "Text to analyze"
50
+
51
+ # Detect bullying/harassment
52
+ safenest detect-bullying "Text to check"
53
+ safenest bullying "Text to check" # alias
54
+
55
+ # Detect unsafe content (self-harm, violence, etc.)
56
+ safenest detect-unsafe "Text to check"
57
+ safenest unsafe "Text to check" # alias
58
+
59
+ # Detect grooming patterns in conversation
60
+ safenest detect-grooming -m '[{"role":"adult","content":"..."},{"role":"child","content":"..."}]'
61
+ safenest grooming -m '...' --age 12 # with child age
62
+ ```
63
+
64
+ ### Analysis & Guidance
65
+
66
+ ```bash
67
+ # Analyze emotions
68
+ safenest emotions "I'm feeling really sad today"
69
+
70
+ # Get action plan for a situation
71
+ safenest action-plan "Child is being bullied at school"
72
+ safenest plan "..." --age 12 --audience parent --severity high
73
+ ```
74
+
75
+ ## Examples
76
+
77
+ ### Check a message for bullying
78
+
79
+ ```bash
80
+ $ safenest bullying "You're worthless and nobody likes you"
81
+
82
+ ⚠ BULLYING DETECTED
83
+
84
+ Severity: HIGH
85
+ Confidence: 92%
86
+ Risk Score: 85%
87
+ Types: verbal, exclusion
88
+
89
+ Rationale:
90
+ The message contains degrading language and exclusionary statements...
91
+
92
+ Action: flag_for_moderator
93
+ ```
94
+
95
+ ### Analyze emotional content
96
+
97
+ ```bash
98
+ $ safenest emotions "I don't want to go to school anymore, everyone hates me"
99
+
100
+ Emotion Analysis
101
+
102
+ Dominant: sadness, anxiety, isolation
103
+ Trend: 📉 worsening
104
+
105
+ Summary:
106
+ The text indicates feelings of social rejection and school avoidance...
107
+
108
+ Recommended Follow-up:
109
+ Consider having a supportive conversation about their school experience...
110
+ ```
111
+
112
+ ## Get an API Key
113
+
114
+ Sign up at [safenest.dev](https://safenest.dev) to get your API key.
115
+
116
+ ## License
117
+
118
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import Conf from 'conf';
6
+ import { SafeNestClient } from '@safenest/sdk';
7
+ const config = new Conf({ projectName: 'safenest' });
8
+ const VERSION = '1.0.0';
9
+ function getClient() {
10
+ const apiKey = config.get('apiKey');
11
+ if (!apiKey) {
12
+ console.error(chalk.red('Not logged in. Run: safenest login <api-key>'));
13
+ process.exit(1);
14
+ }
15
+ return new SafeNestClient(apiKey);
16
+ }
17
+ function formatSeverity(severity) {
18
+ const colors = {
19
+ low: chalk.yellow,
20
+ medium: chalk.hex('#FFA500'),
21
+ high: chalk.red,
22
+ critical: chalk.bgRed.white,
23
+ };
24
+ return (colors[severity] || chalk.white)(severity.toUpperCase());
25
+ }
26
+ function formatRisk(risk) {
27
+ const colors = {
28
+ none: chalk.green,
29
+ safe: chalk.green,
30
+ low: chalk.yellow,
31
+ medium: chalk.hex('#FFA500'),
32
+ high: chalk.red,
33
+ critical: chalk.bgRed.white,
34
+ };
35
+ return (colors[risk] || chalk.white)(risk.toUpperCase());
36
+ }
37
+ const program = new Command();
38
+ program
39
+ .name('safenest')
40
+ .description('SafeNest CLI - AI-powered child safety analysis')
41
+ .version(VERSION);
42
+ // Login command
43
+ program
44
+ .command('login <api-key>')
45
+ .description('Save your API key')
46
+ .action((apiKey) => {
47
+ config.set('apiKey', apiKey);
48
+ console.log(chalk.green('✓ API key saved successfully!'));
49
+ console.log(chalk.dim('Your key is stored locally at: ' + config.path));
50
+ });
51
+ // Logout command
52
+ program
53
+ .command('logout')
54
+ .description('Remove saved API key')
55
+ .action(() => {
56
+ config.delete('apiKey');
57
+ console.log(chalk.green('✓ Logged out successfully!'));
58
+ });
59
+ // Whoami command
60
+ program
61
+ .command('whoami')
62
+ .description('Show current login status')
63
+ .action(() => {
64
+ const apiKey = config.get('apiKey');
65
+ if (apiKey) {
66
+ const masked = apiKey.slice(0, 8) + '...' + apiKey.slice(-4);
67
+ console.log(chalk.green('✓ Logged in'));
68
+ console.log(chalk.dim('API Key: ' + masked));
69
+ }
70
+ else {
71
+ console.log(chalk.yellow('Not logged in'));
72
+ console.log(chalk.dim('Run: safenest login <api-key>'));
73
+ }
74
+ });
75
+ // Detect bullying
76
+ program
77
+ .command('detect-bullying <text>')
78
+ .alias('bullying')
79
+ .description('Analyze text for bullying or harassment')
80
+ .action(async (text) => {
81
+ const spinner = ora('Analyzing...').start();
82
+ try {
83
+ const client = getClient();
84
+ const result = await client.detectBullying({ content: text });
85
+ spinner.stop();
86
+ console.log();
87
+ if (result.is_bullying) {
88
+ console.log(chalk.red.bold('⚠ BULLYING DETECTED'));
89
+ }
90
+ else {
91
+ console.log(chalk.green.bold('✓ No bullying detected'));
92
+ }
93
+ console.log();
94
+ console.log(`Severity: ${formatSeverity(result.severity)}`);
95
+ console.log(`Confidence: ${chalk.cyan((result.confidence * 100).toFixed(0) + '%')}`);
96
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
97
+ if (result.bullying_type?.length > 0) {
98
+ console.log(`Types: ${result.bullying_type.join(', ')}`);
99
+ }
100
+ console.log();
101
+ console.log(chalk.dim('Rationale:'));
102
+ console.log(result.rationale);
103
+ console.log();
104
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
105
+ }
106
+ catch (error) {
107
+ spinner.stop();
108
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
109
+ process.exit(1);
110
+ }
111
+ });
112
+ // Detect grooming
113
+ program
114
+ .command('detect-grooming')
115
+ .alias('grooming')
116
+ .description('Analyze conversation for grooming patterns')
117
+ .requiredOption('-m, --messages <json>', 'Messages as JSON array: [{"role":"adult","content":"..."}]')
118
+ .option('-a, --age <number>', 'Child age')
119
+ .action(async (options) => {
120
+ const spinner = ora('Analyzing...').start();
121
+ try {
122
+ const client = getClient();
123
+ const messages = JSON.parse(options.messages);
124
+ const result = await client.detectGrooming({
125
+ messages,
126
+ childAge: options.age ? parseInt(options.age) : undefined,
127
+ });
128
+ spinner.stop();
129
+ console.log();
130
+ if (result.grooming_risk !== 'none') {
131
+ console.log(chalk.red.bold('⚠ GROOMING RISK DETECTED'));
132
+ }
133
+ else {
134
+ console.log(chalk.green.bold('✓ No grooming detected'));
135
+ }
136
+ console.log();
137
+ console.log(`Risk Level: ${formatRisk(result.grooming_risk)}`);
138
+ console.log(`Confidence: ${chalk.cyan((result.confidence * 100).toFixed(0) + '%')}`);
139
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
140
+ if (result.flags?.length > 0) {
141
+ console.log();
142
+ console.log(chalk.dim('Warning Flags:'));
143
+ result.flags.forEach((flag) => console.log(chalk.yellow(` • ${flag}`)));
144
+ }
145
+ console.log();
146
+ console.log(chalk.dim('Rationale:'));
147
+ console.log(result.rationale);
148
+ console.log();
149
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
150
+ }
151
+ catch (error) {
152
+ spinner.stop();
153
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
154
+ process.exit(1);
155
+ }
156
+ });
157
+ // Detect unsafe content
158
+ program
159
+ .command('detect-unsafe <text>')
160
+ .alias('unsafe')
161
+ .description('Detect self-harm, violence, or other unsafe content')
162
+ .action(async (text) => {
163
+ const spinner = ora('Analyzing...').start();
164
+ try {
165
+ const client = getClient();
166
+ const result = await client.detectUnsafe({ content: text });
167
+ spinner.stop();
168
+ console.log();
169
+ if (result.unsafe) {
170
+ console.log(chalk.red.bold('⚠ UNSAFE CONTENT DETECTED'));
171
+ }
172
+ else {
173
+ console.log(chalk.green.bold('✓ Content is safe'));
174
+ }
175
+ console.log();
176
+ console.log(`Severity: ${formatSeverity(result.severity)}`);
177
+ console.log(`Confidence: ${chalk.cyan((result.confidence * 100).toFixed(0) + '%')}`);
178
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
179
+ if (result.categories?.length > 0) {
180
+ console.log(`Categories: ${result.categories.join(', ')}`);
181
+ }
182
+ console.log();
183
+ console.log(chalk.dim('Rationale:'));
184
+ console.log(result.rationale);
185
+ console.log();
186
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
187
+ }
188
+ catch (error) {
189
+ spinner.stop();
190
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
191
+ process.exit(1);
192
+ }
193
+ });
194
+ // Quick analyze
195
+ program
196
+ .command('analyze <text>')
197
+ .description('Quick safety analysis (bullying + unsafe)')
198
+ .action(async (text) => {
199
+ const spinner = ora('Analyzing...').start();
200
+ try {
201
+ const client = getClient();
202
+ const result = await client.analyze({ content: text });
203
+ spinner.stop();
204
+ console.log();
205
+ if (result.risk_level === 'safe') {
206
+ console.log(chalk.green.bold('✓ Content appears safe'));
207
+ }
208
+ else {
209
+ console.log(chalk.red.bold('⚠ SAFETY CONCERNS DETECTED'));
210
+ }
211
+ console.log();
212
+ console.log(`Risk Level: ${formatRisk(result.risk_level)}`);
213
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
214
+ console.log();
215
+ console.log(chalk.dim('Summary:'));
216
+ console.log(result.summary);
217
+ console.log();
218
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
219
+ }
220
+ catch (error) {
221
+ spinner.stop();
222
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
223
+ process.exit(1);
224
+ }
225
+ });
226
+ // Analyze emotions
227
+ program
228
+ .command('emotions <text>')
229
+ .description('Analyze emotional content')
230
+ .action(async (text) => {
231
+ const spinner = ora('Analyzing...').start();
232
+ try {
233
+ const client = getClient();
234
+ const result = await client.analyzeEmotions({ content: text });
235
+ spinner.stop();
236
+ const trendEmoji = {
237
+ improving: '📈',
238
+ stable: '➡️',
239
+ worsening: '📉',
240
+ };
241
+ console.log();
242
+ console.log(chalk.bold('Emotion Analysis'));
243
+ console.log();
244
+ console.log(`Dominant: ${chalk.cyan(result.dominant_emotions.join(', '))}`);
245
+ console.log(`Trend: ${trendEmoji[result.trend] || ''} ${result.trend}`);
246
+ console.log();
247
+ console.log(chalk.dim('Summary:'));
248
+ console.log(result.summary);
249
+ console.log();
250
+ console.log(chalk.dim('Recommended Follow-up:'));
251
+ console.log(result.recommended_followup);
252
+ }
253
+ catch (error) {
254
+ spinner.stop();
255
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
256
+ process.exit(1);
257
+ }
258
+ });
259
+ // Get action plan
260
+ program
261
+ .command('action-plan <situation>')
262
+ .alias('plan')
263
+ .description('Get guidance for handling a situation')
264
+ .option('-a, --age <number>', 'Child age')
265
+ .option('-t, --audience <type>', 'Audience: child, parent, educator, platform', 'parent')
266
+ .option('-s, --severity <level>', 'Severity: low, medium, high, critical')
267
+ .action(async (situation, options) => {
268
+ const spinner = ora('Generating action plan...').start();
269
+ try {
270
+ const client = getClient();
271
+ const result = await client.getActionPlan({
272
+ situation,
273
+ childAge: options.age ? parseInt(options.age) : undefined,
274
+ audience: options.audience,
275
+ severity: options.severity,
276
+ });
277
+ spinner.stop();
278
+ console.log();
279
+ console.log(chalk.bold('Action Plan'));
280
+ console.log(chalk.dim(`For: ${result.audience} | Tone: ${result.tone}`));
281
+ console.log();
282
+ console.log(chalk.dim('Steps:'));
283
+ result.steps.forEach((step, i) => {
284
+ console.log(chalk.cyan(` ${i + 1}. `) + step);
285
+ });
286
+ }
287
+ catch (error) {
288
+ spinner.stop();
289
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
290
+ process.exit(1);
291
+ }
292
+ });
293
+ program.parse();
294
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@safenest/cli",
3
+ "version": "1.0.0",
4
+ "description": "SafeNest CLI - AI-powered child safety analysis from your terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "safenest": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsx src/index.ts",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "safenest",
16
+ "cli",
17
+ "child-safety",
18
+ "content-moderation",
19
+ "ai"
20
+ ],
21
+ "author": "SafeNest <sales@safenest.dev>",
22
+ "license": "MIT",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/SafeNestSDK/cli.git"
26
+ },
27
+ "homepage": "https://safenest.dev",
28
+ "dependencies": {
29
+ "@safenest/sdk": "^1.0.0",
30
+ "chalk": "^5.3.0",
31
+ "commander": "^12.0.0",
32
+ "conf": "^12.0.0",
33
+ "ora": "^8.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22.10.2",
37
+ "tsx": "^4.19.2",
38
+ "typescript": "^5.7.2"
39
+ },
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ }
43
+ }
package/src/index.ts ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import Conf from 'conf';
7
+ import { SafeNestClient } from '@safenest/sdk';
8
+
9
+ const config = new Conf({ projectName: 'safenest' });
10
+ const VERSION = '1.0.0';
11
+
12
+ function getClient(): SafeNestClient {
13
+ const apiKey = config.get('apiKey') as string;
14
+ if (!apiKey) {
15
+ console.error(chalk.red('Not logged in. Run: safenest login <api-key>'));
16
+ process.exit(1);
17
+ }
18
+ return new SafeNestClient(apiKey);
19
+ }
20
+
21
+ function formatSeverity(severity: string): string {
22
+ const colors: Record<string, (s: string) => string> = {
23
+ low: chalk.yellow,
24
+ medium: chalk.hex('#FFA500'),
25
+ high: chalk.red,
26
+ critical: chalk.bgRed.white,
27
+ };
28
+ return (colors[severity] || chalk.white)(severity.toUpperCase());
29
+ }
30
+
31
+ function formatRisk(risk: string): string {
32
+ const colors: Record<string, (s: string) => string> = {
33
+ none: chalk.green,
34
+ safe: chalk.green,
35
+ low: chalk.yellow,
36
+ medium: chalk.hex('#FFA500'),
37
+ high: chalk.red,
38
+ critical: chalk.bgRed.white,
39
+ };
40
+ return (colors[risk] || chalk.white)(risk.toUpperCase());
41
+ }
42
+
43
+ const program = new Command();
44
+
45
+ program
46
+ .name('safenest')
47
+ .description('SafeNest CLI - AI-powered child safety analysis')
48
+ .version(VERSION);
49
+
50
+ // Login command
51
+ program
52
+ .command('login <api-key>')
53
+ .description('Save your API key')
54
+ .action((apiKey: string) => {
55
+ config.set('apiKey', apiKey);
56
+ console.log(chalk.green('✓ API key saved successfully!'));
57
+ console.log(chalk.dim('Your key is stored locally at: ' + config.path));
58
+ });
59
+
60
+ // Logout command
61
+ program
62
+ .command('logout')
63
+ .description('Remove saved API key')
64
+ .action(() => {
65
+ config.delete('apiKey');
66
+ console.log(chalk.green('✓ Logged out successfully!'));
67
+ });
68
+
69
+ // Whoami command
70
+ program
71
+ .command('whoami')
72
+ .description('Show current login status')
73
+ .action(() => {
74
+ const apiKey = config.get('apiKey') as string;
75
+ if (apiKey) {
76
+ const masked = apiKey.slice(0, 8) + '...' + apiKey.slice(-4);
77
+ console.log(chalk.green('✓ Logged in'));
78
+ console.log(chalk.dim('API Key: ' + masked));
79
+ } else {
80
+ console.log(chalk.yellow('Not logged in'));
81
+ console.log(chalk.dim('Run: safenest login <api-key>'));
82
+ }
83
+ });
84
+
85
+ // Detect bullying
86
+ program
87
+ .command('detect-bullying <text>')
88
+ .alias('bullying')
89
+ .description('Analyze text for bullying or harassment')
90
+ .action(async (text: string) => {
91
+ const spinner = ora('Analyzing...').start();
92
+ try {
93
+ const client = getClient();
94
+ const result = await client.detectBullying({ content: text });
95
+ spinner.stop();
96
+
97
+ console.log();
98
+ if (result.is_bullying) {
99
+ console.log(chalk.red.bold('⚠ BULLYING DETECTED'));
100
+ } else {
101
+ console.log(chalk.green.bold('✓ No bullying detected'));
102
+ }
103
+ console.log();
104
+ console.log(`Severity: ${formatSeverity(result.severity)}`);
105
+ console.log(`Confidence: ${chalk.cyan((result.confidence * 100).toFixed(0) + '%')}`);
106
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
107
+ if (result.bullying_type?.length > 0) {
108
+ console.log(`Types: ${result.bullying_type.join(', ')}`);
109
+ }
110
+ console.log();
111
+ console.log(chalk.dim('Rationale:'));
112
+ console.log(result.rationale);
113
+ console.log();
114
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
115
+ } catch (error) {
116
+ spinner.stop();
117
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
118
+ process.exit(1);
119
+ }
120
+ });
121
+
122
+ // Detect grooming
123
+ program
124
+ .command('detect-grooming')
125
+ .alias('grooming')
126
+ .description('Analyze conversation for grooming patterns')
127
+ .requiredOption('-m, --messages <json>', 'Messages as JSON array: [{"role":"adult","content":"..."}]')
128
+ .option('-a, --age <number>', 'Child age')
129
+ .action(async (options) => {
130
+ const spinner = ora('Analyzing...').start();
131
+ try {
132
+ const client = getClient();
133
+ const messages = JSON.parse(options.messages);
134
+ const result = await client.detectGrooming({
135
+ messages,
136
+ childAge: options.age ? parseInt(options.age) : undefined,
137
+ });
138
+ spinner.stop();
139
+
140
+ console.log();
141
+ if (result.grooming_risk !== 'none') {
142
+ console.log(chalk.red.bold('⚠ GROOMING RISK DETECTED'));
143
+ } else {
144
+ console.log(chalk.green.bold('✓ No grooming detected'));
145
+ }
146
+ console.log();
147
+ console.log(`Risk Level: ${formatRisk(result.grooming_risk)}`);
148
+ console.log(`Confidence: ${chalk.cyan((result.confidence * 100).toFixed(0) + '%')}`);
149
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
150
+ if (result.flags?.length > 0) {
151
+ console.log();
152
+ console.log(chalk.dim('Warning Flags:'));
153
+ result.flags.forEach((flag: string) => console.log(chalk.yellow(` • ${flag}`)));
154
+ }
155
+ console.log();
156
+ console.log(chalk.dim('Rationale:'));
157
+ console.log(result.rationale);
158
+ console.log();
159
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
160
+ } catch (error) {
161
+ spinner.stop();
162
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
163
+ process.exit(1);
164
+ }
165
+ });
166
+
167
+ // Detect unsafe content
168
+ program
169
+ .command('detect-unsafe <text>')
170
+ .alias('unsafe')
171
+ .description('Detect self-harm, violence, or other unsafe content')
172
+ .action(async (text: string) => {
173
+ const spinner = ora('Analyzing...').start();
174
+ try {
175
+ const client = getClient();
176
+ const result = await client.detectUnsafe({ content: text });
177
+ spinner.stop();
178
+
179
+ console.log();
180
+ if (result.unsafe) {
181
+ console.log(chalk.red.bold('⚠ UNSAFE CONTENT DETECTED'));
182
+ } else {
183
+ console.log(chalk.green.bold('✓ Content is safe'));
184
+ }
185
+ console.log();
186
+ console.log(`Severity: ${formatSeverity(result.severity)}`);
187
+ console.log(`Confidence: ${chalk.cyan((result.confidence * 100).toFixed(0) + '%')}`);
188
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
189
+ if (result.categories?.length > 0) {
190
+ console.log(`Categories: ${result.categories.join(', ')}`);
191
+ }
192
+ console.log();
193
+ console.log(chalk.dim('Rationale:'));
194
+ console.log(result.rationale);
195
+ console.log();
196
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
197
+ } catch (error) {
198
+ spinner.stop();
199
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
200
+ process.exit(1);
201
+ }
202
+ });
203
+
204
+ // Quick analyze
205
+ program
206
+ .command('analyze <text>')
207
+ .description('Quick safety analysis (bullying + unsafe)')
208
+ .action(async (text: string) => {
209
+ const spinner = ora('Analyzing...').start();
210
+ try {
211
+ const client = getClient();
212
+ const result = await client.analyze({ content: text });
213
+ spinner.stop();
214
+
215
+ console.log();
216
+ if (result.risk_level === 'safe') {
217
+ console.log(chalk.green.bold('✓ Content appears safe'));
218
+ } else {
219
+ console.log(chalk.red.bold('⚠ SAFETY CONCERNS DETECTED'));
220
+ }
221
+ console.log();
222
+ console.log(`Risk Level: ${formatRisk(result.risk_level)}`);
223
+ console.log(`Risk Score: ${chalk.cyan((result.risk_score * 100).toFixed(0) + '%')}`);
224
+ console.log();
225
+ console.log(chalk.dim('Summary:'));
226
+ console.log(result.summary);
227
+ console.log();
228
+ console.log(`Action: ${chalk.yellow(result.recommended_action)}`);
229
+ } catch (error) {
230
+ spinner.stop();
231
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
232
+ process.exit(1);
233
+ }
234
+ });
235
+
236
+ // Analyze emotions
237
+ program
238
+ .command('emotions <text>')
239
+ .description('Analyze emotional content')
240
+ .action(async (text: string) => {
241
+ const spinner = ora('Analyzing...').start();
242
+ try {
243
+ const client = getClient();
244
+ const result = await client.analyzeEmotions({ content: text });
245
+ spinner.stop();
246
+
247
+ const trendEmoji: Record<string, string> = {
248
+ improving: '📈',
249
+ stable: '➡️',
250
+ worsening: '📉',
251
+ };
252
+
253
+ console.log();
254
+ console.log(chalk.bold('Emotion Analysis'));
255
+ console.log();
256
+ console.log(`Dominant: ${chalk.cyan(result.dominant_emotions.join(', '))}`);
257
+ console.log(`Trend: ${trendEmoji[result.trend] || ''} ${result.trend}`);
258
+ console.log();
259
+ console.log(chalk.dim('Summary:'));
260
+ console.log(result.summary);
261
+ console.log();
262
+ console.log(chalk.dim('Recommended Follow-up:'));
263
+ console.log(result.recommended_followup);
264
+ } catch (error) {
265
+ spinner.stop();
266
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
267
+ process.exit(1);
268
+ }
269
+ });
270
+
271
+ // Get action plan
272
+ program
273
+ .command('action-plan <situation>')
274
+ .alias('plan')
275
+ .description('Get guidance for handling a situation')
276
+ .option('-a, --age <number>', 'Child age')
277
+ .option('-t, --audience <type>', 'Audience: child, parent, educator, platform', 'parent')
278
+ .option('-s, --severity <level>', 'Severity: low, medium, high, critical')
279
+ .action(async (situation: string, options) => {
280
+ const spinner = ora('Generating action plan...').start();
281
+ try {
282
+ const client = getClient();
283
+ const result = await client.getActionPlan({
284
+ situation,
285
+ childAge: options.age ? parseInt(options.age) : undefined,
286
+ audience: options.audience,
287
+ severity: options.severity,
288
+ });
289
+ spinner.stop();
290
+
291
+ console.log();
292
+ console.log(chalk.bold('Action Plan'));
293
+ console.log(chalk.dim(`For: ${result.audience} | Tone: ${result.tone}`));
294
+ console.log();
295
+ console.log(chalk.dim('Steps:'));
296
+ result.steps.forEach((step: string, i: number) => {
297
+ console.log(chalk.cyan(` ${i + 1}. `) + step);
298
+ });
299
+ } catch (error) {
300
+ spinner.stop();
301
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
302
+ process.exit(1);
303
+ }
304
+ });
305
+
306
+ program.parse();
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }