create-n8n 1.0.0 → 1.0.1

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 ADDED
@@ -0,0 +1,95 @@
1
+ <a id="readme-top"></a>
2
+
3
+ <div align="center">
4
+
5
+ # create-n8n
6
+
7
+ **The official scaffolding tool for modular n8n workflow projects.**
8
+
9
+ `create-n8n` generates a native, production-ready directory structure for building n8n workflows as code, powered by the flat8n compiler.
10
+
11
+ [![npm version](https://img.shields.io/npm/v/create-n8n.svg?style=for-the-badge&color=EA4B71)](https://www.npmjs.com/package/create-n8n)
12
+ [![n8n Compatible](https://img.shields.io/badge/n8n-Compatible-FF6E4A.svg?style=for-the-badge)](https://n8n.io)
13
+ [![flat8n Powered](https://img.shields.io/badge/flat8n-Powered-FF6E4A.svg?style=for-the-badge)](https://github.com/jvondev/flat8n)
14
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)
15
+
16
+ [Quick Start](#quick-start) &nbsp;•&nbsp;
17
+ [Why You Need This](#why-you-need-this) &nbsp;•&nbsp;
18
+ [Features](#features) &nbsp;•&nbsp;
19
+ [Architecture](#architecture)
20
+
21
+ </div>
22
+
23
+ ## Quick Start
24
+
25
+ Run the interactive CLI wizard in your terminal to scaffold a new project:
26
+
27
+ ```bash
28
+ npm create n8n@latest
29
+ ```
30
+
31
+ The CLI prompts you for a project name, generates the required directory structure, configures the environment variables, and initializes a Git repository.
32
+
33
+ Once scaffolded, boot the local development environment:
34
+
35
+ ```bash
36
+ cd your-project-name
37
+ npm install
38
+ npm run dev
39
+ ```
40
+
41
+ ## Why You Need This
42
+
43
+ Building n8n workflows through the browser UI is effective for small automations, but scales poorly for engineering teams. Version control becomes difficult, code reuse requires manual copy-pasting, and isolating environments introduces risk.
44
+
45
+ `create-n8n` bridges this gap by establishing a standard, code-first architecture. It integrates directly with `@flat8n/cli`, allowing you to write small, modular JSON workflows in your editor. The compiler then resolves cross-file dependencies and outputs a single, deterministic workflow ready for CI/CD deployment.
46
+
47
+ | Capability | Standard n8n Workspace | `create-n8n` Workspace |
48
+ |---|---|---|
49
+ | **Structure** | Unstructured exports. | Modular, domain-driven directories. |
50
+ | **Development** | Manual UI management. | Automated local dev server sync. |
51
+ | **Reusability** | Copy-paste nodes. | Shared sub-workflow modules. |
52
+ | **Security** | Embedded credentials. | `.env` isolated variables. |
53
+
54
+ ## Features
55
+
56
+ - **Zero-config Environment.** The CLI provides a fully working local n8n instance synchronized with your file system out of the box.
57
+ - **Modular Compilation.** Build complex architectures by separating logic into sub-workflows. The underlying compiler merges them into a single deployable asset.
58
+ - **Hot Reloading.** Edit workflow structures in your code editor or the local n8n UI, and changes synchronize instantly across both.
59
+ - **Deterministic Builds.** The compiler generates stable Node IDs and Webhook IDs, ensuring production endpoints remain stable across deployments.
60
+ - **Secure Defaults.** Scaffolding includes `.gitignore` rules and `.env.example` templates to prevent credential leakage.
61
+
62
+ ## Architecture
63
+
64
+ A project scaffolded with `create-n8n` uses a standard layout optimized for version control and modularity.
65
+
66
+ ### Directory Layout
67
+
68
+ ```text
69
+ my-n8n-app/
70
+ ├── workflows/
71
+ │ ├── main.json # The primary entrypoint workflow
72
+ │ └── subworkflows/
73
+ │ └── child.json # Reusable workflow modules
74
+ ├── .env.example # Environment variable templates
75
+ ├── .gitignore # Prevents committing secrets
76
+ ├── flat8n.config.json # Compiler configuration
77
+ └── package.json # Scripts (dev, build, start)
78
+ ```
79
+
80
+ ### Build Pipeline
81
+
82
+ The underlying compilation engine is the [`@flat8n/cli`](https://www.npmjs.com/package/@flat8n/cli) framework. When you execute `npm run build`, the compiler parses `main.json` and inlines all referenced `subworkflows` into a single deployable artifact located at `dist/main.json`.
83
+
84
+ > [!TIP]
85
+ > You can deploy the resulting `dist/main.json` to any production n8n instance without external dependencies. The output is a native, monolithic n8n workflow.
86
+
87
+ ## License
88
+
89
+ MIT © [JVON DEV](https://github.com/jvondev)
90
+
91
+ <div align="center">
92
+
93
+ <a href="#readme-top">↑ back to top</a>
94
+
95
+ </div>
@@ -0,0 +1,24 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { exec } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import { PACKAGE_JSON_TEMPLATE, GITIGNORE_TEMPLATE, ENV_TEMPLATE, CONFIG_TEMPLATE, README_TEMPLATE, MAIN_JSON_TEMPLATE, CHILD_JSON_TEMPLATE } from "./templates.js";
6
+ const execAsync = promisify(exec);
7
+ export async function generateProject(projectDir, projectName, initGit) {
8
+ // 1. Create Directories
9
+ await mkdir(projectDir, { recursive: true });
10
+ await mkdir(join(projectDir, "workflows"), { recursive: true });
11
+ await mkdir(join(projectDir, "workflows", "subworkflows"), { recursive: true });
12
+ // 2. Write Config / Root Files
13
+ await writeFile(join(projectDir, "package.json"), PACKAGE_JSON_TEMPLATE(projectName));
14
+ await writeFile(join(projectDir, ".gitignore"), GITIGNORE_TEMPLATE);
15
+ await writeFile(join(projectDir, ".env.example"), ENV_TEMPLATE);
16
+ await writeFile(join(projectDir, "flat8n.config.json"), CONFIG_TEMPLATE);
17
+ await writeFile(join(projectDir, "README.md"), README_TEMPLATE(projectName));
18
+ // 3. Write Workflows
19
+ await writeFile(join(projectDir, "workflows", "main.json"), MAIN_JSON_TEMPLATE);
20
+ await writeFile(join(projectDir, "workflows", "subworkflows", "child.json"), CHILD_JSON_TEMPLATE);
21
+ if (initGit) {
22
+ await execAsync("git init", { cwd: projectDir });
23
+ }
24
+ }
package/dist/index.js CHANGED
@@ -1,168 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { mkdir, writeFile, readdir } from "node:fs/promises";
3
- import { resolve, join, basename } from "node:path";
4
- import { exec } from "node:child_process";
5
- import { promisify } from "node:util";
2
+ import { readdir } from "node:fs/promises";
3
+ import { resolve, basename } from "node:path";
6
4
  import pc from "picocolors";
7
5
  import { intro, outro, text, confirm, spinner, log, isCancel, cancel } from "@clack/prompts";
8
- const execAsync = promisify(exec);
9
- const ASCII_ART = [
10
- " ██████╗ █████╗ ██████╗ ",
11
- " ██╔══██╗██╔══██╗██╔══██╗",
12
- " ██║ ██║╚█████╔╝██║ ██║",
13
- " ██║ ██║██╔══██╗██║ ██║",
14
- " ██║ ██║╚█████╔╝██║ ██║",
15
- " ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝"
16
- ];
17
- function showBranding() {
18
- process.stdout.write("\n");
19
- const color = "\x1b[38;2;234;75;113m"; // #EA4B71
20
- const reset = "\x1b[0m";
21
- process.stdout.write(`${color}${ASCII_ART.join('\n')}${reset}\n\n`);
22
- }
23
- const PACKAGE_JSON_TEMPLATE = (name) => `{
24
- "name": "${name}",
25
- "version": "1.0.0",
26
- "description": "An n8n workflow project powered by flat8n",
27
- "scripts": {
28
- "dev": "flat8n",
29
- "build": "flat8n link --entry workflows/main.json --out dist/main.json",
30
- "start": "npx n8n start"
31
- },
32
- "dependencies": {},
33
- "devDependencies": {
34
- "@flat8n/cli": "latest"
35
- }
36
- }
37
- `;
38
- const GITIGNORE_TEMPLATE = `node_modules/
39
- dist/
40
- .env
41
- .n8n/
42
- `;
43
- const ENV_TEMPLATE = `# The port your local n8n dev server runs on
44
- N8N_PORT=5678
45
-
46
- # The host your local n8n dev server binds to
47
- N8N_HOST=localhost
48
-
49
- # Security key for n8n encryption (change this in production)
50
- N8N_ENCRYPTION_KEY=my-super-secret-encryption-key
51
- `;
52
- const CONFIG_TEMPLATE = `{
53
- "$schema": "https://flat8n.com/schema.json",
54
- "workflowsDir": "workflows",
55
- "outDir": "dist"
56
- }
57
- `;
58
- const README_TEMPLATE = (name) => `# ${name}
59
-
60
- [![n8n](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge&logo=n8n&logoColor=white)](https://n8n.io)
61
- [![flat8n](https://img.shields.io/badge/flat8n-FF6E4A.svg)](https://github.com/jvondev/flat8n)
62
-
63
- A modern n8n workflow project, scaffolded with \`create-n8n\`.
64
-
65
- ## Development
66
-
67
- Run the dev server (automatically starts a local n8n instance and watches your files for changes):
68
-
69
- \`\`\`bash
70
- npm run dev
71
- \`\`\`
72
-
73
- ## Build
74
-
75
- Compile your modular workflows into a single deployable monolithic JSON workflow:
76
-
77
- \`\`\`bash
78
- npm run build
79
- \`\`\`
80
-
81
- ## Architecture
82
-
83
- Your source workflows are in the \`workflows/\` directory.
84
- Shared or reusable logic goes in \`workflows/subworkflows/\`.
85
- `;
86
- const MAIN_JSON_TEMPLATE = `{
87
- "name": "Main Entrypoint",
88
- "nodes": [
89
- {
90
- "parameters": {
91
- "httpMethod": "POST",
92
- "path": "test",
93
- "options": {}
94
- },
95
- "id": "1",
96
- "name": "Webhook",
97
- "type": "n8n-nodes-base.webhook",
98
- "typeVersion": 1,
99
- "position": [0, 0],
100
- "webhookId": "test-webhook"
101
- },
102
- {
103
- "parameters": {
104
- "workflowId": "child"
105
- },
106
- "id": "2",
107
- "name": "Execute Subworkflow",
108
- "type": "n8n-nodes-base.executeWorkflow",
109
- "typeVersion": 1,
110
- "position": [200, 0]
111
- }
112
- ],
113
- "connections": {
114
- "Webhook": {
115
- "main": [
116
- [
117
- {
118
- "node": "Execute Subworkflow",
119
- "type": "main",
120
- "index": 0
121
- }
122
- ]
123
- ]
124
- }
125
- }
126
- }
127
- `;
128
- const CHILD_JSON_TEMPLATE = `{
129
- "name": "Child Module",
130
- "id": "child",
131
- "nodes": [
132
- {
133
- "parameters": {},
134
- "id": "1",
135
- "name": "Execute Workflow Trigger",
136
- "type": "n8n-nodes-base.executeWorkflowTrigger",
137
- "typeVersion": 1,
138
- "position": [0, 0]
139
- },
140
- {
141
- "parameters": {
142
- "jsCode": "return $input.all();"
143
- },
144
- "id": "2",
145
- "name": "Process Logic",
146
- "type": "n8n-nodes-base.code",
147
- "typeVersion": 2,
148
- "position": [200, 0]
149
- }
150
- ],
151
- "connections": {
152
- "Execute Workflow Trigger": {
153
- "main": [
154
- [
155
- {
156
- "node": "Process Logic",
157
- "type": "main",
158
- "index": 0
159
- }
160
- ]
161
- ]
162
- }
163
- }
164
- }
165
- `;
6
+ import { showBranding } from "./ui.js";
7
+ import { generateProject } from "./generator.js";
166
8
  async function main() {
167
9
  console.clear();
168
10
  showBranding();
@@ -214,30 +56,8 @@ async function main() {
214
56
  const s = spinner();
215
57
  s.start(`Creating project in ${projectDir}...`);
216
58
  try {
217
- // 1. Create Directories
218
- await mkdir(projectDir, { recursive: true });
219
- await mkdir(join(projectDir, "workflows"), { recursive: true });
220
- await mkdir(join(projectDir, "workflows", "subworkflows"), { recursive: true });
221
- // 2. Write Config / Root Files
222
- await writeFile(join(projectDir, "package.json"), PACKAGE_JSON_TEMPLATE(projectName));
223
- await writeFile(join(projectDir, ".gitignore"), GITIGNORE_TEMPLATE);
224
- await writeFile(join(projectDir, ".env.example"), ENV_TEMPLATE);
225
- await writeFile(join(projectDir, "flat8n.config.json"), CONFIG_TEMPLATE);
226
- await writeFile(join(projectDir, "README.md"), README_TEMPLATE(projectName));
227
- // 3. Write Workflows
228
- await writeFile(join(projectDir, "workflows", "main.json"), MAIN_JSON_TEMPLATE);
229
- await writeFile(join(projectDir, "workflows", "subworkflows", "child.json"), CHILD_JSON_TEMPLATE);
59
+ await generateProject(projectDir, projectName, initGit);
230
60
  s.stop(pc.green(`✔ Project scaffolded successfully in ${projectDir}`));
231
- if (initGit) {
232
- s.start("Initializing Git repository...");
233
- try {
234
- await execAsync("git init", { cwd: projectDir });
235
- s.stop(pc.green("✔ Git repository initialized"));
236
- }
237
- catch (err) {
238
- s.stop(pc.red("✖ Failed to initialize Git repository"));
239
- }
240
- }
241
61
  const cdInstruction = targetDir === "." ? "" : ` cd ${targetDir}\n`;
242
62
  log.info(`\n${pc.bold("Next steps:")}\n${cdInstruction} npm install\n npm run dev`);
243
63
  outro(pc.cyan("Happy automating! ") + pc.dim("— scaffolded by \x1b]8;;https://github.com/jvondev\x1b\\JVON DEV\x1b]8;;\x1b\\"));
@@ -0,0 +1,24 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { exec } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import { PACKAGE_JSON_TEMPLATE, GITIGNORE_TEMPLATE, ENV_TEMPLATE, CONFIG_TEMPLATE, README_TEMPLATE, MAIN_JSON_TEMPLATE, CHILD_JSON_TEMPLATE } from "./templates.js";
6
+ const execAsync = promisify(exec);
7
+ export async function generateProject(projectDir, projectName, initGit) {
8
+ // 1. Create Directories
9
+ await mkdir(projectDir, { recursive: true });
10
+ await mkdir(join(projectDir, "workflows"), { recursive: true });
11
+ await mkdir(join(projectDir, "workflows", "subworkflows"), { recursive: true });
12
+ // 2. Write Config / Root Files
13
+ await writeFile(join(projectDir, "package.json"), PACKAGE_JSON_TEMPLATE(projectName));
14
+ await writeFile(join(projectDir, ".gitignore"), GITIGNORE_TEMPLATE);
15
+ await writeFile(join(projectDir, ".env.example"), ENV_TEMPLATE);
16
+ await writeFile(join(projectDir, "flat8n.config.json"), CONFIG_TEMPLATE);
17
+ await writeFile(join(projectDir, "README.md"), README_TEMPLATE(projectName));
18
+ // 3. Write Workflows
19
+ await writeFile(join(projectDir, "workflows", "main.json"), MAIN_JSON_TEMPLATE);
20
+ await writeFile(join(projectDir, "workflows", "subworkflows", "child.json"), CHILD_JSON_TEMPLATE);
21
+ if (initGit) {
22
+ await execAsync("git init", { cwd: projectDir });
23
+ }
24
+ }
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { readdir } from "node:fs/promises";
3
+ import { resolve, basename } from "node:path";
4
+ import pc from "picocolors";
5
+ import { intro, outro, text, confirm, spinner, log, isCancel, cancel } from "@clack/prompts";
6
+ import { showBranding } from "./ui.js";
7
+ import { generateProject } from "./generator.js";
8
+ async function main() {
9
+ console.clear();
10
+ showBranding();
11
+ intro(pc.bgCyan(pc.black(" create-n8n ")));
12
+ const cwd = process.cwd();
13
+ const cwdFiles = await readdir(cwd);
14
+ const allowedFiles = new Set([".git", ".DS_Store", ".vscode", ".idea", "Thumbs.db"]);
15
+ const isCwdEmpty = cwdFiles.every((f) => allowedFiles.has(f));
16
+ let targetDir = ".";
17
+ if (!isCwdEmpty) {
18
+ const dirPrompt = await text({
19
+ message: "Project name (creates a new folder, or '.' for current directory)",
20
+ placeholder: "my-n8n-app",
21
+ defaultValue: "my-n8n-app",
22
+ validate: (val) => {
23
+ if (!val || val.length === 0)
24
+ return "Project name is required";
25
+ if (!/^[a-z0-9-_./]+$/.test(val))
26
+ return "Invalid path format";
27
+ return;
28
+ }
29
+ });
30
+ if (isCancel(dirPrompt) || typeof dirPrompt !== "string") {
31
+ cancel("Initialization cancelled.");
32
+ return process.exit(1);
33
+ }
34
+ targetDir = dirPrompt;
35
+ if (targetDir === ".") {
36
+ const confirmOverwrite = await confirm({
37
+ message: "Current directory is not empty. Continue and potentially overwrite files?",
38
+ initialValue: false
39
+ });
40
+ if (isCancel(confirmOverwrite) || !confirmOverwrite) {
41
+ cancel("Initialization cancelled.");
42
+ return process.exit(1);
43
+ }
44
+ }
45
+ }
46
+ const projectDir = resolve(cwd, targetDir);
47
+ const projectName = targetDir === "." ? basename(cwd) : basename(projectDir);
48
+ const initGit = await confirm({
49
+ message: "Initialize a new Git repository?",
50
+ initialValue: true
51
+ });
52
+ if (isCancel(initGit)) {
53
+ cancel("Initialization cancelled.");
54
+ return process.exit(1);
55
+ }
56
+ const s = spinner();
57
+ s.start(`Creating project in ${projectDir}...`);
58
+ try {
59
+ await generateProject(projectDir, projectName, initGit);
60
+ s.stop(pc.green(`✔ Project scaffolded successfully in ${projectDir}`));
61
+ const cdInstruction = targetDir === "." ? "" : ` cd ${targetDir}\n`;
62
+ log.info(`\n${pc.bold("Next steps:")}\n${cdInstruction} npm install\n npm run dev`);
63
+ outro(pc.cyan("Happy automating! ") + pc.dim("— scaffolded by \x1b]8;;https://github.com/jvondev\x1b\\JVON DEV\x1b]8;;\x1b\\"));
64
+ process.exit(0);
65
+ }
66
+ catch (error) {
67
+ s.stop(pc.red("✖ Failed to create project"));
68
+ log.error(error.message);
69
+ process.exit(1);
70
+ }
71
+ }
72
+ main().catch(console.error);
@@ -0,0 +1,143 @@
1
+ export const PACKAGE_JSON_TEMPLATE = (name) => `{
2
+ "name": "${name}",
3
+ "version": "1.0.0",
4
+ "description": "An n8n workflow project powered by flat8n",
5
+ "scripts": {
6
+ "dev": "flat8n",
7
+ "build": "flat8n link --entry workflows/main.json --out dist/main.json",
8
+ "start": "npx n8n start"
9
+ },
10
+ "dependencies": {},
11
+ "devDependencies": {
12
+ "@flat8n/cli": "latest"
13
+ }
14
+ }
15
+ `;
16
+ export const GITIGNORE_TEMPLATE = `node_modules/
17
+ dist/
18
+ .env
19
+ .n8n/
20
+ `;
21
+ export const ENV_TEMPLATE = `# The port your local n8n dev server runs on
22
+ N8N_PORT=5678
23
+
24
+ # The host your local n8n dev server binds to
25
+ N8N_HOST=localhost
26
+
27
+ # Security key for n8n encryption (change this in production)
28
+ N8N_ENCRYPTION_KEY=my-super-secret-encryption-key
29
+ `;
30
+ export const CONFIG_TEMPLATE = `{
31
+ "$schema": "https://flat8n.com/schema.json",
32
+ "workflowsDir": "workflows",
33
+ "outDir": "dist"
34
+ }
35
+ `;
36
+ export const README_TEMPLATE = (name) => `# ${name}
37
+
38
+ [![n8n](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge&logo=n8n&logoColor=white)](https://n8n.io)
39
+ [![flat8n](https://img.shields.io/badge/flat8n-FF6E4A.svg)](https://github.com/jvondev/flat8n)
40
+
41
+ A modern n8n workflow project, scaffolded with \`create-n8n\`.
42
+
43
+ ## Development
44
+
45
+ Run the dev server (automatically starts a local n8n instance and watches your files for changes):
46
+
47
+ \`\`\`bash
48
+ npm run dev
49
+ \`\`\`
50
+
51
+ ## Build
52
+
53
+ Compile your modular workflows into a single deployable monolithic JSON workflow:
54
+
55
+ \`\`\`bash
56
+ npm run build
57
+ \`\`\`
58
+
59
+ ## Architecture
60
+
61
+ Your source workflows are in the \`workflows/\` directory.
62
+ Shared or reusable logic goes in \`workflows/subworkflows/\`.
63
+ `;
64
+ export const MAIN_JSON_TEMPLATE = `{
65
+ "name": "Main Entrypoint",
66
+ "nodes": [
67
+ {
68
+ "parameters": {
69
+ "httpMethod": "POST",
70
+ "path": "test",
71
+ "options": {}
72
+ },
73
+ "id": "1",
74
+ "name": "Webhook",
75
+ "type": "n8n-nodes-base.webhook",
76
+ "typeVersion": 1,
77
+ "position": [0, 0],
78
+ "webhookId": "test-webhook"
79
+ },
80
+ {
81
+ "parameters": {
82
+ "workflowId": "child"
83
+ },
84
+ "id": "2",
85
+ "name": "Execute Subworkflow",
86
+ "type": "n8n-nodes-base.executeWorkflow",
87
+ "typeVersion": 1,
88
+ "position": [200, 0]
89
+ }
90
+ ],
91
+ "connections": {
92
+ "Webhook": {
93
+ "main": [
94
+ [
95
+ {
96
+ "node": "Execute Subworkflow",
97
+ "type": "main",
98
+ "index": 0
99
+ }
100
+ ]
101
+ ]
102
+ }
103
+ }
104
+ }
105
+ `;
106
+ export const CHILD_JSON_TEMPLATE = `{
107
+ "name": "Child Module",
108
+ "id": "child",
109
+ "nodes": [
110
+ {
111
+ "parameters": {},
112
+ "id": "1",
113
+ "name": "Execute Workflow Trigger",
114
+ "type": "n8n-nodes-base.executeWorkflowTrigger",
115
+ "typeVersion": 1,
116
+ "position": [0, 0]
117
+ },
118
+ {
119
+ "parameters": {
120
+ "jsCode": "return $input.all();"
121
+ },
122
+ "id": "2",
123
+ "name": "Process Logic",
124
+ "type": "n8n-nodes-base.code",
125
+ "typeVersion": 2,
126
+ "position": [200, 0]
127
+ }
128
+ ],
129
+ "connections": {
130
+ "Execute Workflow Trigger": {
131
+ "main": [
132
+ [
133
+ {
134
+ "node": "Process Logic",
135
+ "type": "main",
136
+ "index": 0
137
+ }
138
+ ]
139
+ ]
140
+ }
141
+ }
142
+ }
143
+ `;
package/dist/src/ui.js ADDED
@@ -0,0 +1,14 @@
1
+ export const ASCII_ART = [
2
+ " ██████╗ █████╗ ██████╗ ",
3
+ " ██╔══██╗██╔══██╗██╔══██╗",
4
+ " ██║ ██║╚█████╔╝██║ ██║",
5
+ " ██║ ██║██╔══██╗██║ ██║",
6
+ " ██║ ██║╚█████╔╝██║ ██║",
7
+ " ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝"
8
+ ];
9
+ export function showBranding() {
10
+ process.stdout.write("\n");
11
+ const color = "\x1b[38;2;234;75;113m"; // #EA4B71
12
+ const reset = "\x1b[0m";
13
+ process.stdout.write(`${color}${ASCII_ART.join('\n')}${reset}\n\n`);
14
+ }
@@ -0,0 +1,143 @@
1
+ export const PACKAGE_JSON_TEMPLATE = (name) => `{
2
+ "name": "${name}",
3
+ "version": "1.0.0",
4
+ "description": "An n8n workflow project powered by flat8n",
5
+ "scripts": {
6
+ "dev": "flat8n",
7
+ "build": "flat8n link --entry workflows/main.json --out dist/main.json",
8
+ "start": "npx n8n start"
9
+ },
10
+ "dependencies": {},
11
+ "devDependencies": {
12
+ "@flat8n/cli": "latest"
13
+ }
14
+ }
15
+ `;
16
+ export const GITIGNORE_TEMPLATE = `node_modules/
17
+ dist/
18
+ .env
19
+ .n8n/
20
+ `;
21
+ export const ENV_TEMPLATE = `# The port your local n8n dev server runs on
22
+ N8N_PORT=5678
23
+
24
+ # The host your local n8n dev server binds to
25
+ N8N_HOST=localhost
26
+
27
+ # Security key for n8n encryption (change this in production)
28
+ N8N_ENCRYPTION_KEY=my-super-secret-encryption-key
29
+ `;
30
+ export const CONFIG_TEMPLATE = `{
31
+ "$schema": "https://flat8n.com/schema.json",
32
+ "workflowsDir": "workflows",
33
+ "outDir": "dist"
34
+ }
35
+ `;
36
+ export const README_TEMPLATE = (name) => `# ${name}
37
+
38
+ [![n8n](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge&logo=n8n&logoColor=white)](https://n8n.io)
39
+ [![flat8n](https://img.shields.io/badge/flat8n-FF6E4A.svg)](https://github.com/jvondev/flat8n)
40
+
41
+ A modern n8n workflow project, scaffolded with \`create-n8n\`.
42
+
43
+ ## Development
44
+
45
+ Run the dev server (automatically starts a local n8n instance and watches your files for changes):
46
+
47
+ \`\`\`bash
48
+ npm run dev
49
+ \`\`\`
50
+
51
+ ## Build
52
+
53
+ Compile your modular workflows into a single deployable monolithic JSON workflow:
54
+
55
+ \`\`\`bash
56
+ npm run build
57
+ \`\`\`
58
+
59
+ ## Architecture
60
+
61
+ Your source workflows are in the \`workflows/\` directory.
62
+ Shared or reusable logic goes in \`workflows/subworkflows/\`.
63
+ `;
64
+ export const MAIN_JSON_TEMPLATE = `{
65
+ "name": "Main Entrypoint",
66
+ "nodes": [
67
+ {
68
+ "parameters": {
69
+ "httpMethod": "POST",
70
+ "path": "test",
71
+ "options": {}
72
+ },
73
+ "id": "1",
74
+ "name": "Webhook",
75
+ "type": "n8n-nodes-base.webhook",
76
+ "typeVersion": 1,
77
+ "position": [0, 0],
78
+ "webhookId": "test-webhook"
79
+ },
80
+ {
81
+ "parameters": {
82
+ "workflowId": "child"
83
+ },
84
+ "id": "2",
85
+ "name": "Execute Subworkflow",
86
+ "type": "n8n-nodes-base.executeWorkflow",
87
+ "typeVersion": 1,
88
+ "position": [200, 0]
89
+ }
90
+ ],
91
+ "connections": {
92
+ "Webhook": {
93
+ "main": [
94
+ [
95
+ {
96
+ "node": "Execute Subworkflow",
97
+ "type": "main",
98
+ "index": 0
99
+ }
100
+ ]
101
+ ]
102
+ }
103
+ }
104
+ }
105
+ `;
106
+ export const CHILD_JSON_TEMPLATE = `{
107
+ "name": "Child Module",
108
+ "id": "child",
109
+ "nodes": [
110
+ {
111
+ "parameters": {},
112
+ "id": "1",
113
+ "name": "Execute Workflow Trigger",
114
+ "type": "n8n-nodes-base.executeWorkflowTrigger",
115
+ "typeVersion": 1,
116
+ "position": [0, 0]
117
+ },
118
+ {
119
+ "parameters": {
120
+ "jsCode": "return $input.all();"
121
+ },
122
+ "id": "2",
123
+ "name": "Process Logic",
124
+ "type": "n8n-nodes-base.code",
125
+ "typeVersion": 2,
126
+ "position": [200, 0]
127
+ }
128
+ ],
129
+ "connections": {
130
+ "Execute Workflow Trigger": {
131
+ "main": [
132
+ [
133
+ {
134
+ "node": "Process Logic",
135
+ "type": "main",
136
+ "index": 0
137
+ }
138
+ ]
139
+ ]
140
+ }
141
+ }
142
+ }
143
+ `;
package/dist/ui.js ADDED
@@ -0,0 +1,14 @@
1
+ export const ASCII_ART = [
2
+ " ██████╗ █████╗ ██████╗ ",
3
+ " ██╔══██╗██╔══██╗██╔══██╗",
4
+ " ██║ ██║╚█████╔╝██║ ██║",
5
+ " ██║ ██║██╔══██╗██║ ██║",
6
+ " ██║ ██║╚█████╔╝██║ ██║",
7
+ " ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝"
8
+ ];
9
+ export function showBranding() {
10
+ process.stdout.write("\n");
11
+ const color = "\x1b[38;2;234;75;113m"; // #EA4B71
12
+ const reset = "\x1b[0m";
13
+ process.stdout.write(`${color}${ASCII_ART.join('\n')}${reset}\n\n`);
14
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-n8n",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Scaffolding tool for n8n workflow projects",
5
5
  "main": "./dist/index.js",
6
6
  "author": "JVON DEV <https://github.com/jvondev>",
@@ -0,0 +1,37 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { exec } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import {
6
+ PACKAGE_JSON_TEMPLATE,
7
+ GITIGNORE_TEMPLATE,
8
+ ENV_TEMPLATE,
9
+ CONFIG_TEMPLATE,
10
+ README_TEMPLATE,
11
+ MAIN_JSON_TEMPLATE,
12
+ CHILD_JSON_TEMPLATE
13
+ } from "./templates.js";
14
+
15
+ const execAsync = promisify(exec);
16
+
17
+ export async function generateProject(projectDir: string, projectName: string, initGit: boolean): Promise<void> {
18
+ // 1. Create Directories
19
+ await mkdir(projectDir, { recursive: true });
20
+ await mkdir(join(projectDir, "workflows"), { recursive: true });
21
+ await mkdir(join(projectDir, "workflows", "subworkflows"), { recursive: true });
22
+
23
+ // 2. Write Config / Root Files
24
+ await writeFile(join(projectDir, "package.json"), PACKAGE_JSON_TEMPLATE(projectName));
25
+ await writeFile(join(projectDir, ".gitignore"), GITIGNORE_TEMPLATE);
26
+ await writeFile(join(projectDir, ".env.example"), ENV_TEMPLATE);
27
+ await writeFile(join(projectDir, "flat8n.config.json"), CONFIG_TEMPLATE);
28
+ await writeFile(join(projectDir, "README.md"), README_TEMPLATE(projectName));
29
+
30
+ // 3. Write Workflows
31
+ await writeFile(join(projectDir, "workflows", "main.json"), MAIN_JSON_TEMPLATE);
32
+ await writeFile(join(projectDir, "workflows", "subworkflows", "child.json"), CHILD_JSON_TEMPLATE);
33
+
34
+ if (initGit) {
35
+ await execAsync("git init", { cwd: projectDir });
36
+ }
37
+ }
package/src/index.ts ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ import { readdir } from "node:fs/promises";
3
+ import { resolve, basename } from "node:path";
4
+ import pc from "picocolors";
5
+ import { intro, outro, text, confirm, spinner, log, isCancel, cancel } from "@clack/prompts";
6
+ import { showBranding } from "./ui.js";
7
+ import { generateProject } from "./generator.js";
8
+
9
+ async function main() {
10
+ console.clear();
11
+ showBranding();
12
+ intro(pc.bgCyan(pc.black(" create-n8n ")));
13
+
14
+ const cwd = process.cwd();
15
+
16
+ const cwdFiles = await readdir(cwd);
17
+ const allowedFiles = new Set([".git", ".DS_Store", ".vscode", ".idea", "Thumbs.db"]);
18
+ const isCwdEmpty = cwdFiles.every((f: string) => allowedFiles.has(f));
19
+
20
+ let targetDir = ".";
21
+
22
+ if (!isCwdEmpty) {
23
+ const dirPrompt = await text({
24
+ message: "Project name (creates a new folder, or '.' for current directory)",
25
+ placeholder: "my-n8n-app",
26
+ defaultValue: "my-n8n-app",
27
+ validate: (val) => {
28
+ if (!val || val.length === 0) return "Project name is required";
29
+ if (!/^[a-z0-9-_./]+$/.test(val)) return "Invalid path format";
30
+ return;
31
+ }
32
+ });
33
+
34
+ if (isCancel(dirPrompt) || typeof dirPrompt !== "string") {
35
+ cancel("Initialization cancelled.");
36
+ return process.exit(1);
37
+ }
38
+ targetDir = dirPrompt;
39
+
40
+ if (targetDir === ".") {
41
+ const confirmOverwrite = await confirm({
42
+ message: "Current directory is not empty. Continue and potentially overwrite files?",
43
+ initialValue: false
44
+ });
45
+ if (isCancel(confirmOverwrite) || !confirmOverwrite) {
46
+ cancel("Initialization cancelled.");
47
+ return process.exit(1);
48
+ }
49
+ }
50
+ }
51
+
52
+ const projectDir = resolve(cwd, targetDir);
53
+ const projectName = targetDir === "." ? basename(cwd) : basename(projectDir);
54
+
55
+ const initGit = await confirm({
56
+ message: "Initialize a new Git repository?",
57
+ initialValue: true
58
+ });
59
+
60
+ if (isCancel(initGit)) {
61
+ cancel("Initialization cancelled.");
62
+ return process.exit(1);
63
+ }
64
+
65
+ const s = spinner();
66
+ s.start(`Creating project in ${projectDir}...`);
67
+
68
+ try {
69
+ await generateProject(projectDir, projectName, initGit as boolean);
70
+ s.stop(pc.green(`✔ Project scaffolded successfully in ${projectDir}`));
71
+
72
+ const cdInstruction = targetDir === "." ? "" : ` cd ${targetDir}\n`;
73
+ log.info(`\n${pc.bold("Next steps:")}\n${cdInstruction} npm install\n npm run dev`);
74
+ outro(pc.cyan("Happy automating! ") + pc.dim("— scaffolded by \x1b]8;;https://github.com/jvondev\x1b\\JVON DEV\x1b]8;;\x1b\\"));
75
+ process.exit(0);
76
+
77
+ } catch (error: any) {
78
+ s.stop(pc.red("✖ Failed to create project"));
79
+ log.error(error.message);
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ main().catch(console.error);
@@ -0,0 +1,149 @@
1
+ export const PACKAGE_JSON_TEMPLATE = (name: string) => `{
2
+ "name": "${name}",
3
+ "version": "1.0.0",
4
+ "description": "An n8n workflow project powered by flat8n",
5
+ "scripts": {
6
+ "dev": "flat8n",
7
+ "build": "flat8n link --entry workflows/main.json --out dist/main.json",
8
+ "start": "npx n8n start"
9
+ },
10
+ "dependencies": {},
11
+ "devDependencies": {
12
+ "@flat8n/cli": "latest"
13
+ }
14
+ }
15
+ `;
16
+
17
+ export const GITIGNORE_TEMPLATE = `node_modules/
18
+ dist/
19
+ .env
20
+ .n8n/
21
+ `;
22
+
23
+ export const ENV_TEMPLATE = `# The port your local n8n dev server runs on
24
+ N8N_PORT=5678
25
+
26
+ # The host your local n8n dev server binds to
27
+ N8N_HOST=localhost
28
+
29
+ # Security key for n8n encryption (change this in production)
30
+ N8N_ENCRYPTION_KEY=my-super-secret-encryption-key
31
+ `;
32
+
33
+ export const CONFIG_TEMPLATE = `{
34
+ "$schema": "https://flat8n.com/schema.json",
35
+ "workflowsDir": "workflows",
36
+ "outDir": "dist"
37
+ }
38
+ `;
39
+
40
+ export const README_TEMPLATE = (name: string) => `# ${name}
41
+
42
+ [![n8n](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge&logo=n8n&logoColor=white)](https://n8n.io)
43
+ [![flat8n](https://img.shields.io/badge/flat8n-FF6E4A.svg)](https://github.com/jvondev/flat8n)
44
+
45
+ A modern n8n workflow project, scaffolded with \`create-n8n\`.
46
+
47
+ ## Development
48
+
49
+ Run the dev server (automatically starts a local n8n instance and watches your files for changes):
50
+
51
+ \`\`\`bash
52
+ npm run dev
53
+ \`\`\`
54
+
55
+ ## Build
56
+
57
+ Compile your modular workflows into a single deployable monolithic JSON workflow:
58
+
59
+ \`\`\`bash
60
+ npm run build
61
+ \`\`\`
62
+
63
+ ## Architecture
64
+
65
+ Your source workflows are in the \`workflows/\` directory.
66
+ Shared or reusable logic goes in \`workflows/subworkflows/\`.
67
+ `;
68
+
69
+ export const MAIN_JSON_TEMPLATE = `{
70
+ "name": "Main Entrypoint",
71
+ "nodes": [
72
+ {
73
+ "parameters": {
74
+ "httpMethod": "POST",
75
+ "path": "test",
76
+ "options": {}
77
+ },
78
+ "id": "1",
79
+ "name": "Webhook",
80
+ "type": "n8n-nodes-base.webhook",
81
+ "typeVersion": 1,
82
+ "position": [0, 0],
83
+ "webhookId": "test-webhook"
84
+ },
85
+ {
86
+ "parameters": {
87
+ "workflowId": "child"
88
+ },
89
+ "id": "2",
90
+ "name": "Execute Subworkflow",
91
+ "type": "n8n-nodes-base.executeWorkflow",
92
+ "typeVersion": 1,
93
+ "position": [200, 0]
94
+ }
95
+ ],
96
+ "connections": {
97
+ "Webhook": {
98
+ "main": [
99
+ [
100
+ {
101
+ "node": "Execute Subworkflow",
102
+ "type": "main",
103
+ "index": 0
104
+ }
105
+ ]
106
+ ]
107
+ }
108
+ }
109
+ }
110
+ `;
111
+
112
+ export const CHILD_JSON_TEMPLATE = `{
113
+ "name": "Child Module",
114
+ "id": "child",
115
+ "nodes": [
116
+ {
117
+ "parameters": {},
118
+ "id": "1",
119
+ "name": "Execute Workflow Trigger",
120
+ "type": "n8n-nodes-base.executeWorkflowTrigger",
121
+ "typeVersion": 1,
122
+ "position": [0, 0]
123
+ },
124
+ {
125
+ "parameters": {
126
+ "jsCode": "return $input.all();"
127
+ },
128
+ "id": "2",
129
+ "name": "Process Logic",
130
+ "type": "n8n-nodes-base.code",
131
+ "typeVersion": 2,
132
+ "position": [200, 0]
133
+ }
134
+ ],
135
+ "connections": {
136
+ "Execute Workflow Trigger": {
137
+ "main": [
138
+ [
139
+ {
140
+ "node": "Process Logic",
141
+ "type": "main",
142
+ "index": 0
143
+ }
144
+ ]
145
+ ]
146
+ }
147
+ }
148
+ }
149
+ `;
package/src/ui.ts ADDED
@@ -0,0 +1,15 @@
1
+ export const ASCII_ART = [
2
+ " ██████╗ █████╗ ██████╗ ",
3
+ " ██╔══██╗██╔══██╗██╔══██╗",
4
+ " ██║ ██║╚█████╔╝██║ ██║",
5
+ " ██║ ██║██╔══██╗██║ ██║",
6
+ " ██║ ██║╚█████╔╝██║ ██║",
7
+ " ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝"
8
+ ];
9
+
10
+ export function showBranding(): void {
11
+ process.stdout.write("\n");
12
+ const color = "\x1b[38;2;234;75;113m"; // #EA4B71
13
+ const reset = "\x1b[0m";
14
+ process.stdout.write(`${color}${ASCII_ART.join('\n')}${reset}\n\n`);
15
+ }
package/tsconfig.json CHANGED
@@ -8,7 +8,8 @@
8
8
  "strict": true,
9
9
  "skipLibCheck": true,
10
10
  "outDir": "./dist",
11
+ "rootDir": "./src",
11
12
  "types": ["node"]
12
13
  },
13
- "include": ["index.ts"]
14
+ "include": ["src/**/*"]
14
15
  }
package/index.ts DELETED
@@ -1,277 +0,0 @@
1
- #!/usr/bin/env node
2
- import { mkdir, writeFile, readdir } from "node:fs/promises";
3
- import { resolve, join, basename } from "node:path";
4
- import { exec } from "node:child_process";
5
- import { promisify } from "node:util";
6
- import pc from "picocolors";
7
- import { intro, outro, text, confirm, spinner, log, isCancel, cancel } from "@clack/prompts";
8
-
9
- const execAsync = promisify(exec);
10
-
11
- const ASCII_ART = [
12
- " ██████╗ █████╗ ██████╗ ",
13
- " ██╔══██╗██╔══██╗██╔══██╗",
14
- " ██║ ██║╚█████╔╝██║ ██║",
15
- " ██║ ██║██╔══██╗██║ ██║",
16
- " ██║ ██║╚█████╔╝██║ ██║",
17
- " ╚═╝ ╚═╝ ╚════╝ ╚═╝ ╚═╝"
18
- ];
19
-
20
- function showBranding(): void {
21
- process.stdout.write("\n");
22
- const color = "\x1b[38;2;234;75;113m"; // #EA4B71
23
- const reset = "\x1b[0m";
24
- process.stdout.write(`${color}${ASCII_ART.join('\n')}${reset}\n\n`);
25
- }
26
-
27
- const PACKAGE_JSON_TEMPLATE = (name: string) => `{
28
- "name": "${name}",
29
- "version": "1.0.0",
30
- "description": "An n8n workflow project powered by flat8n",
31
- "scripts": {
32
- "dev": "flat8n",
33
- "build": "flat8n link --entry workflows/main.json --out dist/main.json",
34
- "start": "npx n8n start"
35
- },
36
- "dependencies": {},
37
- "devDependencies": {
38
- "@flat8n/cli": "latest"
39
- }
40
- }
41
- `;
42
-
43
- const GITIGNORE_TEMPLATE = `node_modules/
44
- dist/
45
- .env
46
- .n8n/
47
- `;
48
-
49
- const ENV_TEMPLATE = `# The port your local n8n dev server runs on
50
- N8N_PORT=5678
51
-
52
- # The host your local n8n dev server binds to
53
- N8N_HOST=localhost
54
-
55
- # Security key for n8n encryption (change this in production)
56
- N8N_ENCRYPTION_KEY=my-super-secret-encryption-key
57
- `;
58
-
59
- const CONFIG_TEMPLATE = `{
60
- "$schema": "https://flat8n.com/schema.json",
61
- "workflowsDir": "workflows",
62
- "outDir": "dist"
63
- }
64
- `;
65
-
66
- const README_TEMPLATE = (name: string) => `# ${name}
67
-
68
- [![n8n](https://img.shields.io/badge/n8n-EA4B71?style=for-the-badge&logo=n8n&logoColor=white)](https://n8n.io)
69
- [![flat8n](https://img.shields.io/badge/flat8n-FF6E4A.svg)](https://github.com/jvondev/flat8n)
70
-
71
- A modern n8n workflow project, scaffolded with \`create-n8n\`.
72
-
73
- ## Development
74
-
75
- Run the dev server (automatically starts a local n8n instance and watches your files for changes):
76
-
77
- \`\`\`bash
78
- npm run dev
79
- \`\`\`
80
-
81
- ## Build
82
-
83
- Compile your modular workflows into a single deployable monolithic JSON workflow:
84
-
85
- \`\`\`bash
86
- npm run build
87
- \`\`\`
88
-
89
- ## Architecture
90
-
91
- Your source workflows are in the \`workflows/\` directory.
92
- Shared or reusable logic goes in \`workflows/subworkflows/\`.
93
- `;
94
-
95
- const MAIN_JSON_TEMPLATE = `{
96
- "name": "Main Entrypoint",
97
- "nodes": [
98
- {
99
- "parameters": {
100
- "httpMethod": "POST",
101
- "path": "test",
102
- "options": {}
103
- },
104
- "id": "1",
105
- "name": "Webhook",
106
- "type": "n8n-nodes-base.webhook",
107
- "typeVersion": 1,
108
- "position": [0, 0],
109
- "webhookId": "test-webhook"
110
- },
111
- {
112
- "parameters": {
113
- "workflowId": "child"
114
- },
115
- "id": "2",
116
- "name": "Execute Subworkflow",
117
- "type": "n8n-nodes-base.executeWorkflow",
118
- "typeVersion": 1,
119
- "position": [200, 0]
120
- }
121
- ],
122
- "connections": {
123
- "Webhook": {
124
- "main": [
125
- [
126
- {
127
- "node": "Execute Subworkflow",
128
- "type": "main",
129
- "index": 0
130
- }
131
- ]
132
- ]
133
- }
134
- }
135
- }
136
- `;
137
-
138
- const CHILD_JSON_TEMPLATE = `{
139
- "name": "Child Module",
140
- "id": "child",
141
- "nodes": [
142
- {
143
- "parameters": {},
144
- "id": "1",
145
- "name": "Execute Workflow Trigger",
146
- "type": "n8n-nodes-base.executeWorkflowTrigger",
147
- "typeVersion": 1,
148
- "position": [0, 0]
149
- },
150
- {
151
- "parameters": {
152
- "jsCode": "return $input.all();"
153
- },
154
- "id": "2",
155
- "name": "Process Logic",
156
- "type": "n8n-nodes-base.code",
157
- "typeVersion": 2,
158
- "position": [200, 0]
159
- }
160
- ],
161
- "connections": {
162
- "Execute Workflow Trigger": {
163
- "main": [
164
- [
165
- {
166
- "node": "Process Logic",
167
- "type": "main",
168
- "index": 0
169
- }
170
- ]
171
- ]
172
- }
173
- }
174
- }
175
- `;
176
-
177
- async function main() {
178
- console.clear();
179
- showBranding();
180
- intro(pc.bgCyan(pc.black(" create-n8n ")));
181
-
182
- const cwd = process.cwd();
183
-
184
- const cwdFiles = await readdir(cwd);
185
- const allowedFiles = new Set([".git", ".DS_Store", ".vscode", ".idea", "Thumbs.db"]);
186
- const isCwdEmpty = cwdFiles.every((f: string) => allowedFiles.has(f));
187
-
188
- let targetDir = ".";
189
-
190
- if (!isCwdEmpty) {
191
- const dirPrompt = await text({
192
- message: "Project name (creates a new folder, or '.' for current directory)",
193
- placeholder: "my-n8n-app",
194
- defaultValue: "my-n8n-app",
195
- validate: (val) => {
196
- if (!val || val.length === 0) return "Project name is required";
197
- if (!/^[a-z0-9-_./]+$/.test(val)) return "Invalid path format";
198
- return;
199
- }
200
- });
201
-
202
- if (isCancel(dirPrompt) || typeof dirPrompt !== "string") {
203
- cancel("Initialization cancelled.");
204
- return process.exit(1);
205
- }
206
- targetDir = dirPrompt;
207
-
208
- if (targetDir === ".") {
209
- const confirmOverwrite = await confirm({
210
- message: "Current directory is not empty. Continue and potentially overwrite files?",
211
- initialValue: false
212
- });
213
- if (isCancel(confirmOverwrite) || !confirmOverwrite) {
214
- cancel("Initialization cancelled.");
215
- return process.exit(1);
216
- }
217
- }
218
- }
219
-
220
- const projectDir = resolve(cwd, targetDir);
221
- const projectName = targetDir === "." ? basename(cwd) : basename(projectDir);
222
-
223
- const initGit = await confirm({
224
- message: "Initialize a new Git repository?",
225
- initialValue: true
226
- });
227
-
228
- if (isCancel(initGit)) {
229
- cancel("Initialization cancelled.");
230
- return process.exit(1);
231
- }
232
-
233
- const s = spinner();
234
- s.start(`Creating project in ${projectDir}...`);
235
-
236
- try {
237
- // 1. Create Directories
238
- await mkdir(projectDir, { recursive: true });
239
- await mkdir(join(projectDir, "workflows"), { recursive: true });
240
- await mkdir(join(projectDir, "workflows", "subworkflows"), { recursive: true });
241
-
242
- // 2. Write Config / Root Files
243
- await writeFile(join(projectDir, "package.json"), PACKAGE_JSON_TEMPLATE(projectName));
244
- await writeFile(join(projectDir, ".gitignore"), GITIGNORE_TEMPLATE);
245
- await writeFile(join(projectDir, ".env.example"), ENV_TEMPLATE);
246
- await writeFile(join(projectDir, "flat8n.config.json"), CONFIG_TEMPLATE);
247
- await writeFile(join(projectDir, "README.md"), README_TEMPLATE(projectName));
248
-
249
- // 3. Write Workflows
250
- await writeFile(join(projectDir, "workflows", "main.json"), MAIN_JSON_TEMPLATE);
251
- await writeFile(join(projectDir, "workflows", "subworkflows", "child.json"), CHILD_JSON_TEMPLATE);
252
-
253
- s.stop(pc.green(`✔ Project scaffolded successfully in ${projectDir}`));
254
-
255
- if (initGit) {
256
- s.start("Initializing Git repository...");
257
- try {
258
- await execAsync("git init", { cwd: projectDir });
259
- s.stop(pc.green("✔ Git repository initialized"));
260
- } catch (err) {
261
- s.stop(pc.red("✖ Failed to initialize Git repository"));
262
- }
263
- }
264
-
265
- const cdInstruction = targetDir === "." ? "" : ` cd ${targetDir}\n`;
266
- log.info(`\n${pc.bold("Next steps:")}\n${cdInstruction} npm install\n npm run dev`);
267
- outro(pc.cyan("Happy automating! ") + pc.dim("— scaffolded by \x1b]8;;https://github.com/jvondev\x1b\\JVON DEV\x1b]8;;\x1b\\"));
268
- process.exit(0);
269
-
270
- } catch (error: any) {
271
- s.stop(pc.red("✖ Failed to create project"));
272
- log.error(error.message);
273
- process.exit(1);
274
- }
275
- }
276
-
277
- main().catch(console.error);