diff --git a/.changeset/little-gorillas-accept.md b/.changeset/little-gorillas-accept.md
new file mode 100644
index 0000000..5cbf88c
--- /dev/null
+++ b/.changeset/little-gorillas-accept.md
@@ -0,0 +1,5 @@
+---
+"@dotgithub/cli": patch
+---
+
+Add starter template support to `dotgithub init` (`node-library`, `bun-app`, `monorepo`) and update init command docs.
diff --git a/README.md b/README.md
index 9a597e8..b410f04 100644
--- a/README.md
+++ b/README.md
@@ -33,13 +33,13 @@ dotgithub init
dotgithub init --output ./my-workflows
```
-### Add GitHub Actions
+### Add GitHub Actions (and generate TypeScript wrappers)
```bash
-# Add a specific action
+# Add a specific action and generate its TypeScript wrapper/types
dotgithub add actions/checkout@v4
-# Add multiple actions
+# Add multiple actions and generate wrappers for each
dotgithub add actions/setup-node@v4 actions/setup-python@v5
```
@@ -53,7 +53,7 @@ dotgithub synth
## Basic Usage
1. **Initialize** your project with `dotgithub init`
-2. **Configure** actions in `dotgithub.json`
+2. **Add actions** with `dotgithub add ...` (this generates TypeScript action wrappers and updates config)
3. **Write** your workflow logic in TypeScript
4. **Synthesize** workflows with `dotgithub synth`
diff --git a/docs/command-init.md b/docs/command-init.md
index 77bc0f8..7d03888 100644
--- a/docs/command-init.md
+++ b/docs/command-init.md
@@ -15,7 +15,8 @@ The `init` command sets up a new DotGitHub project by creating the necessary dir
## Options
- `--force` - Overwrite existing files if they exist
-- `--output
` - Output directory for the workspace (default: `src`)
+- `--output ` - Output directory for the workspace (default: `.github`)
+- `--template ` - Starter template (`node-library`, `bun-app`, `monorepo`)
## What it creates
@@ -35,7 +36,7 @@ The `init` command creates the following files and directories:
dotgithub init
```
-This creates a `src/` directory with all necessary files.
+This creates a `.github/` workspace with all necessary files.
### Custom output directory
@@ -43,7 +44,19 @@ This creates a `src/` directory with all necessary files.
dotgithub init --output ./my-workflows
```
-This creates a `my-workflows/` directory instead of `src/`.
+This creates a `my-workflows/` directory instead of `.github/`.
+
+### Choose a starter template
+
+```bash
+dotgithub init --template bun-app
+```
+
+Available templates:
+
+- `node-library` (default)
+- `bun-app`
+- `monorepo`
### Force overwrite existing files
@@ -89,7 +102,7 @@ A basic entry point that imports the DotGitHub core library.
After running `init`, you typically:
-1. Navigate to the workspace directory: `cd src`
+1. Navigate to the workspace directory: `cd .github/src`
2. Install dependencies: `npm install`
3. Add GitHub Actions: `dotgithub add actions/checkout@v4`
4. Write your workflow logic in TypeScript
diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts
index 65a634c..cf6fac6 100644
--- a/packages/cli/src/commands/init.ts
+++ b/packages/cli/src/commands/init.ts
@@ -2,16 +2,18 @@ import { Command } from 'commander';
import * as fs from 'fs';
import * as path from 'path';
import {
- createConfigFile,
- writeConfig,
createDefaultConfig,
type DotGithubContext,
logger,
} from '@dotgithub/core';
+const SUPPORTED_TEMPLATES = ['node-library', 'bun-app', 'monorepo'] as const;
+type StarterTemplate = (typeof SUPPORTED_TEMPLATES)[number];
+
export interface InitCommandOptions {
force?: boolean;
output?: string;
+ template?: StarterTemplate;
}
export function createInitCommand(
@@ -19,7 +21,7 @@ export function createInitCommand(
): Command {
return new Command('init')
.description(
- 'Initialize a new GitHub Actions workspace with TypeScript and ESM support'
+ 'Initialize a new GitHub Actions TypeScript workspace with synth-ready starter templates'
)
.option('--force', 'Overwrite existing files if they exist', false)
.option(
@@ -27,6 +29,11 @@ export function createInitCommand(
'Output directory for the workspace (default: .github)',
'.github'
)
+ .option(
+ '--template ',
+ `Starter template: ${SUPPORTED_TEMPLATES.join(', ')}`,
+ 'node-library'
+ )
.action(async (options: InitCommandOptions) => {
try {
await initializeWorkspace(options, createContext);
@@ -48,6 +55,8 @@ async function initializeWorkspace(
const outputDir = options.output || '.github';
const outputDirPath = path.resolve(outputDir);
+ const template = normalizeTemplate(options.template);
+
// Create output directory if it doesn't exist
if (!fs.existsSync(outputDirPath)) {
fs.mkdirSync(outputDirPath, { recursive: true });
@@ -65,9 +74,9 @@ async function initializeWorkspace(
// Create default config with workspace output directory
const defaultConfig = createDefaultConfig();
- defaultConfig.rootDir = 'src'; // This will be the workspace directory inside the output directory
- defaultConfig.outputDir = '.'; // Set output directory to current directory
-
+ defaultConfig.rootDir = 'src'; // Workspace directory inside the output directory
+ defaultConfig.outputDir = '.';
+
// Add a local construct definition
defaultConfig.constructs = [
{
@@ -113,7 +122,7 @@ async function initializeWorkspace(
}
// Generate package.json
- const packageJson = generatePackageJson();
+ const packageJson = generatePackageJson(template);
fs.writeFileSync(
packageJsonPath,
JSON.stringify(packageJson, null, 2) + '\n'
@@ -124,17 +133,18 @@ async function initializeWorkspace(
if (fs.existsSync(tsconfigPath) && !options.force) {
logger.info('✓ tsconfig.json already exists, skipping');
} else {
- const tsconfig = generateTsConfig();
+ const tsconfig = generateTsConfig(template);
fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n');
}
// Create basic index.ts file
const indexPath = path.join(workspaceDir, 'index.ts');
if (!fs.existsSync(indexPath) || options.force) {
- const indexContent = generateIndexFile();
+ const indexContent = generateIndexFile(template);
fs.writeFileSync(indexPath, indexContent);
}
+ logger.info(`Template: ${template}`);
logger.info('Generated files:');
logger.debug(` ${outputDir}/dotgithub.json`);
logger.debug(` ${workspaceDir}/package.json`);
@@ -145,11 +155,74 @@ async function initializeWorkspace(
logger.info(' npm install');
}
-function generatePackageJson(): object {
+function normalizeTemplate(value?: string): StarterTemplate {
+ const candidate = (value || 'node-library') as StarterTemplate;
+ if (SUPPORTED_TEMPLATES.includes(candidate)) {
+ return candidate;
+ }
+ throw new Error(
+ `Unsupported template "${value}". Valid templates: ${SUPPORTED_TEMPLATES.join(', ')}`
+ );
+}
+
+function generatePackageJson(template: StarterTemplate): object {
+ if (template === 'bun-app') {
+ return {
+ name: 'github-actions-bun-workspace',
+ version: '1.0.0',
+ description: 'DotGitHub Bun app workspace with TypeScript support',
+ type: 'module',
+ main: 'dist/index.js',
+ scripts: {
+ build: 'tsc',
+ dev: 'bun --watch src/index.ts',
+ clean: 'rm -rf dist',
+ },
+ dependencies: {
+ '@dotgithub/core': '*',
+ '@dotgithub/cli': '*',
+ },
+ devDependencies: {
+ '@types/node': '^20.0.0',
+ typescript: '^5.0.0',
+ },
+ engines: {
+ bun: '>=1.2.22',
+ },
+ packageManager: 'bun@1.2.22',
+ };
+ }
+
+ if (template === 'monorepo') {
+ return {
+ name: 'github-actions-monorepo-workspace',
+ version: '1.0.0',
+ private: true,
+ description: 'DotGitHub monorepo starter with TypeScript support',
+ type: 'module',
+ scripts: {
+ build: 'tsc',
+ clean: 'rm -rf dist',
+ },
+ workspaces: ['packages/*'],
+ dependencies: {
+ '@dotgithub/core': '*',
+ '@dotgithub/cli': '*',
+ },
+ devDependencies: {
+ '@types/node': '^20.0.0',
+ typescript: '^5.0.0',
+ },
+ engines: {
+ node: '>=18.0.0',
+ },
+ };
+ }
+
return {
name: 'github-actions-workspace',
version: '1.0.0',
- description: 'GitHub Actions workspace with TypeScript support',
+ description: 'DotGitHub Node library workspace with TypeScript support',
type: 'module',
main: 'dist/index.js',
module: 'dist/index.js',
@@ -173,12 +246,13 @@ function generatePackageJson(): object {
};
}
-function generateTsConfig(): object {
+function generateTsConfig(template: StarterTemplate): object {
+ const moduleResolution = template === 'bun-app' ? 'bundler' : 'bundler';
return {
compilerOptions: {
target: 'ES2022',
module: 'ESNext',
- moduleResolution: 'bundler',
+ moduleResolution,
allowSyntheticDefaultImports: true,
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
@@ -197,9 +271,20 @@ function generateTsConfig(): object {
};
}
-function generateIndexFile(): string {
- return `// GitHub Actions workspace entry point
-import {
+function generateIndexFile(template: StarterTemplate): string {
+ if (template === 'bun-app') {
+ return `// Bun-oriented DotGitHub starter entry point\n// Use this to author CI/CD workflow constructs in TypeScript and synthesize YAML.\n\n${baseIndexContent()}`;
+ }
+
+ if (template === 'monorepo') {
+ return `// Monorepo-oriented DotGitHub starter entry point\n// Tip: split constructs by package and export a composed default construct.\n\n${baseIndexContent()}`;
+ }
+
+ return `// Node library-oriented DotGitHub starter entry point\n// Author workflow logic in TypeScript and synthesize to GitHub Actions YAML.\n\n${baseIndexContent()}`;
+}
+
+function baseIndexContent(): string {
+ return `import {
GitHubConstruct,
GitHubStack,
JobConstruct,
@@ -279,7 +364,7 @@ export class MyConstruct extends GitHubConstruct {
});
// Create a job
- const job = new JobConstruct(workflow, 'test', {
+ new JobConstruct(workflow, 'test', {
name: 'Test',
'runs-on': 'ubuntu-latest',
steps: [