kythia-core 0.10.1-beta → 0.11.1-beta

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 CHANGED
@@ -305,6 +305,92 @@ The core abandons traditional `sync()` operations in favor of a robust, **Larave
305
305
 
306
306
  -----
307
307
 
308
+ ## �️ CLI Tools & Commands
309
+
310
+ Kythia Core comes with a powerful CLI to streamline development, database management, and localization tasks.
311
+
312
+ Run `npx kythia --help` to see all available commands.
313
+
314
+ ### Database Management
315
+
316
+ #### `migrate`
317
+ Run pending database migrations.
318
+ ```bash
319
+ npx kythia migrate
320
+ ```
321
+ **Options:**
322
+ * `-f, --fresh`: **[DANGER]** Wipe the entire database (drop all tables) and re-run all migrations from scratch.
323
+ * `-r, --rollback`: Rollback the last *batch* of migrations.
324
+
325
+ #### `make:migration`
326
+ Create a new timestamped migration file in an addon.
327
+ ```bash
328
+ npx kythia make:migration --name create_users_table --addon core
329
+ ```
330
+ **Options:**
331
+ * `--name <string>`: Name of the migration (snake_case recommended).
332
+ * `--addon <string>`: Target addon name (must exist in `addons/`).
333
+
334
+ #### `make:model`
335
+ Scaffold a new Sequelize model file in an addon.
336
+ ```bash
337
+ npx kythia make:model --name User --addon core
338
+ ```
339
+ **Options:**
340
+ * `--name <string>`: Name of the model (PascalCase recommended).
341
+ * `--addon <string>`: Target addon name.
342
+
343
+ #### `cache:clear`
344
+ Flush Redis cache. Supports multi-instance selection if `REDIS_URLS` is configured.
345
+ ```bash
346
+ npx kythia cache:clear
347
+ ```
348
+
349
+ ### Localization (i18n)
350
+
351
+ #### `lang:check`
352
+ Lint translation key usage in your code against your language files.
353
+ ```bash
354
+ npx kythia lang:check
355
+ ```
356
+ * **Static Analysis:** Uses AST parsing to find `t('key')` calls.
357
+ * **Validation:** Reports missing keys in JSON files.
358
+ * **Unused Keys:** Reports keys defined in `en.json` but never used in code.
359
+
360
+ #### `lang:translate`
361
+ Auto-translate your `en.json` to a target language using Google Gemini AI.
362
+ ```bash
363
+ npx kythia lang:translate --target ja
364
+ ```
365
+ **Options:**
366
+ * `--target <lang>`: Target language code (default: `ja`).
367
+ * **Requires:** `GEMINI_API_KEYS` in `.env`.
368
+
369
+ ### Development Utilities
370
+
371
+ #### `dev:namespace`
372
+ Automatically add or update JSDoc `@namespace` headers in all project files.
373
+ ```bash
374
+ npx kythia dev:namespace
375
+ ```
376
+ Useful for maintaining consistent file documentation and ownership headers.
377
+
378
+ #### `gen:structure`
379
+ Generate a markdown tree representation of your project structure.
380
+ ```bash
381
+ npx kythia gen:structure
382
+ ```
383
+ Outputs to `temp/structure.md`. Great for documentation or sharing context with AI.
384
+
385
+ #### `version:up`
386
+ Synchronize JSDoc `@version` tags across the project with `package.json`.
387
+ ```bash
388
+ npx kythia version:up
389
+ ```
390
+ Run this after bumping your package version to keep file headers in sync.
391
+
392
+ -----
393
+
308
394
  ## 🔗 Peer Dependencies
309
395
 
310
396
  * **`discord.js`:** This is a `peerDependency`. `kythia-core` requires `discord.js` to function, but it expects the *consuming application* (your main bot) to provide it by listing `discord.js` in its own `dependencies`. This prevents version conflicts and issues with `instanceof` checks, especially common when using `npm link` during development. Ensure your main bot project has `discord.js` installed.
@@ -313,4 +399,4 @@ The core abandons traditional `sync()` operations in favor of a robust, **Larave
313
399
 
314
400
  ## 📜 License
315
401
 
316
- This project is licensed under the CC BY NC 4.0 License - see the [LICENSE](https://www.google.com/search?q=./LICENSE) file for details.
402
+ This project is licensed under the CC BY NC 4.0 License - see the [LICENSE](./LICENSE) file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kythia-core",
3
- "version": "0.10.1-beta",
3
+ "version": "0.11.1-beta",
4
4
  "description": "Core library for the Kythia main Discord bot: extensible, modular, and scalable foundation for commands, components, and event management.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -13,6 +13,9 @@
13
13
  "biome check --write --no-errors-on-unmatched"
14
14
  ]
15
15
  },
16
+ "bin": {
17
+ "kythia": "./src/cli/index.js"
18
+ },
16
19
  "keywords": [
17
20
  "kythia",
18
21
  "discord",
@@ -32,15 +35,22 @@
32
35
  "license": "CC BY NC 4.0",
33
36
  "type": "commonjs",
34
37
  "dependencies": {
38
+ "@babel/parser": "^7.28.5",
39
+ "@babel/traverse": "^7.28.5",
40
+ "@dotenvx/dotenvx": "^1.51.1",
41
+ "@google/genai": "^1.30.0",
35
42
  "@sentry/node": "^10.10.0",
36
43
  "async-exit-hook": "^2.0.1",
37
44
  "async-mutex": "^0.5.0",
38
45
  "cli-color": "^2.0.4",
46
+ "commander": "^14.0.2",
39
47
  "dotenv": "^16.6.1",
40
48
  "figlet": "^1.9.3",
49
+ "glob": "^13.0.0",
41
50
  "ioredis": "^5.7.0",
42
51
  "json-stable-stringify": "^1.3.0",
43
52
  "lru-cache": "^11.2.2",
53
+ "picocolors": "^1.1.1",
44
54
  "sequelize": "^6.37.7",
45
55
  "umzug": "^3.8.2"
46
56
  },
package/src/Kythia.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * @file src/Kythia.js
5
5
  * @copyright © 2025 kenndeclouv
6
6
  * @assistant chaa & graa
7
- * @version 0.10.0-beta
7
+ * @version 0.11.1-beta
8
8
  *
9
9
  * @description
10
10
  * The heart of the application lifecycle. This class acts as the central
@@ -29,7 +29,9 @@ const AddonManager = require('./managers/AddonManager');
29
29
  const EventManager = require('./managers/EventManager');
30
30
 
31
31
  const KythiaMigrator = require('./database/KythiaMigrator');
32
- const ModelLoader = require('./database/ModelLoader');
32
+ // const ModelLoader = require('./database/ModelLoader');
33
+ const bootModels = require('./database/ModelLoader');
34
+ const KythiaModel = require('./database/KythiaModel');
33
35
 
34
36
  class Kythia {
35
37
  /**
@@ -380,7 +382,7 @@ class Kythia {
380
382
  clc.cyan('Created by kenndeclouv'),
381
383
  clc.cyan('Discord Support: ') + clc.underline('https://dsc.gg/kythia'),
382
384
  clc.cyan('Official Documentation: ') +
383
- clc.underline('https://kythia.my.id/commands'),
385
+ clc.underline('https://docs.kythia.me'),
384
386
  '',
385
387
  clc.cyanBright(`Kythia version: ${version}`),
386
388
  '',
@@ -482,17 +484,17 @@ class Kythia {
482
484
  this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Connect Database ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
483
485
  await this.sequelize.authenticate();
484
486
 
485
- this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Run Migrations ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
486
487
  await KythiaMigrator({
487
488
  sequelize: this.sequelize,
488
489
  container: this.container,
489
490
  logger: this.logger,
490
491
  });
491
492
 
492
- this.logger.info('▬▬▬▬▬▬▬▬▬▬▬▬▬▬[ Boot Models ]▬▬▬▬▬▬▬▬▬▬▬▬▬▬');
493
- await ModelLoader(this, this.sequelize);
493
+ await bootModels(this, this.sequelize);
494
494
 
495
- this.logger.info('🔄 Hydrating container with initialized models...');
495
+ this.logger.info('🪝 Attaching Cache Hooks...');
496
+
497
+ KythiaModel.attachHooksToAllModels(this.sequelize, this.client);
496
498
 
497
499
  const handlers = this.addonManager.getHandlers();
498
500
  this.eventManager = new EventManager({
@@ -4,7 +4,7 @@
4
4
  * @file src/client/kythiaClient.js
5
5
  * @copyright © 2025 kenndeclouv
6
6
  * @assistant chaa & graa
7
- * @version 0.10.0-beta
7
+ * @version 0.11.1-beta
8
8
  *
9
9
  * @description
10
10
  * Factory function that initializes the Discord.js Client with high-performance
@@ -49,7 +49,6 @@ module.exports = function kythiaClient() {
49
49
 
50
50
  ThreadManager: {
51
51
  maxSize: 25,
52
- keepOverLimit: (thread) => thread.isActive(),
53
52
  },
54
53
 
55
54
  GuildMemberManager: {
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 🏗️ Abstract Base Command
3
+ *
4
+ * @file src/cli/Command.js
5
+ * @copyright © 2025 kenndeclouv
6
+ * @assistant chaa & graa
7
+ * @version 0.11.1-beta
8
+ *
9
+ * @description
10
+ * The base class for all Kythia CLI commands. It enforces a standard structure
11
+ * (signature, description, handle) and manages the integration with Commander.js.
12
+ *
13
+ * ✨ Core Features:
14
+ * - Standardized Interface: Enforces `handle()` implementation.
15
+ * - Flexible Parsing: Handles both Options and Arguments intelligently.
16
+ * - Auto Registration: Simplifies wiring commands to the main program.
17
+ */
18
+
19
+ class Command {
20
+ /**
21
+ * The name and signature of the console command.
22
+ * @type {string}
23
+ */
24
+ signature = '';
25
+
26
+ /**
27
+ * The console command description.
28
+ * @type {string}
29
+ */
30
+ description = '';
31
+
32
+ /**
33
+ * Execute the console command.
34
+ * @param {Object} options - The options object from Commander
35
+ * @param {...any} args - Positional arguments (if any)
36
+ */
37
+ async handle(_options, ..._args) {
38
+ throw new Error('Command must implement handle method');
39
+ }
40
+
41
+ /**
42
+ * Register the command with the program.
43
+ * @param {import('commander').Command} program
44
+ */
45
+ register(program) {
46
+ const cmd = program
47
+ .command(this.signature)
48
+ .description(this.description)
49
+ .action((...args) => {
50
+ const _commandObj = args.pop();
51
+ const opts = args.pop() || {};
52
+
53
+ this.handle(opts, ...args);
54
+ });
55
+
56
+ this.configure(cmd);
57
+ }
58
+
59
+ /**
60
+ * Configure additional options or arguments.
61
+ * @param {import('commander').Command} cmd
62
+ */
63
+ configure(_cmd) {
64
+ // Override to add options
65
+ }
66
+ }
67
+
68
+ module.exports = Command;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * 🧹 Redis Cache Flusher
3
+ *
4
+ * @file src/cli/commands/CacheClearCommand.js
5
+ * @copyright © 2025 kenndeclouv
6
+ * @assistant chaa & graa
7
+ * @version 0.11.1-beta
8
+ *
9
+ * @description
10
+ * Interactive utility to flush Redis cache. Supports intelligent handling of
11
+ * multiple Redis instances defined in environment variables.
12
+ *
13
+ * ✨ Core Features:
14
+ * - Multi-Target: Detects and lists all Redis URLs from config.
15
+ * - Safety First: Requires explicit confirmation before flushing.
16
+ * - Target Selection: Allows flushing specific instances or all at once.
17
+ */
18
+
19
+ const Command = require('../Command');
20
+ const pc = require('picocolors');
21
+ const readline = require('node:readline');
22
+ const Redis = require('ioredis');
23
+ require('@dotenvx/dotenvx/config');
24
+
25
+ function askQuestion(query) {
26
+ const rl = readline.createInterface({
27
+ input: process.stdin,
28
+ output: process.stdout,
29
+ });
30
+ return new Promise((resolve) =>
31
+ rl.question(query, (ans) => {
32
+ rl.close();
33
+ resolve(ans);
34
+ }),
35
+ );
36
+ }
37
+
38
+ class CacheClearCommand extends Command {
39
+ signature = 'cache:clear';
40
+ description = 'Flush Redis cache (supports multi-instance selection)';
41
+
42
+ async handle() {
43
+ console.log(pc.dim('🔍 Detecting Redis configuration...'));
44
+
45
+ const redisUrlsRaw = process.env.REDIS_URLS;
46
+
47
+ if (!redisUrlsRaw) {
48
+ console.error(pc.red('❌ REDIS_URLS not found in .env'));
49
+ process.exit(1);
50
+ }
51
+
52
+ const urls = redisUrlsRaw
53
+ .split(',')
54
+ .map((u) => u.trim())
55
+ .filter((u) => u.length > 0);
56
+
57
+ if (urls.length === 0) {
58
+ console.error(pc.red('❌ No valid Redis URLs found.'));
59
+ process.exit(1);
60
+ }
61
+
62
+ let targets = [];
63
+
64
+ if (urls.length === 1) {
65
+ targets = [urls[0]];
66
+ console.log(pc.cyan(`🎯 Target: ${targets[0]}`));
67
+ } else {
68
+ console.log(
69
+ pc.yellow(`⚠️ Multiple Redis instances detected (${urls.length}):`),
70
+ );
71
+ console.log(
72
+ pc.dim('---------------------------------------------------'),
73
+ );
74
+ console.log(`0. ${pc.bgRed(pc.white(' FLUSH ALL INSTANCES '))}`);
75
+ urls.forEach((url, index) => {
76
+ const maskedUrl = url.replace(/:([^@]+)@/, ':****@');
77
+ console.log(`${index + 1}. ${maskedUrl}`);
78
+ });
79
+ console.log(
80
+ pc.dim('---------------------------------------------------'),
81
+ );
82
+
83
+ const answer = await askQuestion(
84
+ pc.cyan('Select target to flush (number): '),
85
+ );
86
+ const choice = parseInt(answer, 10);
87
+
88
+ if (Number.isNaN(choice) || choice < 0 || choice > urls.length) {
89
+ console.error(pc.red('❌ Invalid selection.'));
90
+ process.exit(1);
91
+ }
92
+
93
+ if (choice === 0) {
94
+ targets = urls;
95
+ } else {
96
+ targets = [urls[choice - 1]];
97
+ }
98
+ }
99
+
100
+ const confirm = await askQuestion(
101
+ pc.bgRed(pc.white(' DANGER ')) +
102
+ pc.yellow(
103
+ ` Are you sure you want to FLUSHALL ${targets.length} instance(s)? (y/n): `,
104
+ ),
105
+ );
106
+
107
+ if (confirm.toLowerCase() !== 'y' && confirm.toLowerCase() !== 'yes') {
108
+ console.log(pc.cyan('🛡️ Operation cancelled.'));
109
+ process.exit(0);
110
+ }
111
+
112
+ console.log('');
113
+ for (const url of targets) {
114
+ const masked = url.replace(/:([^@]+)@/, ':****@');
115
+ try {
116
+ console.log(pc.dim(`🔌 Connecting to ${masked}...`));
117
+ const redis = new Redis(url, {
118
+ maxRetriesPerRequest: 1,
119
+ retryStrategy: null,
120
+ });
121
+
122
+ await redis.flushall();
123
+ console.log(pc.green(`✅ FLUSHALL Success: ${masked}`));
124
+
125
+ redis.quit();
126
+ } catch (err) {
127
+ console.error(pc.red(`❌ Failed to flush ${masked}: ${err.message}`));
128
+ }
129
+ }
130
+
131
+ console.log(pc.green('\n✨ Cache clearing process completed.'));
132
+ process.exit(0);
133
+ }
134
+ }
135
+
136
+ module.exports = CacheClearCommand;