Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Use it to prototype architectures, onboard teams faster, or spin up reproducible
- πŸ›  Interactive wizard (or fully non-interactive via flags)
- πŸ” Post-init extensibility: add services & plugins anytime
- πŸ“¦ Shared package (`packages/shared`) for cross-service JS utilities
- πŸ“š **Language-specific shared libraries** (Python packages, Go modules, Java libraries)
- πŸ§ͺ Vitest test setup for the CLI itself
- 🌈 Colorized dev logs & health probing for Node/frontend services
- πŸ”₯ Unified hot reload aggregator (`create-polyglot hot`) for Node, Next.js, Python (uvicorn), Go, and Java (Spring Boot)
Expand Down Expand Up @@ -167,8 +168,11 @@ create-polyglot dev --docker
| `create-polyglot init <name>` | Scaffold a new workspace (root invocation without `init` is deprecated). |
| `create-polyglot add service <name>` | Add a service after init (`--type`, `--port`, `--yes`). |
| `create-polyglot add plugin <name>` | Create plugin skeleton under `plugins/<name>`. |
| `create-polyglot add lib <name>` | Create a shared library (`--type python|go|java`, `--yes`). |
| `create-polyglot remove service <name>` | Remove a service from the workspace (`--keep-files`, `--yes`). |
| `create-polyglot remove plugin <name>` | Remove a plugin from the workspace (`--keep-files`, `--yes`). |
| `create-polyglot remove lib <name>` | Remove a shared library from the workspace (`--keep-files`, `--yes`). |
| `create-polyglot libraries` / `libs` | List all shared libraries (`--json`). |
| `create-polyglot dev [--docker]` | Run Node & frontend services locally or all via compose. |

## Init Options
Expand Down Expand Up @@ -224,26 +228,52 @@ my-org/

## Development Workflow
1. Scaffold with `init`.
2. (Optional) Add more services or plugins.
2. (Optional) Add more services, plugins, or shared libraries.
3. Run `create-polyglot dev` (local) or `create-polyglot dev --docker`.
4. Edit services under `services/<name>`.
5. Extend infra / databases inside `compose.yaml`.

## Managing Services & Plugins
## Managing Services, Plugins & Shared Libraries

### Adding Components
Add services after initial scaffolding:
Add services, plugins, and shared libraries after initial scaffolding:
```bash
# Add services
create-polyglot add service payments --type node --port 4100

# Add plugins
create-polyglot add plugin analytics

# Add shared libraries
create-polyglot add lib common-utils --type python
create-polyglot add lib shared-models --type go
create-polyglot add lib data-types --type java
```

### Listing Components
```bash
# List all services
create-polyglot services

# List all shared libraries
create-polyglot libraries

# Get JSON output
create-polyglot libs --json
```

### Removing Components
Clean removal of services and plugins:
Clean removal of services, plugins, and shared libraries:
```bash
# Remove a service (including files and configuration)
create-polyglot remove service payments --yes

# Remove a shared library
create-polyglot remove lib common-utils

# Keep files but remove from configuration
create-polyglot remove lib common-utils --keep-files

# Remove a plugin
create-polyglot remove plugin analytics --yes

Expand Down Expand Up @@ -305,22 +335,40 @@ If `--frontend-generator create-next-app` is supplied, the tool shells out to `n
Example:
```jsonc
{
"name": "my-org",
"name": "my-org",
"preset": "none",
"packageManager": "npm",
"services": [
{ "name": "node", "type": "node", "port": 3001, "path": "services/node" }
]
],
"sharedLibs": [
{ "name": "common-utils", "type": "python", "path": "packages/libs/common-utils", "createdAt": "2024-01-15T10:30:00.000Z" }
],
"plugins": {}
}
```
Used by `add service` to assert uniqueness and regenerate `compose.yaml`.
Used by `add service` and `add lib` to assert uniqueness and regenerate `compose.yaml`.

## Plugins
`create-polyglot add plugin <name>` scaffolds `plugins/<name>/index.js` with a hook skeleton (`afterInit`). Future releases will execute hooks automatically during lifecycle events.

## Shared Package
## Shared Libraries
`packages/shared` shows cross-service Node utilities. Extend or add per-language shared modules.

For language-specific shared libraries:
```bash
# Create Python package
create-polyglot add lib utils --type python

# Create Go module
create-polyglot add lib common --type go

# Create Java library
create-polyglot add lib models --type java
```

See [Shared Libraries Guide](./docs/guide/shared-libraries.md) for detailed usage.

## Force Overwrite
If the target directory already exists, the CLI aborts unless `--force` is passed. Use with caution.

Expand All @@ -335,8 +383,9 @@ Generates ESLint + Prettier base configs at the root. Extend rules per service i
- Healthchecks and depends_on in `compose.yaml`
- Additional generators (Remix, Astro, SvelteKit)
- Automatic test harness & CI workflow template
- Language-specific shared libs (Python package, Go module)
- Hot reload integration aggregator
- Service dependency management
- Cross-language testing utilities

## Contributing
Contributions welcome! See `CONTRIBUTING.md` for guidelines. Please run tests before submitting a PR:
Expand Down
70 changes: 60 additions & 10 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ program
// Additional commands must be registered before final parse.
program
.command('add')
.description('Add a new service or plugin')
.argument('<entity>', 'service | plugin')
.argument('<name>', 'Name of the service or plugin')
.option('--type <type>', 'Service type (node|python|go|java|frontend|remix|astro|sveltekit)')
.description('Add a new service, plugin, or shared library')
.argument('<entity>', 'service | plugin | lib')
.argument('<name>', 'Name of the service, plugin, or library')
.option('--type <type>', 'Service type (node|python|go|java|frontend|remix|astro|sveltekit) or Library type (python|go)')
.option('--lang <type>', '(Deprecated) Alias of --type')
.option('--port <port>', 'Service port')
.option('--yes', 'Non-interactive defaults')
Expand Down Expand Up @@ -92,8 +92,24 @@ program
await addService(projectDir, { type, name, port }, opts);
} else if (entity === 'plugin') {
await scaffoldPlugin(projectDir, name);
} else if (entity === 'lib') {
let type = opts.type || opts.lang;
if (!opts.yes) {
const promptsMod = await import('prompts');
const p = promptsMod.default;
if (!type) {
const ans = await p({ type: 'select', name: 'type', message: 'Library type:', choices: [
{ title: 'Python Package', value: 'python' },
{ title: 'Go Module', value: 'go' }
] });
type = ans.type;
}
}
if (!type) throw new Error('Library type required');
const { scaffoldSharedLibrary } = await import('./lib/scaffold.js');
await scaffoldSharedLibrary(projectDir, { type, name }, opts);
} else {
console.error(chalk.red(`Unknown entity '${entity}'. Use service or plugin.`));
console.error(chalk.red(`Unknown entity '${entity}'. Use service, plugin, or lib.`));
process.exit(1);
}
} catch (e) {
Expand All @@ -104,10 +120,10 @@ program

program
.command('remove')
.description('Remove a service or plugin')
.argument('<entity>', 'service | plugin')
.argument('<name>', 'Name of the service or plugin')
.option('--keep-files', 'Keep service files, only remove from configuration')
.description('Remove a service, plugin, or shared library')
.argument('<entity>', 'service | plugin | lib')
.argument('<name>', 'Name of the service, plugin, or library')
.option('--keep-files', 'Keep service/library files, only remove from configuration')
.option('--yes', 'Skip confirmation prompt')
.action(async (entity, name, opts) => {
const projectDir = process.cwd();
Expand All @@ -116,8 +132,11 @@ program
await removeService(projectDir, name, opts);
} else if (entity === 'plugin') {
await removePlugin(projectDir, name, opts);
} else if (entity === 'lib') {
const { removeSharedLibrary } = await import('./lib/scaffold.js');
await removeSharedLibrary(projectDir, name, opts);
} else {
console.error(chalk.red(`Unknown entity '${entity}'. Use service or plugin.`));
console.error(chalk.red(`Unknown entity '${entity}'. Use service, plugin, or lib.`));
process.exit(1);
}
} catch (e) {
Expand Down Expand Up @@ -158,6 +177,37 @@ program
}
});

program
.command('libraries')
.alias('libs')
.description('List shared libraries in the current workspace (table)')
.option('--json', 'Output raw JSON instead of table')
.action(async (opts) => {
try {
const cwd = process.cwd();
const cfgPath = path.join(cwd, 'polyglot.json');
if (!fs.existsSync(cfgPath)) {
console.log(chalk.red('polyglot.json not found. Run inside a generated workspace.'));
process.exit(1);
}
const cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf-8'));
const libs = cfg.sharedLibs || [];
if (opts.json) {
console.log(JSON.stringify(libs, null, 2));
} else {
if (libs.length === 0) {
console.log(chalk.yellow('No shared libraries found.'));
} else {
const { renderLibrariesTable } = await import('./lib/ui.js');
renderLibrariesTable(libs, { title: 'Shared Libraries' });
}
}
} catch (e) {
console.error(chalk.red('Failed to list libraries:'), e.message);
process.exit(1);
}
});

program
.command('admin')
.description('Launch admin dashboard to monitor service status')
Expand Down
Loading
Loading