create-forge-plugin 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bendaamerahmed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=names.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/names.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { deriveNames } from '../names.js';
3
+ describe('deriveNames', () => {
4
+ it('single-segment name without org', () => {
5
+ const n = deriveNames('pagerduty');
6
+ expect(n.pluginId).toBe('pagerduty');
7
+ expect(n.packageName).toBe('forge-plugin-pagerduty');
8
+ expect(n.dirName).toBe('forge-plugin-pagerduty');
9
+ expect(n.pascalName).toBe('Pagerduty');
10
+ expect(n.camelName).toBe('pagerduty');
11
+ expect(n.title).toBe('Pagerduty');
12
+ expect(n.org).toBeUndefined();
13
+ });
14
+ it('multi-segment name with org', () => {
15
+ const n = deriveNames('my-custom-plugin', '@acme');
16
+ expect(n.pluginId).toBe('my-custom-plugin');
17
+ expect(n.packageName).toBe('@acme/forge-plugin-my-custom-plugin');
18
+ expect(n.dirName).toBe('forge-plugin-my-custom-plugin');
19
+ expect(n.pascalName).toBe('MyCustomPlugin');
20
+ expect(n.camelName).toBe('myCustomPlugin');
21
+ expect(n.title).toBe('My Custom Plugin');
22
+ expect(n.org).toBe('@acme');
23
+ });
24
+ it('two-segment name', () => {
25
+ const n = deriveNames('slack-notify');
26
+ expect(n.pascalName).toBe('SlackNotify');
27
+ expect(n.camelName).toBe('slackNotify');
28
+ expect(n.title).toBe('Slack Notify');
29
+ });
30
+ it('single char segments', () => {
31
+ const n = deriveNames('a-b-c');
32
+ expect(n.pascalName).toBe('ABC');
33
+ expect(n.camelName).toBe('aBC');
34
+ });
35
+ it('throws on uppercase in name', () => {
36
+ expect(() => deriveNames('MyPlugin')).toThrow('Invalid plugin ID');
37
+ });
38
+ it('throws on leading hyphen', () => {
39
+ expect(() => deriveNames('-bad')).toThrow('Invalid plugin ID');
40
+ });
41
+ it('throws on invalid org (no @)', () => {
42
+ expect(() => deriveNames('plugin', 'myorg')).toThrow('Invalid org scope');
43
+ });
44
+ it('throws on org with uppercase', () => {
45
+ expect(() => deriveNames('plugin', '@MyOrg')).toThrow('Invalid org scope');
46
+ });
47
+ });
48
+ //# sourceMappingURL=names.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"names.test.js","sourceRoot":"","sources":["../../src/__tests__/names.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scaffolder.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffolder.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/scaffolder.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,78 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { deriveNames } from '../names.js';
6
+ import { scaffoldPlugin } from '../scaffolder.js';
7
+ async function withTempDir(fn) {
8
+ const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'forge-plugin-test-'));
9
+ try {
10
+ await fn(dir);
11
+ }
12
+ finally {
13
+ await fs.rm(dir, { recursive: true, force: true });
14
+ }
15
+ }
16
+ describe('scaffoldPlugin', () => {
17
+ it('creates all UI plugin files', async () => {
18
+ await withTempDir(async (tmpDir) => {
19
+ const names = deriveNames('pagerduty', '@acme');
20
+ const result = await scaffoldPlugin(names, 'ui', tmpDir);
21
+ expect(result.filesCreated).toContain('forgeportal-plugin.json');
22
+ expect(result.filesCreated).toContain('package.json');
23
+ expect(result.filesCreated).toContain('tsconfig.json');
24
+ expect(result.filesCreated).toContain('README.md');
25
+ expect(result.filesCreated).toContain('src/index.ts');
26
+ expect(result.filesCreated).toContain('src/PagerdutyTab.tsx');
27
+ });
28
+ });
29
+ it('creates all backend plugin files', async () => {
30
+ await withTempDir(async (tmpDir) => {
31
+ const names = deriveNames('slack-notify');
32
+ const result = await scaffoldPlugin(names, 'backend', tmpDir);
33
+ expect(result.filesCreated).toContain('src/index.ts');
34
+ expect(result.filesCreated).toContain('src/actions/slackNotifyAction.ts');
35
+ expect(result.filesCreated).toContain('src/routes.ts');
36
+ // UI files must NOT be present
37
+ expect(result.filesCreated).not.toContain('src/SlackNotifyTab.tsx');
38
+ });
39
+ });
40
+ it('creates all fullstack plugin files', async () => {
41
+ await withTempDir(async (tmpDir) => {
42
+ const names = deriveNames('costview', '@myco');
43
+ const result = await scaffoldPlugin(names, 'fullstack', tmpDir);
44
+ expect(result.filesCreated).toContain('src/ui/index.ts');
45
+ expect(result.filesCreated).toContain('src/ui/CostviewCard.tsx');
46
+ expect(result.filesCreated).toContain('src/backend/index.ts');
47
+ expect(result.filesCreated).toContain('src/backend/actions/costviewAction.ts');
48
+ expect(result.filesCreated).toContain('src/backend/routes.ts');
49
+ expect(result.filesCreated).toContain('src/index.ts');
50
+ });
51
+ });
52
+ it('throws if target directory already exists', async () => {
53
+ await withTempDir(async (tmpDir) => {
54
+ const names = deriveNames('duplicate');
55
+ await fs.mkdir(path.join(tmpDir, names.dirName));
56
+ await expect(scaffoldPlugin(names, 'ui', tmpDir)).rejects.toThrow('already exists');
57
+ });
58
+ });
59
+ it('generated package.json is valid JSON with correct name', async () => {
60
+ await withTempDir(async (tmpDir) => {
61
+ const names = deriveNames('pagerduty', '@acme');
62
+ const result = await scaffoldPlugin(names, 'ui', tmpDir);
63
+ const pkgPath = path.join(result.targetDir, 'package.json');
64
+ const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
65
+ expect(pkg.name).toBe('@acme/forge-plugin-pagerduty');
66
+ });
67
+ });
68
+ it('generated forgeportal-plugin.json is valid JSON', async () => {
69
+ await withTempDir(async (tmpDir) => {
70
+ const names = deriveNames('test-plugin');
71
+ const result = await scaffoldPlugin(names, 'backend', tmpDir);
72
+ const manifestPath = path.join(result.targetDir, 'forgeportal-plugin.json');
73
+ const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
74
+ expect(manifest.forgeportal.type).toBe('backend');
75
+ });
76
+ });
77
+ });
78
+ //# sourceMappingURL=scaffolder.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffolder.test.js","sourceRoot":"","sources":["../../src/__tests__/scaffolder.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=templates.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/templates.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { deriveNames } from '../names.js';
3
+ import { genManifest, genPackageJson, genTsConfig, genUiIndex, genUiTab, genBackendIndex, genBackendAction, genFullstackUiIndex, } from '../templates.js';
4
+ const pdNames = deriveNames('pagerduty', '@acme');
5
+ const multiNames = deriveNames('my-plugin');
6
+ describe('genManifest', () => {
7
+ it('UI type — no backend capabilities', () => {
8
+ const manifest = JSON.parse(genManifest(pdNames, 'ui'));
9
+ expect(manifest.name).toBe('@acme/forge-plugin-pagerduty');
10
+ expect(manifest.forgeportal.engineVersion).toBe('^1.0.0');
11
+ expect(manifest.forgeportal.type).toBe('ui');
12
+ expect(manifest.forgeportal.capabilities.ui?.entityTabs).toContain('pagerduty-tab');
13
+ expect(manifest.forgeportal.capabilities.backend).toBeUndefined();
14
+ });
15
+ it('backend type — no UI capabilities', () => {
16
+ const manifest = JSON.parse(genManifest(pdNames, 'backend'));
17
+ expect(manifest.forgeportal.type).toBe('backend');
18
+ expect(manifest.forgeportal.capabilities.backend?.actionProviders).toContain('pagerduty.myAction@v1');
19
+ expect(manifest.forgeportal.capabilities.ui).toBeUndefined();
20
+ });
21
+ it('fullstack type — both capabilities', () => {
22
+ const manifest = JSON.parse(genManifest(pdNames, 'fullstack'));
23
+ expect(manifest.forgeportal.type).toBe('fullstack');
24
+ expect(manifest.forgeportal.capabilities.ui).toBeDefined();
25
+ expect(manifest.forgeportal.capabilities.backend).toBeDefined();
26
+ });
27
+ it('output is valid JSON', () => {
28
+ expect(() => JSON.parse(genManifest(pdNames, 'ui'))).not.toThrow();
29
+ expect(() => JSON.parse(genManifest(pdNames, 'backend'))).not.toThrow();
30
+ expect(() => JSON.parse(genManifest(pdNames, 'fullstack'))).not.toThrow();
31
+ });
32
+ });
33
+ describe('genPackageJson', () => {
34
+ it('UI plugin has react peer dep', () => {
35
+ const pkg = JSON.parse(genPackageJson(pdNames, 'ui'));
36
+ expect(pkg.peerDependencies['@forgeportal/plugin-sdk']).toBe('^1.0.0');
37
+ expect(pkg.peerDependencies['react']).toBeDefined();
38
+ expect(pkg.devDependencies['fastify']).toBeUndefined();
39
+ });
40
+ it('backend plugin has fastify dev dep, no react', () => {
41
+ const pkg = JSON.parse(genPackageJson(pdNames, 'backend'));
42
+ expect(pkg.devDependencies['fastify']).toBeDefined();
43
+ expect(pkg.devDependencies['react']).toBeUndefined();
44
+ expect(pkg.peerDependencies['react']).toBeUndefined();
45
+ });
46
+ it('fullstack plugin has both react and fastify', () => {
47
+ const pkg = JSON.parse(genPackageJson(pdNames, 'fullstack'));
48
+ expect(pkg.peerDependencies['react']).toBeDefined();
49
+ expect(pkg.devDependencies['fastify']).toBeDefined();
50
+ });
51
+ it('package name is correctly set', () => {
52
+ const pkg = JSON.parse(genPackageJson(pdNames, 'ui'));
53
+ expect(pkg.name).toBe('@acme/forge-plugin-pagerduty');
54
+ });
55
+ it('no org: package name without scope', () => {
56
+ const pkg = JSON.parse(genPackageJson(multiNames, 'ui'));
57
+ expect(pkg.name).toBe('forge-plugin-my-plugin');
58
+ });
59
+ });
60
+ describe('genTsConfig', () => {
61
+ it('UI tsconfig has jsx: react-jsx', () => {
62
+ const tsc = JSON.parse(genTsConfig('ui'));
63
+ expect(tsc.compilerOptions.jsx).toBe('react-jsx');
64
+ });
65
+ it('backend tsconfig has no jsx', () => {
66
+ const tsc = JSON.parse(genTsConfig('backend'));
67
+ expect(tsc.compilerOptions.jsx).toBeUndefined();
68
+ });
69
+ it('fullstack tsconfig has jsx: react-jsx', () => {
70
+ const tsc = JSON.parse(genTsConfig('fullstack'));
71
+ expect(tsc.compilerOptions.jsx).toBe('react-jsx');
72
+ });
73
+ });
74
+ describe('genUiIndex', () => {
75
+ it('exports registerPlugin function', () => {
76
+ const src = genUiIndex(pdNames);
77
+ expect(src).toContain('export function registerPlugin(sdk: ForgePluginSDK)');
78
+ expect(src).toContain("import type { ForgePluginSDK } from '@forgeportal/plugin-sdk'");
79
+ expect(src).toContain("id: 'pagerduty-tab'");
80
+ });
81
+ it('imports correct component', () => {
82
+ const src = genUiIndex(pdNames);
83
+ expect(src).toContain('import { PagerdutyTab }');
84
+ });
85
+ });
86
+ describe('genUiTab', () => {
87
+ it('uses useEntity hook', () => {
88
+ const src = genUiTab(pdNames);
89
+ expect(src).toContain("import { useEntity, useConfig }");
90
+ expect(src).toContain("from '@forgeportal/plugin-sdk/react'");
91
+ expect(src).toContain('const { entity } = useEntity()');
92
+ expect(src).toContain('export function PagerdutyTab()');
93
+ });
94
+ it('uses correct component name for multi-segment', () => {
95
+ const src = genUiTab(multiNames);
96
+ expect(src).toContain('export function MyPluginTab()');
97
+ });
98
+ });
99
+ describe('genBackendIndex', () => {
100
+ it('exports registerBackendPlugin function', () => {
101
+ const src = genBackendIndex(pdNames);
102
+ expect(src).toContain('export function registerBackendPlugin(sdk: ForgeBackendPluginSDK)');
103
+ expect(src).toContain("import type { ForgeBackendPluginSDK } from '@forgeportal/plugin-sdk'");
104
+ });
105
+ it('registers the action provider', () => {
106
+ const src = genBackendIndex(pdNames);
107
+ expect(src).toContain('sdk.registerActionProvider(pagerdutyAction)');
108
+ });
109
+ it('registers a backend route', () => {
110
+ const src = genBackendIndex(pdNames);
111
+ expect(src).toContain("path: '/status'");
112
+ expect(src).toContain('sdk.registerBackendRoute(');
113
+ });
114
+ });
115
+ describe('genBackendAction', () => {
116
+ it('has correct action id and version', () => {
117
+ const src = genBackendAction(pdNames);
118
+ expect(src).toContain("id: 'pagerduty.myAction'");
119
+ expect(src).toContain("version: 'v1'");
120
+ });
121
+ it('handler is async', () => {
122
+ const src = genBackendAction(pdNames);
123
+ expect(src).toContain('async handler(ctx, input)');
124
+ });
125
+ it('uses ctx.logger', () => {
126
+ const src = genBackendAction(pdNames);
127
+ expect(src).toContain('ctx.logger.info');
128
+ });
129
+ });
130
+ describe('genFullstackUiIndex', () => {
131
+ it('exports registerPlugin (not registerBackendPlugin)', () => {
132
+ const src = genFullstackUiIndex(pdNames);
133
+ expect(src).toContain('export function registerPlugin(sdk: ForgePluginSDK)');
134
+ expect(src).not.toContain('registerBackendPlugin');
135
+ });
136
+ });
137
+ //# sourceMappingURL=templates.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../src/__tests__/templates.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,WAAW,EAAE,cAAc,EAAE,WAAW,EACxC,UAAU,EAAE,QAAQ,EACpB,eAAe,EAAE,gBAAgB,EACjC,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,OAAO,GAAK,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AACpD,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;AAE5C,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAOrD,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC3D,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1D,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACpF,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,CAK1D,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAClD,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QACtG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,CAK5D,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3D,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACnE,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACxE,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAGnD,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAGxD,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,CAAC,CAG1D,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAqB,CAAC;QAC1E,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,CAAqB,CAAC;QAC7E,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAA0C,CAAC;QACnF,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAA0C,CAAC;QACxF,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,aAAa,EAAE,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,CAA0C,CAAC;QAC1F,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,qDAAqD,CAAC,CAAC;QAC7E,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,+DAA+D,CAAC,CAAC;QACvF,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAC;QACzD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sCAAsC,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,oCAAoC,CAAC,CAAC;QAC5D,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QACjC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,mEAAmE,CAAC,CAAC;QAC3F,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sEAAsE,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,6CAA6C,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;QACvD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAC1B,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE;QACzB,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,GAAG,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,qDAAqD,CAAC,CAAC;QAC7E,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/dist/args.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { PluginType } from './names.js';
2
+ export interface CliArgs {
3
+ /** Short plugin name from positional arg, e.g. "pagerduty" */
4
+ name?: string;
5
+ /** Plugin type from --type flag */
6
+ type?: PluginType;
7
+ /** npm org scope from --org flag, e.g. "@myorg" */
8
+ org?: string;
9
+ /** Output parent directory (default: process.cwd()) */
10
+ outDir?: string;
11
+ /** Skip all prompts, use defaults */
12
+ yes: boolean;
13
+ }
14
+ export declare function parseArgs(argv: string[]): CliArgs;
15
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,OAAO;IACtB,8DAA8D;IAC9D,IAAI,CAAC,EAAI,MAAM,CAAC;IAChB,mCAAmC;IACnC,IAAI,CAAC,EAAI,UAAU,CAAC;IACpB,mDAAmD;IACnD,GAAG,CAAC,EAAK,MAAM,CAAC;IAChB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,GAAG,EAAM,OAAO,CAAC;CAClB;AAID,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CA4CjD"}
package/dist/args.js ADDED
@@ -0,0 +1,72 @@
1
+ const VALID_TYPES = ['ui', 'backend', 'fullstack'];
2
+ export function parseArgs(argv) {
3
+ const args = argv.slice(2); // skip "node" + script
4
+ const result = { yes: false };
5
+ let i = 0;
6
+ while (i < args.length) {
7
+ const arg = args[i];
8
+ if (arg === '--type' || arg === '-t') {
9
+ const val = args[++i];
10
+ if (!val || !VALID_TYPES.includes(val)) {
11
+ console.error(`Error: --type must be one of: ${VALID_TYPES.join(', ')}`);
12
+ process.exit(1);
13
+ }
14
+ result.type = val;
15
+ }
16
+ else if (arg.startsWith('--type=')) {
17
+ const val = arg.split('=')[1];
18
+ if (!val || !VALID_TYPES.includes(val)) {
19
+ console.error(`Error: --type must be one of: ${VALID_TYPES.join(', ')}`);
20
+ process.exit(1);
21
+ }
22
+ result.type = val;
23
+ }
24
+ else if (arg === '--org' || arg === '-o') {
25
+ result.org = args[++i];
26
+ }
27
+ else if (arg.startsWith('--org=')) {
28
+ result.org = arg.split('=')[1];
29
+ }
30
+ else if (arg === '--out-dir' || arg === '--outDir') {
31
+ result.outDir = args[++i];
32
+ }
33
+ else if (arg === '--yes' || arg === '-y') {
34
+ result.yes = true;
35
+ }
36
+ else if (arg === '--help' || arg === '-h') {
37
+ printHelp();
38
+ process.exit(0);
39
+ }
40
+ else if (!arg.startsWith('-')) {
41
+ result.name = arg;
42
+ }
43
+ else {
44
+ console.error(`Unknown flag: ${arg}`);
45
+ printHelp();
46
+ process.exit(1);
47
+ }
48
+ i++;
49
+ }
50
+ return result;
51
+ }
52
+ function printHelp() {
53
+ console.log(`
54
+ Usage: create-forge-plugin [name] [options]
55
+
56
+ Arguments:
57
+ name Short plugin ID (kebab-case), e.g. "pagerduty"
58
+
59
+ Options:
60
+ --type, -t Plugin type: ui | backend | fullstack
61
+ --org, -o npm scope, e.g. "@myorg"
62
+ --out-dir Output parent directory (default: current directory)
63
+ --yes, -y Skip prompts, use defaults
64
+ --help, -h Show this help
65
+
66
+ Examples:
67
+ npx create-forge-plugin pagerduty --type ui --org @myorg
68
+ npx create-forge-plugin slack-notify --type backend
69
+ npx create-forge-plugin costview --type fullstack --org @acme
70
+ `);
71
+ }
72
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env node
2
+ import process from 'node:process';
3
+ import path from 'node:path';
4
+ import { intro, outro, text, select, note, spinner, isCancel, cancel, } from '@clack/prompts';
5
+ import { parseArgs } from './args.js';
6
+ import { deriveNames } from './names.js';
7
+ import { scaffoldPlugin } from './scaffolder.js';
8
+ async function main() {
9
+ const args = parseArgs(process.argv);
10
+ intro('⚡ create-forge-plugin — ForgePortal Plugin Scaffolder');
11
+ // ── Collect plugin name ───────────────────────────────────────────────────
12
+ let pluginId;
13
+ if (args.name) {
14
+ pluginId = args.name;
15
+ }
16
+ else {
17
+ const result = await text({
18
+ message: 'Plugin ID (short kebab-case name):',
19
+ placeholder: 'pagerduty',
20
+ validate(value) {
21
+ if (!value)
22
+ return 'Plugin ID is required.';
23
+ if (!/^[a-z][a-z0-9-]*$/.test(value)) {
24
+ return 'Must be lowercase kebab-case (letters, numbers, hyphens). e.g. "my-plugin"';
25
+ }
26
+ },
27
+ });
28
+ if (isCancel(result)) {
29
+ cancel('Cancelled.');
30
+ process.exit(0);
31
+ }
32
+ pluginId = result;
33
+ }
34
+ // ── Collect plugin type ───────────────────────────────────────────────────
35
+ let type;
36
+ if (args.type) {
37
+ type = args.type;
38
+ }
39
+ else {
40
+ const result = await select({
41
+ message: 'Plugin type:',
42
+ options: [
43
+ {
44
+ value: 'ui',
45
+ label: 'UI only',
46
+ hint: 'Entity tabs, entity cards, top-level routes (React components)',
47
+ },
48
+ {
49
+ value: 'backend',
50
+ label: 'Backend only',
51
+ hint: 'Fastify routes, action providers, catalog providers',
52
+ },
53
+ {
54
+ value: 'fullstack',
55
+ label: 'Fullstack',
56
+ hint: 'Both UI components and backend capabilities in one package',
57
+ },
58
+ ],
59
+ });
60
+ if (isCancel(result)) {
61
+ cancel('Cancelled.');
62
+ process.exit(0);
63
+ }
64
+ type = result;
65
+ }
66
+ // ── Collect org scope (optional) ──────────────────────────────────────────
67
+ let org;
68
+ if (args.org) {
69
+ org = args.org;
70
+ }
71
+ else if (!args.yes) {
72
+ const result = await text({
73
+ message: 'npm scope (optional):',
74
+ placeholder: '@myorg',
75
+ validate(value) {
76
+ if (!value)
77
+ return; // optional
78
+ if (!value.startsWith('@'))
79
+ return 'Scope must start with @, e.g. "@myorg"';
80
+ if (!/^@[a-z][a-z0-9-]*$/.test(value))
81
+ return 'Scope must be lowercase, e.g. "@myorg"';
82
+ },
83
+ });
84
+ if (isCancel(result)) {
85
+ cancel('Cancelled.');
86
+ process.exit(0);
87
+ }
88
+ org = result || undefined;
89
+ }
90
+ // ── Derive all names ──────────────────────────────────────────────────────
91
+ let names;
92
+ try {
93
+ names = deriveNames(pluginId, org);
94
+ }
95
+ catch (err) {
96
+ cancel(err instanceof Error ? err.message : String(err));
97
+ process.exit(1);
98
+ }
99
+ const parentDir = args.outDir ? path.resolve(args.outDir) : process.cwd();
100
+ // ── Preview before writing ────────────────────────────────────────────────
101
+ note([
102
+ `Package: ${names.packageName}`,
103
+ `Type: ${type}`,
104
+ `Directory: ${path.join(parentDir, names.dirName)}`,
105
+ ].join('\n'), 'Plugin summary');
106
+ // ── Scaffold files ────────────────────────────────────────────────────────
107
+ const s = spinner();
108
+ s.start('Generating plugin files\u2026');
109
+ let result;
110
+ try {
111
+ result = await scaffoldPlugin(names, type, parentDir);
112
+ }
113
+ catch (err) {
114
+ s.stop('Failed.');
115
+ cancel(err instanceof Error ? err.message : String(err));
116
+ process.exit(1);
117
+ }
118
+ s.stop(`Created ${result.filesCreated.length} files.`);
119
+ // ── List generated files ──────────────────────────────────────────────────
120
+ note(result.filesCreated.map((f) => ` ${f}`).join('\n'), 'Files created');
121
+ // ── Next steps ────────────────────────────────────────────────────────────
122
+ const steps = buildNextSteps(names, type, result.targetDir);
123
+ note(steps, 'Next steps');
124
+ outro(`Plugin "${names.pluginId}" scaffolded successfully!`);
125
+ }
126
+ function buildNextSteps(names, type, targetDir) {
127
+ const relDir = path.relative(process.cwd(), targetDir) || names.dirName;
128
+ const lines = [];
129
+ lines.push(`1. Install dependencies:`);
130
+ lines.push(` cd ${relDir}`);
131
+ lines.push(` pnpm install`);
132
+ lines.push('');
133
+ lines.push(`2. Build:`);
134
+ lines.push(` pnpm build`);
135
+ lines.push('');
136
+ lines.push(`3. Install in the ForgePortal monorepo:`);
137
+ lines.push(` cd <path-to-forgeportal>`);
138
+ lines.push(` pnpm add ${names.packageName}`);
139
+ lines.push('');
140
+ if (type === 'ui' || type === 'fullstack') {
141
+ lines.push(`4. Register the UI plugin in apps/ui/src/plugins/index.ts:`);
142
+ lines.push(` import { registerPlugin } from '${names.packageName}${type === 'fullstack' ? '/ui' : ''}';`);
143
+ lines.push(` registerPluginById('${names.pluginId}', registerPlugin);`);
144
+ lines.push('');
145
+ }
146
+ if (type === 'backend' || type === 'fullstack') {
147
+ const stepNum = type === 'fullstack' ? 5 : 4;
148
+ lines.push(`${stepNum}. Register the backend plugin in forgeportal.yaml:`);
149
+ lines.push(` pluginPackages:`);
150
+ lines.push(` packages:`);
151
+ lines.push(` - "${names.packageName}"`);
152
+ lines.push('');
153
+ }
154
+ lines.push(`Docs: https://github.com/your-org/forgeportal/tree/main/docs/plugin-development.md`);
155
+ return lines.join('\n');
156
+ }
157
+ main().catch((err) => {
158
+ console.error(err);
159
+ process.exit(1);
160
+ });
161
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}