@joktec/skills 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.
Files changed (129) hide show
  1. package/AGENTS.md +89 -0
  2. package/LICENSE +21 -0
  3. package/README.md +502 -0
  4. package/adapters/README.md +12 -0
  5. package/adapters/copilot/README.md +5 -0
  6. package/adapters/cursor/README.md +5 -0
  7. package/adapters/gemini/README.md +5 -0
  8. package/adapters/windsurf/README.md +5 -0
  9. package/bin/joktec-skills.mjs +553 -0
  10. package/dist/claude/skills/joktec-adapter-skill/SKILL.md +26 -0
  11. package/dist/claude/skills/joktec-adapter-skill/agents/openai.yaml +4 -0
  12. package/dist/claude/skills/joktec-adapter-skill/references/adapters.md +12 -0
  13. package/dist/claude/skills/joktec-broker-skill/SKILL.md +26 -0
  14. package/dist/claude/skills/joktec-broker-skill/agents/openai.yaml +4 -0
  15. package/dist/claude/skills/joktec-broker-skill/references/brokers.md +12 -0
  16. package/dist/claude/skills/joktec-common-skill/SKILL.md +28 -0
  17. package/dist/claude/skills/joktec-common-skill/agents/openai.yaml +4 -0
  18. package/dist/claude/skills/joktec-common-skill/references/core.md +19 -0
  19. package/dist/claude/skills/joktec-common-skill/references/utils-cron.md +17 -0
  20. package/dist/claude/skills/joktec-database-extended-skill/SKILL.md +25 -0
  21. package/dist/claude/skills/joktec-database-extended-skill/agents/openai.yaml +4 -0
  22. package/dist/claude/skills/joktec-database-extended-skill/references/extended-databases.md +11 -0
  23. package/dist/claude/skills/joktec-framework-skill/SKILL.md +31 -0
  24. package/dist/claude/skills/joktec-framework-skill/agents/openai.yaml +4 -0
  25. package/dist/claude/skills/joktec-framework-skill/references/framework-map.md +25 -0
  26. package/dist/claude/skills/joktec-integration-skill/SKILL.md +24 -0
  27. package/dist/claude/skills/joktec-integration-skill/agents/openai.yaml +4 -0
  28. package/dist/claude/skills/joktec-integration-skill/references/integrations.md +9 -0
  29. package/dist/claude/skills/joktec-mongo-skill/SKILL.md +22 -0
  30. package/dist/claude/skills/joktec-mongo-skill/agents/openai.yaml +4 -0
  31. package/dist/claude/skills/joktec-mongo-skill/references/repository.md +20 -0
  32. package/dist/claude/skills/joktec-mongo-skill/references/schema-and-plugins.md +17 -0
  33. package/dist/claude/skills/joktec-mysql-skill/SKILL.md +22 -0
  34. package/dist/claude/skills/joktec-mysql-skill/agents/openai.yaml +4 -0
  35. package/dist/claude/skills/joktec-mysql-skill/references/entities.md +15 -0
  36. package/dist/claude/skills/joktec-mysql-skill/references/repository.md +23 -0
  37. package/dist/claude/skills/joktec-tool-skill/SKILL.md +25 -0
  38. package/dist/claude/skills/joktec-tool-skill/agents/openai.yaml +4 -0
  39. package/dist/claude/skills/joktec-tool-skill/references/tools.md +13 -0
  40. package/dist/codex/skills/joktec-adapter-skill/SKILL.md +26 -0
  41. package/dist/codex/skills/joktec-adapter-skill/agents/openai.yaml +4 -0
  42. package/dist/codex/skills/joktec-adapter-skill/references/adapters.md +12 -0
  43. package/dist/codex/skills/joktec-broker-skill/SKILL.md +26 -0
  44. package/dist/codex/skills/joktec-broker-skill/agents/openai.yaml +4 -0
  45. package/dist/codex/skills/joktec-broker-skill/references/brokers.md +12 -0
  46. package/dist/codex/skills/joktec-common-skill/SKILL.md +28 -0
  47. package/dist/codex/skills/joktec-common-skill/agents/openai.yaml +4 -0
  48. package/dist/codex/skills/joktec-common-skill/references/core.md +19 -0
  49. package/dist/codex/skills/joktec-common-skill/references/utils-cron.md +17 -0
  50. package/dist/codex/skills/joktec-database-extended-skill/SKILL.md +25 -0
  51. package/dist/codex/skills/joktec-database-extended-skill/agents/openai.yaml +4 -0
  52. package/dist/codex/skills/joktec-database-extended-skill/references/extended-databases.md +11 -0
  53. package/dist/codex/skills/joktec-framework-skill/SKILL.md +31 -0
  54. package/dist/codex/skills/joktec-framework-skill/agents/openai.yaml +4 -0
  55. package/dist/codex/skills/joktec-framework-skill/references/framework-map.md +25 -0
  56. package/dist/codex/skills/joktec-integration-skill/SKILL.md +24 -0
  57. package/dist/codex/skills/joktec-integration-skill/agents/openai.yaml +4 -0
  58. package/dist/codex/skills/joktec-integration-skill/references/integrations.md +9 -0
  59. package/dist/codex/skills/joktec-mongo-skill/SKILL.md +22 -0
  60. package/dist/codex/skills/joktec-mongo-skill/agents/openai.yaml +4 -0
  61. package/dist/codex/skills/joktec-mongo-skill/references/repository.md +20 -0
  62. package/dist/codex/skills/joktec-mongo-skill/references/schema-and-plugins.md +17 -0
  63. package/dist/codex/skills/joktec-mysql-skill/SKILL.md +22 -0
  64. package/dist/codex/skills/joktec-mysql-skill/agents/openai.yaml +4 -0
  65. package/dist/codex/skills/joktec-mysql-skill/references/entities.md +15 -0
  66. package/dist/codex/skills/joktec-mysql-skill/references/repository.md +23 -0
  67. package/dist/codex/skills/joktec-tool-skill/SKILL.md +25 -0
  68. package/dist/codex/skills/joktec-tool-skill/agents/openai.yaml +4 -0
  69. package/dist/codex/skills/joktec-tool-skill/references/tools.md +13 -0
  70. package/dist/copilot/.github/copilot-instructions.md +459 -0
  71. package/dist/cursor/.cursor/rules/joktec-adapter-skill.mdc +44 -0
  72. package/dist/cursor/.cursor/rules/joktec-broker-skill.mdc +44 -0
  73. package/dist/cursor/.cursor/rules/joktec-common-skill.mdc +73 -0
  74. package/dist/cursor/.cursor/rules/joktec-database-extended-skill.mdc +42 -0
  75. package/dist/cursor/.cursor/rules/joktec-framework-skill.mdc +62 -0
  76. package/dist/cursor/.cursor/rules/joktec-integration-skill.mdc +39 -0
  77. package/dist/cursor/.cursor/rules/joktec-mongo-skill.mdc +68 -0
  78. package/dist/cursor/.cursor/rules/joktec-mysql-skill.mdc +69 -0
  79. package/dist/cursor/.cursor/rules/joktec-tool-skill.mdc +44 -0
  80. package/dist/gemini/GEMINI.md +477 -0
  81. package/dist/windsurf/.windsurf/rules/joktec-adapter-skill.md +40 -0
  82. package/dist/windsurf/.windsurf/rules/joktec-broker-skill.md +40 -0
  83. package/dist/windsurf/.windsurf/rules/joktec-common-skill.md +69 -0
  84. package/dist/windsurf/.windsurf/rules/joktec-database-extended-skill.md +38 -0
  85. package/dist/windsurf/.windsurf/rules/joktec-framework-skill.md +58 -0
  86. package/dist/windsurf/.windsurf/rules/joktec-integration-skill.md +35 -0
  87. package/dist/windsurf/.windsurf/rules/joktec-mongo-skill.md +64 -0
  88. package/dist/windsurf/.windsurf/rules/joktec-mysql-skill.md +65 -0
  89. package/dist/windsurf/.windsurf/rules/joktec-tool-skill.md +40 -0
  90. package/package.json +69 -0
  91. package/scripts/build-copilot-instructions.mjs +22 -0
  92. package/scripts/build-cursor-rules.mjs +23 -0
  93. package/scripts/build-gemini-md.mjs +22 -0
  94. package/scripts/build-skill-folders.mjs +16 -0
  95. package/scripts/build-windsurf-rules.mjs +18 -0
  96. package/scripts/lib.mjs +110 -0
  97. package/scripts/sync-from-joktec-framework.mjs +53 -0
  98. package/scripts/validate-skills.mjs +78 -0
  99. package/skill-pack.json +146 -0
  100. package/skills/joktec-adapter-skill/SKILL.md +26 -0
  101. package/skills/joktec-adapter-skill/agents/openai.yaml +4 -0
  102. package/skills/joktec-adapter-skill/references/adapters.md +12 -0
  103. package/skills/joktec-broker-skill/SKILL.md +26 -0
  104. package/skills/joktec-broker-skill/agents/openai.yaml +4 -0
  105. package/skills/joktec-broker-skill/references/brokers.md +12 -0
  106. package/skills/joktec-common-skill/SKILL.md +28 -0
  107. package/skills/joktec-common-skill/agents/openai.yaml +4 -0
  108. package/skills/joktec-common-skill/references/core.md +19 -0
  109. package/skills/joktec-common-skill/references/utils-cron.md +17 -0
  110. package/skills/joktec-database-extended-skill/SKILL.md +25 -0
  111. package/skills/joktec-database-extended-skill/agents/openai.yaml +4 -0
  112. package/skills/joktec-database-extended-skill/references/extended-databases.md +11 -0
  113. package/skills/joktec-framework-skill/SKILL.md +31 -0
  114. package/skills/joktec-framework-skill/agents/openai.yaml +4 -0
  115. package/skills/joktec-framework-skill/references/framework-map.md +25 -0
  116. package/skills/joktec-integration-skill/SKILL.md +24 -0
  117. package/skills/joktec-integration-skill/agents/openai.yaml +4 -0
  118. package/skills/joktec-integration-skill/references/integrations.md +9 -0
  119. package/skills/joktec-mongo-skill/SKILL.md +22 -0
  120. package/skills/joktec-mongo-skill/agents/openai.yaml +4 -0
  121. package/skills/joktec-mongo-skill/references/repository.md +20 -0
  122. package/skills/joktec-mongo-skill/references/schema-and-plugins.md +17 -0
  123. package/skills/joktec-mysql-skill/SKILL.md +22 -0
  124. package/skills/joktec-mysql-skill/agents/openai.yaml +4 -0
  125. package/skills/joktec-mysql-skill/references/entities.md +15 -0
  126. package/skills/joktec-mysql-skill/references/repository.md +23 -0
  127. package/skills/joktec-tool-skill/SKILL.md +25 -0
  128. package/skills/joktec-tool-skill/agents/openai.yaml +4 -0
  129. package/skills/joktec-tool-skill/references/tools.md +13 -0
@@ -0,0 +1,553 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import readline from 'node:readline';
6
+ import readlinePromises from 'node:readline/promises';
7
+ import { stdin as input, stdout as output } from 'node:process';
8
+ import { DIST_DIR, ROOT, ensureDir, loadSkills, readJson, writeFile } from '../scripts/lib.mjs';
9
+
10
+ const AGENTS = ['codex', 'claude', 'cursor', 'gemini', 'copilot', 'windsurf'];
11
+ const DEFAULT_SOURCE = 'https://github.com/joktec/joktec-skills.git';
12
+ const SKILL_ALIASES = {
13
+ framework: 'joktec-framework-skill',
14
+ common: 'joktec-common-skill',
15
+ core: 'joktec-common-skill',
16
+ utils: 'joktec-common-skill',
17
+ cron: 'joktec-common-skill',
18
+ types: 'joktec-common-skill',
19
+ mongo: 'joktec-mongo-skill',
20
+ mysql: 'joktec-mysql-skill',
21
+ sql: 'joktec-mysql-skill',
22
+ broker: 'joktec-broker-skill',
23
+ brokers: 'joktec-broker-skill',
24
+ kafka: 'joktec-broker-skill',
25
+ rabbit: 'joktec-broker-skill',
26
+ sqs: 'joktec-broker-skill',
27
+ redcast: 'joktec-broker-skill',
28
+ adapter: 'joktec-adapter-skill',
29
+ adapters: 'joktec-adapter-skill',
30
+ cacher: 'joktec-adapter-skill',
31
+ mailer: 'joktec-adapter-skill',
32
+ notifier: 'joktec-adapter-skill',
33
+ storage: 'joktec-adapter-skill',
34
+ database: 'joktec-database-extended-skill',
35
+ databases: 'joktec-database-extended-skill',
36
+ elastic: 'joktec-database-extended-skill',
37
+ arango: 'joktec-database-extended-skill',
38
+ bigquery: 'joktec-database-extended-skill',
39
+ integration: 'joktec-integration-skill',
40
+ integrations: 'joktec-integration-skill',
41
+ firebase: 'joktec-integration-skill',
42
+ gpt: 'joktec-integration-skill',
43
+ tool: 'joktec-tool-skill',
44
+ tools: 'joktec-tool-skill',
45
+ http: 'joktec-tool-skill',
46
+ file: 'joktec-tool-skill',
47
+ alert: 'joktec-tool-skill',
48
+ };
49
+
50
+ const color = {
51
+ dim: text => (output.isTTY ? `\x1b[2m${text}\x1b[0m` : text),
52
+ cyan: text => (output.isTTY ? `\x1b[36m${text}\x1b[0m` : text),
53
+ green: text => (output.isTTY ? `\x1b[32m${text}\x1b[0m` : text),
54
+ yellow: text => (output.isTTY ? `\x1b[33m${text}\x1b[0m` : text),
55
+ bold: text => (output.isTTY ? `\x1b[1m${text}\x1b[0m` : text),
56
+ };
57
+
58
+ function parseArgs(argv) {
59
+ const args = { _: [] };
60
+ for (let i = 0; i < argv.length; i += 1) {
61
+ const item = argv[i];
62
+ if (!item.startsWith('-')) {
63
+ args._.push(item);
64
+ continue;
65
+ }
66
+
67
+ const [rawKey, inlineValue] = item.replace(/^--?/, '').split('=');
68
+ const key = rawKey === 'a' ? 'agent' : rawKey === 'y' ? 'yes' : rawKey;
69
+ if (['yes', 'all', 'dry-run', 'force', 'help', 'version', 'recommended'].includes(key)) {
70
+ args[key] = true;
71
+ continue;
72
+ }
73
+ args[key] = inlineValue ?? argv[++i];
74
+ }
75
+ return args;
76
+ }
77
+
78
+ function printHelp() {
79
+ console.log(`JokTec Skills CLI
80
+
81
+ Usage:
82
+ npx @joktec/skills add [skills] --agent <agent> --project <path>
83
+ npx @joktec/skills install [skills] --agent <agent> --project <path>
84
+ npx @joktec/skills doctor --project <path>
85
+ npx @joktec/skills list
86
+
87
+ Agents:
88
+ codex, claude, cursor, gemini, copilot, windsurf, all
89
+
90
+ Examples:
91
+ npx @joktec/skills add mongo --agent codex --project .
92
+ npx @joktec/skills add mongo,mysql --agent cursor --project .
93
+ npx @joktec/skills install --all --agent all --project . --yes
94
+ npx @joktec/skills doctor --project .
95
+ `);
96
+ }
97
+
98
+ function splitList(value) {
99
+ if (!value) return [];
100
+ return String(value)
101
+ .split(',')
102
+ .map(item => item.trim())
103
+ .filter(Boolean);
104
+ }
105
+
106
+ function isSourceToken(value) {
107
+ if (!value) return false;
108
+ return /^https?:\/\//.test(value) || /^git@/.test(value) || value === 'joktec/joktec-skills';
109
+ }
110
+
111
+ function sourceToUrl(value) {
112
+ if (!value || value === 'joktec/joktec-skills') return DEFAULT_SOURCE;
113
+ return value;
114
+ }
115
+
116
+ function consumeSourceToken(args) {
117
+ if (!isSourceToken(args._[0])) return DEFAULT_SOURCE;
118
+ return sourceToUrl(args._.shift());
119
+ }
120
+
121
+ function resolveSkillIds(pack, requested, all = false) {
122
+ const allIds = pack.skills.map(skill => skill.id);
123
+ if (all) return allIds;
124
+
125
+ const initial = requested.length
126
+ ? requested
127
+ : pack.skills.filter(skill => skill.requiredByDefault).map(skill => skill.id);
128
+
129
+ return Array.from(
130
+ new Set(
131
+ initial.map(item => {
132
+ const normalized = item.replace(/^@joktec\//, '').replace(/-skill$/, '');
133
+ const id = SKILL_ALIASES[normalized] || SKILL_ALIASES[item] || item;
134
+ if (!allIds.includes(id)) throw new Error(`Unknown skill: ${item}`);
135
+ return id;
136
+ }),
137
+ ),
138
+ );
139
+ }
140
+
141
+ function resolveDependencies(pack, ids, includeRecommended) {
142
+ const byId = new Map(pack.skills.map(skill => [skill.id, skill]));
143
+ const selected = new Set(ids);
144
+ const addedDependencies = new Set();
145
+ const addedRecommended = new Set();
146
+
147
+ let changed = true;
148
+ while (changed) {
149
+ changed = false;
150
+ for (const id of Array.from(selected)) {
151
+ const skill = byId.get(id);
152
+ for (const dep of skill.dependencies || []) {
153
+ if (!selected.has(dep)) {
154
+ selected.add(dep);
155
+ addedDependencies.add(dep);
156
+ changed = true;
157
+ }
158
+ }
159
+ if (includeRecommended) {
160
+ for (const rec of skill.recommended || []) {
161
+ if (!selected.has(rec)) {
162
+ selected.add(rec);
163
+ addedRecommended.add(rec);
164
+ changed = true;
165
+ }
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ const ordered = pack.skills.map(skill => skill.id).filter(id => selected.has(id));
172
+ return {
173
+ selected: ordered,
174
+ addedDependencies: Array.from(addedDependencies),
175
+ addedRecommended: Array.from(addedRecommended),
176
+ };
177
+ }
178
+
179
+ function parseAgents(value) {
180
+ const requested = splitList(value || 'codex');
181
+ const result = requested.includes('all') ? AGENTS : requested;
182
+ for (const agent of result) {
183
+ if (!AGENTS.includes(agent)) throw new Error(`Unknown agent: ${agent}`);
184
+ }
185
+ return Array.from(new Set(result));
186
+ }
187
+
188
+ function copyRecursive(src, dest, force) {
189
+ if (fs.existsSync(dest)) {
190
+ if (!force) throw new Error(`Target exists: ${dest}. Use --force to overwrite.`);
191
+ fs.rmSync(dest, { recursive: true, force: true });
192
+ }
193
+ ensureDir(path.dirname(dest));
194
+ fs.cpSync(src, dest, { recursive: true });
195
+ }
196
+
197
+ function copyFile(src, dest, force) {
198
+ if (fs.existsSync(dest) && !force) throw new Error(`Target exists: ${dest}. Use --force to overwrite.`);
199
+ ensureDir(path.dirname(dest));
200
+ fs.copyFileSync(src, dest);
201
+ }
202
+
203
+ function addCopy(operations, src, dest, type = 'file', agent) {
204
+ operations.push({ src, dest, type, agent });
205
+ }
206
+
207
+ function planAgentInstall(agent, project, skillIds) {
208
+ const operations = [];
209
+
210
+ if (agent === 'codex') {
211
+ const srcBase = path.join(DIST_DIR, 'codex', 'skills');
212
+ const destBase = path.join(project, '.agents', 'skills');
213
+ for (const id of skillIds) addCopy(operations, path.join(srcBase, id), path.join(destBase, id), 'dir', agent);
214
+ }
215
+
216
+ if (agent === 'claude') {
217
+ const srcBase = path.join(DIST_DIR, 'claude', 'skills');
218
+ const destBase = path.join(project, '.claude', 'skills');
219
+ for (const id of skillIds) addCopy(operations, path.join(srcBase, id), path.join(destBase, id), 'dir', agent);
220
+ }
221
+
222
+ if (agent === 'cursor') {
223
+ for (const id of skillIds) {
224
+ addCopy(
225
+ operations,
226
+ path.join(DIST_DIR, 'cursor', '.cursor', 'rules', `${id}.mdc`),
227
+ path.join(project, '.cursor', 'rules', `${id}.mdc`),
228
+ 'file',
229
+ agent,
230
+ );
231
+ }
232
+ }
233
+
234
+ if (agent === 'windsurf') {
235
+ for (const id of skillIds) {
236
+ addCopy(
237
+ operations,
238
+ path.join(DIST_DIR, 'windsurf', '.windsurf', 'rules', `${id}.md`),
239
+ path.join(project, '.windsurf', 'rules', `${id}.md`),
240
+ 'file',
241
+ agent,
242
+ );
243
+ }
244
+ }
245
+
246
+ if (agent === 'gemini') {
247
+ addCopy(operations, path.join(DIST_DIR, 'gemini', 'GEMINI.md'), path.join(project, 'GEMINI.md'), 'file', agent);
248
+ }
249
+
250
+ if (agent === 'copilot') {
251
+ addCopy(
252
+ operations,
253
+ path.join(DIST_DIR, 'copilot', '.github', 'copilot-instructions.md'),
254
+ path.join(project, '.github', 'copilot-instructions.md'),
255
+ 'file',
256
+ agent,
257
+ );
258
+ }
259
+
260
+ return operations;
261
+ }
262
+
263
+ function executeOperations(operations, force, dryRun) {
264
+ if (dryRun) return;
265
+ for (const operation of operations) {
266
+ if (operation.type === 'dir') copyRecursive(operation.src, operation.dest, force);
267
+ else copyFile(operation.src, operation.dest, force);
268
+ }
269
+ }
270
+
271
+ function writeManifest(project, pack, agents, skillIds, dryRun) {
272
+ const file = path.join(project, '.joktec-skills.json');
273
+ const existing = fs.existsSync(file) ? readJson(file) : {};
274
+ const manifest = {
275
+ package: '@joktec/skills',
276
+ version: pack.version,
277
+ installedAt: new Date().toISOString(),
278
+ agents: Array.from(new Set([...(existing.agents || []), ...agents])).sort(),
279
+ skills: Array.from(new Set([...(existing.skills || []), ...skillIds])).sort(),
280
+ frameworkBaseline: pack.frameworkBaseline,
281
+ packageBaseline: pack.packageBaseline,
282
+ };
283
+ if (!dryRun) writeFile(file, JSON.stringify(manifest, null, 2));
284
+ return manifest;
285
+ }
286
+
287
+ function logo() {
288
+ console.log(color.dim(` ██╗ ██████╗ ██╗ ██╗████████╗███████╗ ██████╗
289
+ ██║██╔═══██╗██║ ██╔╝╚══██╔══╝██╔════╝██╔════╝
290
+ ██║██║ ██║█████╔╝ ██║ █████╗ ██║
291
+ ██ ██║██║ ██║██╔═██╗ ██║ ██╔══╝ ██║
292
+ ╚█████╔╝╚██████╔╝██║ ██╗ ██║ ███████╗╚██████╗
293
+ ╚════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝`));
294
+ console.log('');
295
+ }
296
+
297
+ function step(label, value) {
298
+ console.log(`${color.green('◇')} ${label}${value ? `\n│ ${value}` : ''}`);
299
+ console.log('│');
300
+ }
301
+
302
+ function label() {
303
+ console.log(`┌ ${color.cyan(' skills ')}`);
304
+ console.log('│');
305
+ }
306
+
307
+ function done(message) {
308
+ console.log(`└ ${color.green('Done!')} ${color.dim(message)}`);
309
+ }
310
+
311
+ function displayPath(file) {
312
+ const home = process.env.HOME;
313
+ return home && file.startsWith(home) ? `~${file.slice(home.length)}` : file;
314
+ }
315
+
316
+ function box(title, lines) {
317
+ const contentWidth = Math.max(title.length + 6, ...lines.map(line => line.length + 4), 70);
318
+ const width = output.isTTY ? Math.min(contentWidth, Math.max(70, (output.columns || 100) - 4)) : contentWidth;
319
+ const head = `${color.green('◇')} ${title} ${'─'.repeat(Math.max(1, width - title.length - 5))}╮`;
320
+ console.log(head);
321
+ console.log(`│${' '.repeat(width)}│`);
322
+ for (const line of lines) {
323
+ console.log(`│ ${line}${' '.repeat(Math.max(0, width - line.length - 2))}│`);
324
+ }
325
+ console.log(`├${'─'.repeat(width)}╯`);
326
+ console.log('│');
327
+ }
328
+
329
+ function buildSummaryLines(project, operations) {
330
+ return operations.flatMap(operation => [
331
+ color.cyan(displayPath(operation.dest)),
332
+ color.dim(` copy -> ${operation.agent[0].toUpperCase()}${operation.agent.slice(1)}`),
333
+ '',
334
+ ]);
335
+ }
336
+
337
+ function buildInstalledLines(project, operations, skillIds, dryRun) {
338
+ const bySkill = new Map();
339
+ for (const operation of operations) {
340
+ const id = skillIds.find(skillId => operation.dest.includes(skillId));
341
+ if (id && !bySkill.has(id)) bySkill.set(id, operation.dest);
342
+ }
343
+ return Array.from(bySkill.entries()).flatMap(([id, dest]) => [
344
+ `${color.green('✓')} ${id} ${color.dim(dryRun ? '(planned)' : '(copied)')}`,
345
+ color.dim(` -> ${displayPath(dest)}`),
346
+ ]);
347
+ }
348
+
349
+ function renderMultiSelect(items, cursor, selected, message) {
350
+ readline.cursorTo(output, 0);
351
+ readline.clearScreenDown(output);
352
+ console.log(`${color.green('◇')} ${message} ${color.dim('(space to toggle, enter to confirm)')}`);
353
+ for (let i = 0; i < items.length; i += 1) {
354
+ const marker = selected.has(items[i].value) ? color.green('●') : '○';
355
+ const pointer = i === cursor ? color.cyan('›') : ' ';
356
+ console.log(`│ ${pointer} ${marker} ${items[i].label}`);
357
+ }
358
+ }
359
+
360
+ async function multiSelect(items, defaults, message) {
361
+ if (!input.isTTY || !output.isTTY) return defaults;
362
+
363
+ const selected = new Set(defaults);
364
+ let cursor = 0;
365
+ output.write('\n');
366
+ readline.emitKeypressEvents(input);
367
+ input.setRawMode(true);
368
+
369
+ try {
370
+ return await new Promise(resolve => {
371
+ const onKey = (str, key) => {
372
+ if (key.name === 'c' && key.ctrl) {
373
+ input.setRawMode(false);
374
+ process.exit(130);
375
+ }
376
+ if (key.name === 'up' || key.name === 'k') cursor = (cursor - 1 + items.length) % items.length;
377
+ if (key.name === 'down' || key.name === 'j') cursor = (cursor + 1) % items.length;
378
+ if (key.name === 'space') {
379
+ const id = items[cursor].value;
380
+ if (selected.has(id)) selected.delete(id);
381
+ else selected.add(id);
382
+ }
383
+ if (key.name === 'return') {
384
+ input.off('keypress', onKey);
385
+ input.setRawMode(false);
386
+ readline.cursorTo(output, 0);
387
+ readline.clearScreenDown(output);
388
+ resolve(Array.from(selected));
389
+ return;
390
+ }
391
+ renderMultiSelect(items, cursor, selected, message);
392
+ };
393
+
394
+ renderMultiSelect(items, cursor, selected, message);
395
+ input.on('keypress', onKey);
396
+ });
397
+ } finally {
398
+ if (input.isRaw) input.setRawMode(false);
399
+ }
400
+ }
401
+
402
+ async function confirm(message, yes) {
403
+ if (yes || !input.isTTY) return true;
404
+ const rl = readlinePromises.createInterface({ input, output });
405
+ const answer = await rl.question(`${color.green('◇')} ${message}\n│ `);
406
+ rl.close();
407
+ console.log('│');
408
+ return !/^n(o)?$/i.test(answer.trim());
409
+ }
410
+
411
+ async function addCommand(args) {
412
+ const { pack } = loadSkills();
413
+ const project = path.resolve(args.project || '.');
414
+ const source = consumeSourceToken(args);
415
+ const agents = parseAgents(args.agent);
416
+ const requested = [...args._, ...splitList(args.skills)].flatMap(splitList);
417
+
418
+ logo();
419
+ label();
420
+ step('Source:', source);
421
+ step(source === DEFAULT_SOURCE ? 'Repository ready' : 'External source accepted');
422
+ step(`Found ${color.green(String(pack.skills.length))} skills`);
423
+
424
+ let selected;
425
+ const canPromptForSkills = !args.yes && !args.all && !requested.length && input.isTTY && output.isTTY;
426
+ if (canPromptForSkills) {
427
+ selected = await multiSelect(
428
+ pack.skills.map(skill => ({
429
+ value: skill.id,
430
+ label: `${skill.id} ${color.dim(`(${skill.packages.join(', ')})`)}`,
431
+ })),
432
+ pack.skills.filter(skill => skill.requiredByDefault).map(skill => skill.id),
433
+ 'Select skills to install',
434
+ );
435
+ } else {
436
+ selected = resolveSkillIds(pack, requested, Boolean(args.all));
437
+ step('Select skills to install', selected.join(', '));
438
+ }
439
+
440
+ const resolution = resolveDependencies(pack, selected, Boolean(args.yes || args.recommended || !requested.length));
441
+ step('Installation scope', 'Project');
442
+
443
+ if (resolution.addedDependencies.length) {
444
+ step('Required dependencies added', resolution.addedDependencies.join(', '));
445
+ }
446
+ if (resolution.addedRecommended.length) {
447
+ step('Recommended skills added', resolution.addedRecommended.join(', '));
448
+ }
449
+
450
+ const operations = agents.flatMap(agent => planAgentInstall(agent, project, resolution.selected));
451
+ box('Installation Summary', buildSummaryLines(project, operations));
452
+
453
+ const ok = await confirm('Proceed with installation?', Boolean(args.yes));
454
+ if (!ok) {
455
+ console.log(`${color.yellow('◇')} Installation cancelled`);
456
+ return;
457
+ }
458
+
459
+ executeOperations(operations, Boolean(args.force), Boolean(args['dry-run']));
460
+ const manifest = writeManifest(project, pack, agents, resolution.selected, Boolean(args['dry-run']));
461
+
462
+ step(args['dry-run'] ? 'Dry run complete' : 'Installation complete');
463
+ box(
464
+ `${args['dry-run'] ? 'Planned' : 'Installed'} ${resolution.selected.length} skills`,
465
+ buildInstalledLines(project, operations, resolution.selected, Boolean(args['dry-run'])),
466
+ );
467
+ console.log(`│ ${color.dim(`Manifest: ${displayPath(path.join(project, '.joktec-skills.json'))}`)}`);
468
+ console.log(`│ ${color.dim(`@joktec/skills ${manifest.version}`)}`);
469
+ console.log('│');
470
+ done('Review skills before use; they run with full agent permissions.');
471
+ }
472
+
473
+ function collectProjectJoktecPackages(project) {
474
+ const file = path.join(project, 'package.json');
475
+ if (!fs.existsSync(file)) return {};
476
+ const pkg = readJson(file);
477
+ return Object.assign(
478
+ {},
479
+ pkg.dependencies || {},
480
+ pkg.devDependencies || {},
481
+ pkg.peerDependencies || {},
482
+ pkg.optionalDependencies || {},
483
+ );
484
+ }
485
+
486
+ function normalizeVersion(version) {
487
+ const match = String(version || '').match(/\d+\.\d+\.\d+/);
488
+ return match?.[0] || null;
489
+ }
490
+
491
+ function compareVersions(a, b) {
492
+ const pa = normalizeVersion(a)?.split('.').map(Number);
493
+ const pb = normalizeVersion(b)?.split('.').map(Number);
494
+ if (!pa || !pb) return null;
495
+ for (let i = 0; i < 3; i += 1) {
496
+ if (pa[i] > pb[i]) return 1;
497
+ if (pa[i] < pb[i]) return -1;
498
+ }
499
+ return 0;
500
+ }
501
+
502
+ function doctorCommand(args) {
503
+ const { pack } = loadSkills();
504
+ const project = path.resolve(args.project || '.');
505
+ const deps = collectProjectJoktecPackages(project);
506
+ const joktecDeps = Object.entries(deps).filter(([name]) => name.startsWith('@joktec/'));
507
+
508
+ console.log(`JokTec Skills: ${pack.version}`);
509
+ console.log(`Framework baseline: ${pack.frameworkBaseline?.commit || 'unknown'}`);
510
+ console.log(`Project: ${project}`);
511
+
512
+ if (!joktecDeps.length) {
513
+ console.log('\nNo @joktec/* packages found in package.json.');
514
+ return;
515
+ }
516
+
517
+ console.log('\nDetected @joktec/* packages:');
518
+ for (const [name, version] of joktecDeps) {
519
+ const baseline = pack.packageBaseline?.[name];
520
+ const cmp = baseline ? compareVersions(version, baseline) : null;
521
+ let status = 'baseline unknown';
522
+ if (cmp === 0) status = 'matches skill baseline';
523
+ if (cmp === 1) status = 'newer than skill baseline; verify newly added APIs';
524
+ if (cmp === -1) status = 'older than skill baseline; some APIs may not exist';
525
+ console.log(`- ${name}: ${version}${baseline ? ` (baseline ${baseline}; ${status})` : ` (${status})`}`);
526
+ }
527
+ }
528
+
529
+ function listCommand() {
530
+ const { pack } = loadSkills();
531
+ for (const skill of pack.skills) {
532
+ const deps = skill.dependencies?.length ? ` deps: ${skill.dependencies.join(',')}` : '';
533
+ const rec = skill.recommended?.length ? ` recommended: ${skill.recommended.join(',')}` : '';
534
+ console.log(`${skill.id} - ${skill.packages.join(', ')}${deps}${rec}`);
535
+ }
536
+ }
537
+
538
+ async function main() {
539
+ const args = parseArgs(process.argv.slice(2));
540
+ const command = args._.shift() || 'help';
541
+ if (args.help || command === 'help') return printHelp();
542
+ if (args.version || command === 'version') return console.log(readJson(path.join(ROOT, 'package.json')).version);
543
+ if (command === 'list') return listCommand();
544
+ if (command === 'doctor' || command === 'check') return doctorCommand(args);
545
+ if (command === 'add' || command === 'install') return addCommand(args);
546
+
547
+ throw new Error(`Unknown command: ${command}`);
548
+ }
549
+
550
+ main().catch(error => {
551
+ console.error(`Error: ${error.message}`);
552
+ process.exit(1);
553
+ });
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: joktec-adapter-skill
3
+ description: Use when wiring or using JokTec adapter packages @joktec/cacher, @joktec/mailer, @joktec/notifier, or @joktec/storage for cache, mail delivery, push notifications, object storage, config-driven clients, and app-neutral adapter services.
4
+ ---
5
+
6
+ # JokTec Adapter Skill
7
+
8
+ Use this skill for pluggable external capability adapters.
9
+
10
+ ## Packages
11
+
12
+ - `@joktec/cacher`: cache stores.
13
+ - `@joktec/mailer`: mail delivery.
14
+ - `@joktec/notifier`: push notifications.
15
+ - `@joktec/storage`: object storage and file metadata helpers.
16
+
17
+ ## Rules
18
+
19
+ - Keep adapter services app-neutral.
20
+ - Use validated config and `conId` where supported.
21
+ - Keep secrets and credentials in app config or runtime environment, never in code.
22
+ - Prefer adapter services over direct SDK usage in app services.
23
+
24
+ ## Reference
25
+
26
+ Read `references/adapters.md` for setup and package-specific notes.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "JokTec Adapters"
3
+ short_description: "Use JokTec adapter packages"
4
+ default_prompt: "Use $joktec-adapter-skill when wiring JokTec adapters."
@@ -0,0 +1,12 @@
1
+ # Adapter Usage
2
+
3
+ ## Runtime Pattern
4
+
5
+ Adapters are global Nest modules. Services own native client creation and expose package-specific operations.
6
+
7
+ ## Package Notes
8
+
9
+ - Cacher: choose local, Redis, or Memcached stores based on runtime config.
10
+ - Mailer: centralize mail transport configuration in the service that owns outbound email.
11
+ - Notifier: keep push provider configuration outside app business logic.
12
+ - Storage: keep storage metadata and object operations behind the adapter service.
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: joktec-broker-skill
3
+ description: Use when wiring or using JokTec broker packages @joktec/kafka, @joktec/rabbit, @joktec/sqs, or @joktec/redcast, including client config, producer decorators, consumer loaders, message handlers, auto-binding, and app-level broker flows.
4
+ ---
5
+
6
+ # JokTec Broker Skill
7
+
8
+ Use this skill for message broker packages.
9
+
10
+ ## Packages
11
+
12
+ - `@joktec/kafka`: Kafka client, decorators, loaders, metrics.
13
+ - `@joktec/rabbit`: RabbitMQ client, decorators, auto-binding, metrics.
14
+ - `@joktec/sqs`: AWS SQS/SNS queue and topic wrapper.
15
+ - `@joktec/redcast`: Redis-backed broker behavior.
16
+
17
+ ## Rules
18
+
19
+ - Keep message business semantics in the consumer app.
20
+ - Use broker decorators for transport wiring, not for domain policy.
21
+ - Preserve config-driven client selection and `conId` when available.
22
+ - Keep topic, queue, and routing names explicit in app configuration or decorators.
23
+
24
+ ## Reference
25
+
26
+ Read `references/brokers.md` for setup and package-specific notes.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "JokTec Brokers"
3
+ short_description: "Use JokTec messaging packages"
4
+ default_prompt: "Use $joktec-broker-skill when wiring JokTec broker clients or decorators."
@@ -0,0 +1,12 @@
1
+ # Broker Usage
2
+
3
+ ## Runtime Pattern
4
+
5
+ Broker services follow `AbstractClientService` lifecycle. Loader classes discover decorator metadata after module initialization. Apps define producers, consumers, and message semantics.
6
+
7
+ ## Package Notes
8
+
9
+ - Kafka: check topic existence and broker advertised listeners in local development.
10
+ - RabbitMQ: use module options and decorators for exchanges, queues, and bindings.
11
+ - SQS: local ElasticMQ-style environments may require queues to exist before consumers start.
12
+ - Redcast: use Redis-backed list, stream, or pub/sub behavior when a lightweight broker is enough.
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: joktec-common-skill
3
+ description: Use when implementing or wiring @joktec/core, @joktec/utils, or @joktec/cron in a consumer app, especially BaseController, BaseService, pagination, config, client lifecycle, bootstrap, cron decorators, or utility helpers.
4
+ ---
5
+
6
+ # JokTec Common Skill
7
+
8
+ Use this skill for shared framework primitives, low-level helpers, cron utilities, and config schema type support.
9
+
10
+ ## Packages
11
+
12
+ - `@joktec/core`: NestJS bootstrap, modules, config, logger, metrics, base abstractions, transports, pagination, Bull.
13
+ - `@joktec/utils`: conversion helpers, UUID/OTP/hash generators, validators, common constants.
14
+ - `@joktec/cron`: cron decorators, schedulers, workers, and job contracts.
15
+ - `@joktec/types`: generated config schema/type support for the JokTec package set.
16
+
17
+ ## Rules
18
+
19
+ - Keep `@joktec/core` app-neutral; do not import adapters, brokers, databases, integrations, or consumer app code into core concepts.
20
+ - Use `BaseController` and `BaseService` for standard CRUD flows before hand-writing repetitive controllers.
21
+ - Use page, offset, or cursor pagination contracts from core; let database packages execute storage-specific pagination.
22
+ - Use `AbstractClientService` patterns for client packages that need config, lifecycle, retry, and `conId`.
23
+ - Use `@joktec/utils` for shared helpers instead of duplicating conversion, validation, hashing, or UUID logic.
24
+
25
+ ## References
26
+
27
+ - Read `references/core.md` for bootstrap, CRUD, pagination, and client lifecycle.
28
+ - Read `references/utils-cron.md` for utility helpers and cron usage.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "JokTec Common"
3
+ short_description: "Use core, utilities, and cron"
4
+ default_prompt: "Use $joktec-common-skill when wiring JokTec core, utils, or cron."