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.
- package/README.md +16 -0
- package/package.json +41 -0
- package/src/bundle-codegen.js +259 -0
- package/src/generator.js +107 -0
- package/src/github.js +110 -0
- package/src/index.js +144 -0
- package/src/manual.js +171 -0
- package/src/prompts.js +128 -0
- package/src/template.js +62 -0
- package/templates/apps/blog/astro.config.mjs +25 -0
- package/templates/apps/blog/netlify.toml +3 -0
- package/templates/apps/blog/package.json +34 -0
- package/templates/apps/blog/public/favicon.svg +4 -0
- package/templates/apps/blog/src/layouts/BaseLayout.astro +39 -0
- package/templates/apps/blog/src/pages/index.astro +18 -0
- package/templates/apps/blog/src/pages/posts/welcome.md +23 -0
- package/templates/apps/blog/src/styles/global.css.template +6 -0
- package/templates/apps/blog/tsconfig.json +11 -0
- package/templates/apps/docs/astro.config.mjs +42 -0
- package/templates/apps/docs/netlify.toml +4 -0
- package/templates/apps/docs/package.json +21 -0
- package/templates/apps/docs/src/content/docs/api/python.md +24 -0
- package/templates/apps/docs/src/content/docs/api/typescript.md +24 -0
- package/templates/apps/docs/src/content/docs/getting-started/installation.md +27 -0
- package/templates/apps/docs/src/content/docs/getting-started/introduction.md +22 -0
- package/templates/apps/docs/src/content/docs/getting-started/quick-start.md +28 -0
- package/templates/apps/docs/src/content/docs/index.mdx.template +24 -0
- package/templates/apps/docs/src/styles/custom.css.template +14 -0
- package/templates/apps/docs/tsconfig.json +3 -0
- package/templates/apps/web/index.html +17 -0
- package/templates/apps/web/netlify.toml +6 -0
- package/templates/apps/web/package.json +34 -0
- package/templates/apps/web/postcss.config.js +5 -0
- package/templates/apps/web/public/favicon.svg +4 -0
- package/templates/apps/web/src/App.css +111 -0
- package/templates/apps/web/src/App.tsx +46 -0
- package/templates/apps/web/src/index.css.template +12 -0
- package/templates/apps/web/src/main.tsx +10 -0
- package/templates/apps/web/tsconfig.json +36 -0
- package/templates/apps/web/tsconfig.node.json +13 -0
- package/templates/apps/web/vite.config.ts +21 -0
- package/templates/github/CODE_OF_CONDUCT.md +5 -0
- package/templates/github/CONTRIBUTING.md +49 -0
- package/templates/github/FUNDING.yml +2 -0
- package/templates/github/FUNDING.yml.template +1 -0
- package/templates/github/ISSUE_TEMPLATE/bug_report.md +20 -0
- package/templates/github/ISSUE_TEMPLATE/feature_request.md +18 -0
- package/templates/github/workflows/ci.yml +65 -0
- package/templates/github/workflows/publish.yml +81 -0
- package/templates/github/workflows/release.yml +16 -0
- package/templates/packages/cli/README.md +19 -0
- package/templates/packages/cli/package.json +38 -0
- package/templates/packages/cli/src/index.ts +353 -0
- package/templates/packages/cli/tsconfig.json +23 -0
- package/templates/packages/python/README.md +21 -0
- package/templates/packages/python/__pythonModule__/__init__.py +24 -0
- package/templates/packages/python/__pythonModule__/schemas.py.template +8 -0
- package/templates/packages/python/pyproject.toml +34 -0
- package/templates/packages/python/tests/test_basic.py +20 -0
- package/templates/packages/sdk/README.md.template +25 -0
- package/templates/packages/sdk/package.json.template +43 -0
- package/templates/packages/sdk/src/__tests__/bundle.test.ts.template +18 -0
- package/templates/packages/sdk/src/bundle.ts.template +6 -0
- package/templates/packages/sdk/src/index.ts.template +13 -0
- package/templates/packages/sdk/src/relations.ts.template +6 -0
- package/templates/packages/sdk/src/views.ts.template +6 -0
- package/templates/packages/sdk/tsconfig.json +26 -0
- package/templates/root/.release-please-manifest.json +3 -0
- package/templates/root/CHANGELOG.md +15 -0
- package/templates/root/CHANGELOG.md.template +10 -0
- package/templates/root/LICENSE.template +21 -0
- package/templates/root/README.md.template +42 -0
- package/templates/root/SECURITY.md +35 -0
- package/templates/root/SECURITY.md.template +11 -0
- package/templates/root/SKILLS.md.template +10 -0
- package/templates/root/package.json.template +28 -0
- package/templates/root/pnpm-workspace.yaml +4 -0
- package/templates/root/release-please-config.json +61 -0
- 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,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)
|