create-minions-bundle 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.
Files changed (79) hide show
  1. package/README.md +16 -0
  2. package/package.json +41 -0
  3. package/src/bundle-codegen.js +259 -0
  4. package/src/generator.js +107 -0
  5. package/src/github.js +110 -0
  6. package/src/index.js +144 -0
  7. package/src/manual.js +171 -0
  8. package/src/prompts.js +128 -0
  9. package/src/template.js +62 -0
  10. package/templates/apps/blog/astro.config.mjs +25 -0
  11. package/templates/apps/blog/netlify.toml +3 -0
  12. package/templates/apps/blog/package.json +34 -0
  13. package/templates/apps/blog/public/favicon.svg +4 -0
  14. package/templates/apps/blog/src/layouts/BaseLayout.astro +39 -0
  15. package/templates/apps/blog/src/pages/index.astro +18 -0
  16. package/templates/apps/blog/src/pages/posts/welcome.md +23 -0
  17. package/templates/apps/blog/src/styles/global.css.template +6 -0
  18. package/templates/apps/blog/tsconfig.json +11 -0
  19. package/templates/apps/docs/astro.config.mjs +42 -0
  20. package/templates/apps/docs/netlify.toml +4 -0
  21. package/templates/apps/docs/package.json +21 -0
  22. package/templates/apps/docs/src/content/docs/api/python.md +24 -0
  23. package/templates/apps/docs/src/content/docs/api/typescript.md +24 -0
  24. package/templates/apps/docs/src/content/docs/getting-started/installation.md +27 -0
  25. package/templates/apps/docs/src/content/docs/getting-started/introduction.md +22 -0
  26. package/templates/apps/docs/src/content/docs/getting-started/quick-start.md +28 -0
  27. package/templates/apps/docs/src/content/docs/index.mdx.template +24 -0
  28. package/templates/apps/docs/src/styles/custom.css.template +14 -0
  29. package/templates/apps/docs/tsconfig.json +3 -0
  30. package/templates/apps/web/index.html +17 -0
  31. package/templates/apps/web/netlify.toml +6 -0
  32. package/templates/apps/web/package.json +34 -0
  33. package/templates/apps/web/postcss.config.js +5 -0
  34. package/templates/apps/web/public/favicon.svg +4 -0
  35. package/templates/apps/web/src/App.css +111 -0
  36. package/templates/apps/web/src/App.tsx +46 -0
  37. package/templates/apps/web/src/index.css.template +12 -0
  38. package/templates/apps/web/src/main.tsx +10 -0
  39. package/templates/apps/web/tsconfig.json +36 -0
  40. package/templates/apps/web/tsconfig.node.json +13 -0
  41. package/templates/apps/web/vite.config.ts +21 -0
  42. package/templates/github/CODE_OF_CONDUCT.md +5 -0
  43. package/templates/github/CONTRIBUTING.md +49 -0
  44. package/templates/github/FUNDING.yml +2 -0
  45. package/templates/github/FUNDING.yml.template +1 -0
  46. package/templates/github/ISSUE_TEMPLATE/bug_report.md +20 -0
  47. package/templates/github/ISSUE_TEMPLATE/feature_request.md +18 -0
  48. package/templates/github/workflows/ci.yml +65 -0
  49. package/templates/github/workflows/publish.yml +81 -0
  50. package/templates/github/workflows/release.yml +16 -0
  51. package/templates/packages/cli/README.md +19 -0
  52. package/templates/packages/cli/package.json +38 -0
  53. package/templates/packages/cli/src/index.ts +353 -0
  54. package/templates/packages/cli/tsconfig.json +23 -0
  55. package/templates/packages/python/README.md +21 -0
  56. package/templates/packages/python/__pythonModule__/__init__.py +24 -0
  57. package/templates/packages/python/__pythonModule__/schemas.py.template +8 -0
  58. package/templates/packages/python/pyproject.toml +34 -0
  59. package/templates/packages/python/tests/test_basic.py +20 -0
  60. package/templates/packages/sdk/README.md.template +25 -0
  61. package/templates/packages/sdk/package.json.template +43 -0
  62. package/templates/packages/sdk/src/__tests__/bundle.test.ts.template +18 -0
  63. package/templates/packages/sdk/src/bundle.ts.template +6 -0
  64. package/templates/packages/sdk/src/index.ts.template +13 -0
  65. package/templates/packages/sdk/src/relations.ts.template +6 -0
  66. package/templates/packages/sdk/src/views.ts.template +6 -0
  67. package/templates/packages/sdk/tsconfig.json +26 -0
  68. package/templates/root/.release-please-manifest.json +3 -0
  69. package/templates/root/CHANGELOG.md +15 -0
  70. package/templates/root/CHANGELOG.md.template +10 -0
  71. package/templates/root/LICENSE.template +21 -0
  72. package/templates/root/README.md.template +42 -0
  73. package/templates/root/SECURITY.md +35 -0
  74. package/templates/root/SECURITY.md.template +11 -0
  75. package/templates/root/SKILLS.md.template +10 -0
  76. package/templates/root/package.json.template +28 -0
  77. package/templates/root/pnpm-workspace.yaml +4 -0
  78. package/templates/root/release-please-config.json +61 -0
  79. package/templates/root/tsconfig.json +31 -0
@@ -0,0 +1,81 @@
1
+ name: Publish Packages
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ tags:
7
+ - 'v*'
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ publish-npm:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Install pnpm
20
+ uses: pnpm/action-setup@v4
21
+ with:
22
+ version: 9
23
+
24
+ - name: Use Node.js 20
25
+ uses: actions/setup-node@v4
26
+ with:
27
+ node-version: 20
28
+ cache: pnpm
29
+ registry-url: https://registry.npmjs.org
30
+
31
+ - name: Install dependencies
32
+ run: pnpm install
33
+
34
+ - name: Lint
35
+ run: pnpm run lint
36
+
37
+ - name: Build
38
+ run: pnpm run build
39
+
40
+ - name: Test
41
+ run: pnpm run test
42
+
43
+ - name: Publish {{sdkName}}
44
+ run: pnpm --filter {{sdkName}} publish --no-git-checks --access public
45
+ env:
46
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
47
+
48
+ - name: Publish {{cliName}}
49
+ run: pnpm --filter {{cliName}} publish --no-git-checks --access public
50
+ env:
51
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
52
+
53
+ publish-python:
54
+ runs-on: ubuntu-latest
55
+
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+
59
+ - name: Set up Python 3.12
60
+ uses: actions/setup-python@v5
61
+ with:
62
+ python-version: '3.12'
63
+
64
+ - name: Install build tools
65
+ run: pip install hatch
66
+
67
+ - name: Run tests
68
+ working-directory: packages/python
69
+ run: |
70
+ pip install -e ".[test]"
71
+ pytest -v
72
+
73
+ - name: Build package
74
+ working-directory: packages/python
75
+ run: hatch build
76
+
77
+ - name: Publish to PyPI
78
+ uses: pypa/gh-action-pypi-publish@release/v1
79
+ with:
80
+ packages-dir: packages/python/dist/
81
+ password: ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,16 @@
1
+ name: Release Please
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ permissions:
7
+ contents: write
8
+ pull-requests: write
9
+ jobs:
10
+ release-please:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: googleapis/release-please-action@v4
14
+ with:
15
+ config-file: release-please-config.json
16
+ manifest-file: .release-please-manifest.json
@@ -0,0 +1,19 @@
1
+ # {{cliName}}
2
+
3
+ CLI for {{projectCapitalized}} — {{projectDescription}}
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g {{cliName}}
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ {{cliCommand}} --help
15
+ ```
16
+
17
+ ## License
18
+
19
+ [{{license}}](../../LICENSE)
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "{{cliName}}",
3
+ "version": "0.1.0",
4
+ "description": "CLI for {{projectName}} — {{projectDescription}}",
5
+ "license": "{{license}}",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/{{githubRepo}}"
9
+ },
10
+ "homepage": "https://{{domainHelp}}",
11
+ "keywords": {{keywordsJson}},
12
+ "author": "{{authorName}} <{{authorEmail}}> ({{authorUrl}})",
13
+ "type": "module",
14
+ "bin": {
15
+ "{{cliCommand}}": "./dist/index.js"
16
+ },
17
+ "main": "./dist/index.js",
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "lint": "tsc --noEmit",
25
+ "dev": "node --loader ts-node/esm src/index.ts"
26
+ },
27
+ "dependencies": {
28
+ "{{sdkName}}": "workspace:*",
29
+ "chalk": "^5.3.0",
30
+ "commander": "^14.0.3",
31
+ "inquirer": "^13.2.5",
32
+ "minions-sdk": "^0.2.2"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.3.0",
36
+ "typescript": "^5.9.3"
37
+ }
38
+ }
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * {{cliName}} — CLI for {{projectCapitalized}}
5
+ *
6
+ * Uses minions-sdk's JsonFileStorageAdapter for sharded, atomic file storage:
7
+ * <rootDir>/<id[0..1]>/<id[2..3]>/<id>.json
8
+ */
9
+
10
+ import { Command } from 'commander';
11
+ import chalk from 'chalk';
12
+ import {
13
+ createMinion,
14
+ updateMinion,
15
+ softDelete,
16
+ generateId,
17
+ TypeRegistry,
18
+ } from 'minions-sdk';
19
+ import type { Minion, StorageFilter } from 'minions-sdk';
20
+ import { JsonFileStorageAdapter } from 'minions-sdk/node';
21
+ import { bundleTypes as customTypes } from '{{sdkName}}';
22
+
23
+ const program = new Command();
24
+ const STORE_DIR = process.env.MINIONS_STORE || '.minions';
25
+
26
+ // Register custom types
27
+ const registry = new TypeRegistry();
28
+ for (const t of customTypes) {
29
+ registry.register(t);
30
+ }
31
+
32
+ // Lazily initialize storage (async)
33
+ let _storage: import('minions-sdk').StorageAdapter | null = null;
34
+ async function getStorage() {
35
+ if (!_storage) {
36
+ _storage = await JsonFileStorageAdapter.create(STORE_DIR);
37
+ }
38
+ return _storage;
39
+ }
40
+
41
+ function findType(slug: string) {
42
+ const type = registry.getBySlug(slug);
43
+ if (!type) {
44
+ console.error(chalk.red(`Unknown type: ${slug}`));
45
+ console.error(chalk.dim(`Available: ${customTypes.map(t => t.slug).join(', ')}`));
46
+ process.exit(1);
47
+ }
48
+ return type;
49
+ }
50
+
51
+ program
52
+ .name('{{cliCommand}}')
53
+ .description('{{projectDescription}}')
54
+ .version('0.1.0');
55
+
56
+ // ─── info ──────────────────────────────────────────────────
57
+ program
58
+ .command('info')
59
+ .description('Show project info')
60
+ .action(() => {
61
+ console.log(chalk.bold('{{projectCapitalized}}'));
62
+ console.log(chalk.dim('{{projectDescription}}'));
63
+ console.log('');
64
+ console.log(` SDK: ${chalk.cyan('{{sdkName}}')}`);
65
+ console.log(` CLI: ${chalk.cyan('{{cliName}}')}`);
66
+ console.log(` Python: ${chalk.cyan('{{pythonPackage}}')}`);
67
+ console.log(` Store: ${chalk.cyan(STORE_DIR)}`);
68
+ console.log(` Types: ${chalk.cyan(String(customTypes.length))}`);
69
+ });
70
+
71
+ // ─── types ─────────────────────────────────────────────────
72
+ const types = program.command('types').description('Manage MinionType schemas');
73
+
74
+ types
75
+ .command('list')
76
+ .alias('ls')
77
+ .description('List all available MinionTypes')
78
+ .action(() => {
79
+ console.log(chalk.bold(`\n ${customTypes.length} MinionTypes available:\n`));
80
+ for (const type of customTypes) {
81
+ const fieldCount = type.schema.length;
82
+ console.log(` ${type.icon} ${chalk.bold(type.name)} ${chalk.dim(`(${type.slug})`)}`);
83
+ console.log(` ${chalk.dim(type.description || '')}`);
84
+ console.log(` ${chalk.dim(`${fieldCount} fields: ${type.schema.map(f => f.name).join(', ')}`)}`);
85
+ console.log('');
86
+ }
87
+ });
88
+
89
+ types
90
+ .command('show <slug>')
91
+ .description('Show detailed schema for a MinionType')
92
+ .action((slug: string) => {
93
+ const type = findType(slug);
94
+ console.log(`\n ${type.icon} ${chalk.bold(type.name)}`);
95
+ console.log(` ${chalk.dim(type.description || '')}`);
96
+ console.log(` ${chalk.dim(`ID: ${type.id} Slug: ${type.slug}`)}\n`);
97
+ console.log(chalk.bold(' Fields:\n'));
98
+ for (const field of type.schema) {
99
+ const typeColor = field.type === 'string' ? 'green' : field.type === 'number' ? 'yellow' : field.type === 'boolean' ? 'blue' : 'magenta';
100
+ const req = field.required ? chalk.red('*') : ' ';
101
+ console.log(` ${req} ${chalk.bold(field.name)} ${(chalk as any)[typeColor](field.type)}${field.description ? ` ${chalk.dim(field.description)}` : ''}`);
102
+ }
103
+ console.log('');
104
+ });
105
+
106
+ // ─── create ────────────────────────────────────────────────
107
+ program
108
+ .command('create <type>')
109
+ .description('Create a new Minion of the specified type')
110
+ .option('-d, --data <json>', 'Field data as JSON string')
111
+ .option('-f, --file <path>', 'Read field data from a JSON file')
112
+ .option('-t, --title <title>', 'Minion title')
113
+ .option('-s, --status <status>', 'Status: active, todo, in_progress, completed, cancelled')
114
+ .option('-p, --priority <priority>', 'Priority: low, medium, high, urgent')
115
+ .option('--tags <tags>', 'Comma-separated tags')
116
+ .action(async (typeSlug: string, opts: any) => {
117
+ const type = findType(typeSlug);
118
+ const storage = await getStorage();
119
+
120
+ let fields: Record<string, unknown> = {};
121
+ if (opts.file) {
122
+ const { readFileSync } = await import('fs');
123
+ fields = JSON.parse(readFileSync(opts.file, 'utf-8'));
124
+ } else if (opts.data) {
125
+ fields = JSON.parse(opts.data);
126
+ }
127
+
128
+ const title = opts.title || (fields as any).title || (fields as any).name || type.name;
129
+ const tags = opts.tags ? opts.tags.split(',').map((t: string) => t.trim()) : undefined;
130
+
131
+ const { minion } = createMinion({
132
+ title,
133
+ fields,
134
+ status: opts.status || 'active',
135
+ priority: opts.priority,
136
+ tags,
137
+ createdBy: 'cli',
138
+ }, type);
139
+
140
+ await storage.set(minion);
141
+
142
+ console.log(chalk.green(`\n ✔ Created ${type.icon} ${type.name}`));
143
+ console.log(` ${chalk.dim('ID:')} ${minion.id}`);
144
+ console.log(` ${chalk.dim('Title:')} ${minion.title}`);
145
+ console.log(` ${chalk.dim('Path:')} ${STORE_DIR}/${minion.id.replace(/-/g, '').slice(0, 2)}/${minion.id.replace(/-/g, '').slice(2, 4)}/${minion.id}.json`);
146
+ console.log('');
147
+ });
148
+
149
+ // ─── list ──────────────────────────────────────────────────
150
+ program
151
+ .command('list [type]')
152
+ .alias('ls')
153
+ .description('List all Minions, optionally filtered by type')
154
+ .option('--status <status>', 'Filter by status')
155
+ .option('--json', 'Output as JSON')
156
+ .option('-n, --limit <n>', 'Max results', parseInt)
157
+ .action(async (typeSlug: string | undefined, opts: any) => {
158
+ const storage = await getStorage();
159
+ const filter: StorageFilter = {};
160
+ if (typeSlug) {
161
+ const type = findType(typeSlug);
162
+ filter.minionTypeId = type.id;
163
+ }
164
+ if (opts.status) filter.status = opts.status;
165
+ if (opts.limit) filter.limit = opts.limit;
166
+
167
+ const minions = await storage.list(filter);
168
+
169
+ if (opts.json) { console.log(JSON.stringify(minions, null, 2)); return; }
170
+ if (minions.length === 0) { console.log(chalk.dim('\n No Minions found.\n')); return; }
171
+
172
+ console.log(chalk.bold(`\n ${minions.length} Minion(s):\n`));
173
+ for (const m of minions) {
174
+ const type = registry.getById(m.minionTypeId);
175
+ const icon = type?.icon || '?';
176
+ const status = m.status ? chalk.dim(`[${m.status}]`) : '';
177
+ console.log(` ${icon} ${chalk.bold(m.title)} ${status}`);
178
+ console.log(` ${chalk.dim(m.id)} ${chalk.dim(type?.slug || m.minionTypeId)}`);
179
+ }
180
+ console.log('');
181
+ });
182
+
183
+ // ─── show ──────────────────────────────────────────────────
184
+ program
185
+ .command('show <id>')
186
+ .description('Show a Minion by ID')
187
+ .option('--json', 'Output as JSON')
188
+ .action(async (id: string, opts: any) => {
189
+ const storage = await getStorage();
190
+ const minion = await storage.get(id);
191
+
192
+ if (!minion) {
193
+ console.error(chalk.red(`\n Minion not found: ${id}\n`));
194
+ process.exit(1);
195
+ }
196
+
197
+ if (opts.json) { console.log(JSON.stringify(minion, null, 2)); return; }
198
+
199
+ const type = registry.getById(minion.minionTypeId);
200
+ console.log(`\n ${type?.icon || '?'} ${chalk.bold(minion.title)}`);
201
+ console.log(` ${chalk.dim(`Type: ${type?.slug || minion.minionTypeId} ID: ${minion.id}`)}`);
202
+ console.log(` ${chalk.dim(`Status: ${minion.status || '-'} Priority: ${minion.priority || '-'}`)}`);
203
+ console.log(` ${chalk.dim(`Created: ${minion.createdAt} Updated: ${minion.updatedAt}`)}`);
204
+ if (minion.tags?.length) console.log(` ${chalk.dim(`Tags: ${minion.tags.join(', ')}`)}`);
205
+ console.log(chalk.bold('\n Fields:\n'));
206
+ for (const [key, value] of Object.entries(minion.fields || {})) {
207
+ console.log(` ${chalk.dim('•')} ${chalk.bold(key)}: ${value}`);
208
+ }
209
+ console.log('');
210
+ });
211
+
212
+ // ─── update ────────────────────────────────────────────────
213
+ program
214
+ .command('update <id>')
215
+ .description('Update fields on an existing Minion')
216
+ .option('-d, --data <json>', 'Fields to update as JSON')
217
+ .option('-s, --status <status>', 'Update status')
218
+ .option('-p, --priority <priority>', 'Update priority')
219
+ .option('-t, --title <title>', 'Update title')
220
+ .option('--tags <tags>', 'Replace tags (comma-separated)')
221
+ .action(async (id: string, opts: any) => {
222
+ const storage = await getStorage();
223
+ const existing = await storage.get(id);
224
+ if (!existing) {
225
+ console.error(chalk.red(`\n Minion not found: ${id}\n`));
226
+ process.exit(1);
227
+ }
228
+
229
+ const updates: any = {};
230
+ if (opts.data) updates.fields = { ...existing.fields, ...JSON.parse(opts.data) };
231
+ if (opts.status) updates.status = opts.status;
232
+ if (opts.priority) updates.priority = opts.priority;
233
+ if (opts.title) updates.title = opts.title;
234
+ if (opts.tags) updates.tags = opts.tags.split(',').map((t: string) => t.trim());
235
+
236
+ const typeDefinition = registry.getById(existing.minionTypeId);
237
+ if (!typeDefinition) {
238
+ console.error(chalk.red(`\n Unknown MinionType for this minion.\n`));
239
+ process.exit(1);
240
+ }
241
+ const { minion: updated } = updateMinion(existing, { ...updates, updatedBy: 'cli' }, typeDefinition);
242
+ await storage.set(updated);
243
+
244
+ const type = registry.getById(updated.minionTypeId);
245
+ console.log(chalk.green(`\n ✔ Updated ${type?.icon || '?'} ${updated.title}`));
246
+ for (const [key, value] of Object.entries(updates)) {
247
+ if (key === 'fields') {
248
+ for (const [fk, fv] of Object.entries(value as any)) {
249
+ console.log(` ${chalk.dim('•')} fields.${fk} → ${fv}`);
250
+ }
251
+ } else {
252
+ console.log(` ${chalk.dim('•')} ${key} → ${value}`);
253
+ }
254
+ }
255
+ console.log('');
256
+ });
257
+
258
+ // ─── delete ────────────────────────────────────────────────
259
+ program
260
+ .command('delete <id>')
261
+ .description('Soft-delete a Minion (set deletedAt timestamp)')
262
+ .option('--hard', 'Permanently remove the file from disk')
263
+ .action(async (id: string, opts: any) => {
264
+ const storage = await getStorage();
265
+ const existing = await storage.get(id);
266
+ if (!existing) {
267
+ console.error(chalk.red(`\n Minion not found: ${id}\n`));
268
+ process.exit(1);
269
+ }
270
+
271
+ if (opts.hard) {
272
+ await storage.delete(id);
273
+ console.log(chalk.yellow(`\n 🗑 Permanently deleted ${id}\n`));
274
+ } else {
275
+ const deleted = softDelete(existing, 'cli');
276
+ await storage.set(deleted);
277
+ console.log(chalk.yellow(`\n ✔ Soft-deleted ${existing.title}`));
278
+ console.log(chalk.dim(` Use --hard to permanently remove\n`));
279
+ }
280
+ });
281
+
282
+ // ─── search ────────────────────────────────────────────────
283
+ program
284
+ .command('search <query>')
285
+ .description('Full-text search across all Minions')
286
+ .option('--json', 'Output as JSON')
287
+ .action(async (query: string, opts: any) => {
288
+ const storage = await getStorage();
289
+ const results = await storage.search(query);
290
+
291
+ if (opts.json) { console.log(JSON.stringify(results, null, 2)); return; }
292
+ if (results.length === 0) { console.log(chalk.dim(`\n No results for "${query}".\n`)); return; }
293
+
294
+ console.log(chalk.bold(`\n ${results.length} result(s) for "${query}":\n`));
295
+ for (const m of results) {
296
+ const type = registry.getById(m.minionTypeId);
297
+ const icon = type?.icon || '?';
298
+ const status = m.status ? chalk.dim(`[${m.status}]`) : '';
299
+ console.log(` ${icon} ${chalk.bold(m.title)} ${status}`);
300
+ console.log(` ${chalk.dim(m.id)} ${chalk.dim(type?.slug || m.minionTypeId)}`);
301
+ }
302
+ console.log('');
303
+ });
304
+
305
+ // ─── validate ──────────────────────────────────────────────
306
+ program
307
+ .command('validate <file>')
308
+ .description('Validate a JSON file against its MinionType schema')
309
+ .action(async (file: string) => {
310
+ const { readFileSync } = await import('fs');
311
+ const { validateFields } = await import('minions-sdk');
312
+ const data = JSON.parse(readFileSync(file, 'utf-8')) as Minion;
313
+ const type = registry.getById(data.minionTypeId);
314
+
315
+ if (!type) {
316
+ console.error(chalk.red(`\n Unknown type: ${data.minionTypeId}\n`));
317
+ process.exit(1);
318
+ }
319
+
320
+ const result = validateFields(data.fields, type.schema);
321
+ if (result.valid) {
322
+ console.log(chalk.green(`\n ✔ Valid ${type.icon} ${type.name}\n`));
323
+ } else {
324
+ console.log(chalk.red(`\n ✘ ${result.errors.length} validation error(s):\n`));
325
+ for (const err of result.errors) {
326
+ console.log(` ${chalk.red('•')} ${err.field}: ${err.message}`);
327
+ }
328
+ console.log('');
329
+ process.exit(1);
330
+ }
331
+ });
332
+
333
+ // ─── stats ─────────────────────────────────────────────────
334
+ program
335
+ .command('stats')
336
+ .description('Show statistics about stored Minions')
337
+ .action(async () => {
338
+ const storage = await getStorage();
339
+ console.log(chalk.bold('\n Minion Statistics:\n'));
340
+
341
+ let total = 0;
342
+ for (const type of customTypes) {
343
+ const minions = await storage.list({ minionTypeId: type.id });
344
+ const count = minions.length;
345
+ total += count;
346
+ const bar = chalk.cyan('█'.repeat(Math.min(count, 30)));
347
+ console.log(` ${type.icon} ${(type.name || '').padEnd(22)} ${String(count).padStart(4)} ${count > 0 ? bar : chalk.dim('0')}`);
348
+ }
349
+ console.log(`\n ${chalk.bold('Total:')} ${total} Minion(s)`);
350
+ console.log(` ${chalk.dim(`Store: ${STORE_DIR}`)}\n`);
351
+ });
352
+
353
+ program.parse();
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": [
7
+ "ES2022"
8
+ ],
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "declaration": true,
12
+ "strict": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true
15
+ },
16
+ "include": [
17
+ "src/**/*"
18
+ ],
19
+ "exclude": [
20
+ "node_modules",
21
+ "dist"
22
+ ]
23
+ }
@@ -0,0 +1,21 @@
1
+ # {{pythonPackage}}
2
+
3
+ {{projectDescription}}
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install {{pythonPackage}}
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from {{pythonModule}} import create_client
15
+
16
+ client = create_client()
17
+ ```
18
+
19
+ ## License
20
+
21
+ [{{license}}](../../LICENSE)
@@ -0,0 +1,24 @@
1
+ """
2
+ {{projectCapitalized}} Python SDK
3
+
4
+ {{projectDescription}}
5
+ """
6
+
7
+ __version__ = "0.1.0"
8
+
9
+
10
+ def create_client(**kwargs):
11
+ """Create a client for {{projectCapitalized}}.
12
+
13
+ Args:
14
+ **kwargs: Configuration options.
15
+
16
+ Returns:
17
+ dict: Client configuration.
18
+ """
19
+ return {
20
+ "version": __version__,
21
+ **kwargs,
22
+ }
23
+
24
+ from .schemas import *
@@ -0,0 +1,8 @@
1
+ """
2
+ {{projectCapitalized}} SDK — Type Schemas
3
+ Custom MinionType schemas for {{projectCapitalized}}.
4
+ """
5
+
6
+ from minions.types import FieldDefinition, FieldValidation, MinionType
7
+
8
+ {{pythonSchemas}}
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "{{pythonPackage}}"
7
+ version = "0.1.0"
8
+ description = "{{projectDescription}}"
9
+ readme = "README.md"
10
+ license = { text = "{{license}}" }
11
+ requires-python = ">=3.11"
12
+ keywords = {{keywordsJson}}
13
+ authors = [
14
+ { name = "{{authorName}}", email = "{{authorEmail}}" }
15
+ ]
16
+ dependencies = [
17
+ "minions-sdk>=0.2.1",
18
+ ]
19
+
20
+ [project.optional-dependencies]
21
+ test = ["pytest>=7.0"]
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/{{githubRepo}}"
25
+ Repository = "https://github.com/{{githubRepo}}"
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["{{pythonModule}}"]
29
+
30
+ [tool.pytest.ini_options]
31
+ testpaths = ["tests"]
32
+ python_files = ["test_*.py"]
33
+ python_classes = ["Test*"]
34
+ python_functions = ["test_*"]
@@ -0,0 +1,20 @@
1
+ """Tests for {{pythonPackage}}."""
2
+
3
+ from {{pythonModule}} import create_client, __version__
4
+
5
+
6
+ def test_version():
7
+ """Test that version is set."""
8
+ assert __version__ == "0.1.0"
9
+
10
+
11
+ def test_create_client():
12
+ """Test client creation."""
13
+ client = create_client()
14
+ assert client["version"] == "0.1.0"
15
+
16
+
17
+ def test_create_client_with_options():
18
+ """Test client creation with options."""
19
+ client = create_client(debug=True)
20
+ assert client["debug"] is True
@@ -0,0 +1,25 @@
1
+ # {{sdkName}}
2
+
3
+ {{projectDescription}}
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install {{sdkName}} minions-sdk
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { createMinion } from '{{sdkName}}';
15
+
16
+ // TODO: Add your SDK's usage example here
17
+ ```
18
+
19
+ ## API
20
+
21
+ See the [documentation](https://{{domainHelp}}) for full API reference.
22
+
23
+ ## License
24
+
25
+ [{{license}}](../../LICENSE)