@synoikia/create-plugin 0.1.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -0
  3. package/dist/build.d.ts +16 -0
  4. package/dist/build.js +32 -0
  5. package/dist/build.js.map +1 -0
  6. package/dist/check.d.ts +7 -0
  7. package/dist/check.js +49 -0
  8. package/dist/check.js.map +1 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +104 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/create.d.ts +2 -0
  13. package/dist/create.js +56 -0
  14. package/dist/create.js.map +1 -0
  15. package/dist/files.d.ts +12 -0
  16. package/dist/files.js +42 -0
  17. package/dist/files.js.map +1 -0
  18. package/dist/index.d.ts +10 -0
  19. package/dist/index.js +8 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/prompts.d.ts +46 -0
  22. package/dist/prompts.js +112 -0
  23. package/dist/prompts.js.map +1 -0
  24. package/dist/repo-root.d.ts +10 -0
  25. package/dist/repo-root.js +47 -0
  26. package/dist/repo-root.js.map +1 -0
  27. package/dist/repo.d.ts +68 -0
  28. package/dist/repo.js +222 -0
  29. package/dist/repo.js.map +1 -0
  30. package/dist/scaffold.d.ts +68 -0
  31. package/dist/scaffold.js +296 -0
  32. package/dist/scaffold.js.map +1 -0
  33. package/dist/vitest.d.ts +12 -0
  34. package/dist/vitest.js +17 -0
  35. package/dist/vitest.js.map +1 -0
  36. package/package.json +56 -0
  37. package/templates/plugin/blank/plugin.yaml +22 -0
  38. package/templates/plugin/blank/src/plugin.ts +74 -0
  39. package/templates/plugin/blank/test/plugin.test.ts +33 -0
  40. package/templates/plugin/common/README.md +23 -0
  41. package/templates/plugin/common/RUNBOOK.md +10 -0
  42. package/templates/plugin/common/src/index.ts +4 -0
  43. package/templates/plugin/common/tsconfig.build.json +9 -0
  44. package/templates/plugin/common/tsconfig.json +7 -0
  45. package/templates/plugin/common/vitest.config.ts +1 -0
  46. package/templates/plugin/openapi-rest/plugin.yaml +49 -0
  47. package/templates/plugin/openapi-rest/src/plugin.ts +89 -0
  48. package/templates/plugin/openapi-rest/test/e2e.test.ts +49 -0
  49. package/templates/plugin/openapi-rest/test/fake-upstream.ts +63 -0
  50. package/templates/plugin/openapi-rest/test/plugin.test.ts +66 -0
  51. package/templates/plugin/static-rest/plugin.yaml +61 -0
  52. package/templates/plugin/static-rest/src/plugin.ts +70 -0
  53. package/templates/plugin/static-rest/test/e2e.test.ts +46 -0
  54. package/templates/plugin/static-rest/test/fake-upstream.ts +39 -0
  55. package/templates/plugin/static-rest/test/plugin.test.ts +67 -0
  56. package/templates/plugin/websocket-rpc/plugin.yaml +45 -0
  57. package/templates/plugin/websocket-rpc/src/client.ts +96 -0
  58. package/templates/plugin/websocket-rpc/src/plugin.ts +98 -0
  59. package/templates/plugin/websocket-rpc/test/e2e.test.ts +46 -0
  60. package/templates/plugin/websocket-rpc/test/fake-upstream.ts +63 -0
  61. package/templates/plugin/websocket-rpc/test/plugin.test.ts +67 -0
  62. package/templates/repo/base/README.md +27 -0
  63. package/templates/repo/base/_github/workflows/ci.yml +27 -0
  64. package/templates/repo/base/_gitignore +6 -0
  65. package/templates/repo/base/_nvmrc +1 -0
  66. package/templates/repo/base/_prettierignore +3 -0
  67. package/templates/repo/base/_prettierrc +5 -0
  68. package/templates/repo/base/eslint.config.js +17 -0
  69. package/templates/repo/base/package.json +34 -0
  70. package/templates/repo/base/plugin-files.d.ts +10 -0
  71. package/templates/repo/base/pnpm-workspace.yaml +11 -0
  72. package/templates/repo/base/tsconfig.base.json +21 -0
  73. package/templates/repo/base/vitest.shared.ts +9 -0
  74. package/templates/repo/claude/CLAUDE.md +38 -0
  75. package/templates/repo/claude/_claude/agents/security-reviewer.md +33 -0
  76. package/templates/repo/claude/_claude/hooks/format.mjs +20 -0
  77. package/templates/repo/claude/_claude/hooks/guard-paths.mjs +43 -0
  78. package/templates/repo/claude/_claude/hooks/session-start.sh +13 -0
  79. package/templates/repo/claude/_claude/settings.json +21 -0
  80. package/templates/repo/claude/_claude/skills/add-plugin/SKILL.md +39 -0
  81. package/templates/repo/claude/_claude/skills/release-plugin/SKILL.md +18 -0
  82. package/templates/repo/release/RELEASING.md +27 -0
  83. package/templates/repo/release/_github/workflows/release.yml +89 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 via-justa
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.
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # @synoikia/create-plugin
2
+
3
+ Create [Synoikia](https://github.com/via-justa/synoikia-core) plugin repositories and plugins, then build, check and publish them. Guide: [`docs/plugin-authoring.md`](https://github.com/via-justa/synoikia-core/blob/main/docs/plugin-authoring.md).
4
+
5
+ ```sh
6
+ pnpm create @synoikia/plugin my-plugins [--repository owner/name] [--release] [--claude]
7
+ ```
8
+
9
+ Inside a plugin repository (a root devDependency; `pnpm new` maps to `synoikia-plugin new`):
10
+
11
+ | Command | What |
12
+ | --------------------------------------------------- | ------------------------------------------------------------------------- |
13
+ | `synoikia-plugin new` | Add a plugin: `--id --name --archetype --auth [--api-key-header] --yes` |
14
+ | `synoikia-plugin build [dir]` | Bundle a plugin into `dist/index.js`, `plugin.yaml` validated and inlined |
15
+ | `synoikia-plugin check [dir…]` | Manifest, `plugin.yaml` and version checks |
16
+ | `synoikia-plugin repo pack\|index\|verify\|publish` | The signed plugin repository steps a release workflow runs |
17
+
18
+ `@synoikia/create-plugin/vitest` exports `pluginFiles()`, the Vitest plugin that loads `.yaml` and `.md` imports as the bundle does.
19
+
20
+ Templates are plain files with `{{name}}` placeholders; nothing in a template runs. A smoke test scaffolds every archetype and runs its generated tests against the workspace SDK and core.
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Bundles a plugin into one self-contained `dist/index.js`: under the permission model the child can
3
+ * read nothing outside its own package directory, so every dependency is inlined, and data files
4
+ * (`plugin.yaml`, guides) are compiled in. The banner gives the ESM bundle a `require` for bundled
5
+ * CommonJS (such as `ws`).
6
+ */
7
+ export interface BuildOptions {
8
+ /** The plugin package directory. */
9
+ dir: string;
10
+ entry?: string;
11
+ outfile?: string;
12
+ /** Extra export conditions, e.g. `synoikia-source` to bundle workspace sources in tests. */
13
+ conditions?: string[];
14
+ minify?: boolean;
15
+ }
16
+ export declare function buildPlugin(opts: BuildOptions): Promise<string>;
package/dist/build.js ADDED
@@ -0,0 +1,32 @@
1
+ import path from 'node:path';
2
+ import { build } from 'esbuild';
3
+ import { moduleSource } from './files.js';
4
+ const BANNER = "import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);";
5
+ const dataFiles = {
6
+ name: 'synoikia-plugin-files',
7
+ setup(b) {
8
+ b.onLoad({ filter: /\.(ya?ml|md)$/ }, (args) => ({
9
+ contents: moduleSource(args.path),
10
+ loader: 'js',
11
+ watchFiles: [args.path],
12
+ }));
13
+ },
14
+ };
15
+ export async function buildPlugin(opts) {
16
+ const outfile = path.resolve(opts.dir, opts.outfile ?? 'dist/index.js');
17
+ await build({
18
+ entryPoints: [path.resolve(opts.dir, opts.entry ?? 'src/index.ts')],
19
+ outfile,
20
+ bundle: true,
21
+ platform: 'node',
22
+ format: 'esm',
23
+ target: 'node22',
24
+ logLevel: 'warning',
25
+ banner: { js: BANNER },
26
+ conditions: opts.conditions,
27
+ minify: opts.minify ?? false,
28
+ plugins: [dataFiles],
29
+ });
30
+ return outfile;
31
+ }
32
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAmB1C,MAAM,MAAM,GACV,mHAAmH,CAAC;AAEtH,MAAM,SAAS,GAAW;IACxB,IAAI,EAAE,uBAAuB;IAC7B,KAAK,CAAC,CAAC;QACL,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC/C,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;YACjC,MAAM,EAAE,IAAI;YACZ,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;SACxB,CAAC,CAAC,CAAC;IACN,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAkB;IAClD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC;IACxE,MAAM,KAAK,CAAC;QACV,WAAW,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;QACnE,OAAO;QACP,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,QAAQ;QAChB,QAAQ,EAAE,SAAS;QACnB,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE;QACtB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;QAC5B,OAAO,EAAE,CAAC,SAAS,CAAC;KACrB,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Static checks of a plugin package, the ones every plugin used to repeat in a `manifest.test.ts`:
3
+ * the manifest is valid and compatible with this SDK, credential fields are write-only secrets in
4
+ * `sensitiveKeys`, `plugin.yaml` is valid and names only declared match profiles, the manifest id is
5
+ * the directory name, and `manifest.json` and `package.json` carry the same version.
6
+ */
7
+ export declare function checkPlugin(dir: string): string[];
package/dist/check.js ADDED
@@ -0,0 +1,49 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { checkManifest, isSdkCompatible, parseManifest, parsePluginSettings } from '@synoikia/plugin-sdk';
4
+ import { parseYamlFile } from './files.js';
5
+ /**
6
+ * Static checks of a plugin package, the ones every plugin used to repeat in a `manifest.test.ts`:
7
+ * the manifest is valid and compatible with this SDK, credential fields are write-only secrets in
8
+ * `sensitiveKeys`, `plugin.yaml` is valid and names only declared match profiles, the manifest id is
9
+ * the directory name, and `manifest.json` and `package.json` carry the same version.
10
+ */
11
+ export function checkPlugin(dir) {
12
+ const read = (name) => JSON.parse(readFileSync(path.join(dir, name), 'utf8'));
13
+ let manifest;
14
+ try {
15
+ manifest = read('manifest.json');
16
+ }
17
+ catch (err) {
18
+ return [`manifest.json: ${err.message}`];
19
+ }
20
+ const issues = [];
21
+ let settings;
22
+ const yamlFile = path.join(dir, 'plugin.yaml');
23
+ if (existsSync(yamlFile)) {
24
+ try {
25
+ settings = parsePluginSettings(parseYamlFile(yamlFile));
26
+ }
27
+ catch (err) {
28
+ issues.push(`plugin.yaml: ${err.message}`);
29
+ }
30
+ }
31
+ issues.push(...checkManifest(manifest, settings));
32
+ if (issues.some((i) => i.startsWith('manifest:')))
33
+ return issues;
34
+ const parsed = parseManifest(manifest);
35
+ if (!isSdkCompatible(parsed))
36
+ issues.push(`manifest.json: sdk ${parsed.sdk} does not accept this SDK`);
37
+ if (parsed.id !== path.basename(path.resolve(dir)))
38
+ issues.push(`manifest.json: id ${parsed.id} is not the directory name ${path.basename(path.resolve(dir))}`);
39
+ try {
40
+ const pkg = read('package.json');
41
+ if (pkg.version !== parsed.version)
42
+ issues.push(`package.json version ${String(pkg.version)} and manifest.json version ${parsed.version} differ`);
43
+ }
44
+ catch (err) {
45
+ issues.push(`package.json: ${err.message}`);
46
+ }
47
+ return issues;
48
+ }
49
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE1G,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAA4B,CAAC;IACjH,IAAI,QAAiB,CAAC;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,kBAAmB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,QAA6C,CAAC;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC/C,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,QAAQ,GAAG,mBAAmB,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,gBAAiB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClD,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACjE,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,sBAAsB,MAAM,CAAC,GAAG,2BAA2B,CAAC,CAAC;IACvG,IAAI,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,CAAC,IAAI,CAAC,qBAAqB,MAAM,CAAC,EAAE,8BAA8B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9G,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;QACjC,IAAI,GAAG,CAAC,OAAO,KAAK,MAAM,CAAC,OAAO;YAChC,MAAM,CAAC,IAAI,CAAC,wBAAwB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,8BAA8B,MAAM,CAAC,OAAO,SAAS,CAAC,CAAC;IAClH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,iBAAkB,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, readdirSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { parseArgs } from 'node:util';
5
+ import { buildPlugin } from './build.js';
6
+ import { checkPlugin } from './check.js';
7
+ import { askPluginOptions, PLUGIN_FLAGS, prompts } from './prompts.js';
8
+ import { createRepoTool } from './repo.js';
9
+ import { findRepoRoot } from './repo-root.js';
10
+ import { install, newPlugin } from './scaffold.js';
11
+ const USAGE = `synoikia-plugin <command>
12
+
13
+ new Add a plugin to this repository (asks for what flags don't give)
14
+ --id <id> --name <name> --namespace <ns> --description <text>
15
+ --archetype openapi-rest|static-rest|websocket-rpc|blank
16
+ --auth bearer|api-key|basic|none [--api-key-header X-Api-Key]
17
+ --yes (no prompts) --skip-install
18
+ build [dir] Bundle a plugin into dist/index.js (plugin.yaml and guides inlined)
19
+ --conditions a,b Extra export conditions
20
+ check [dir...] Check manifests, plugin.yaml and versions (default: this plugin, or all)
21
+ repo pack|index|verify|publish
22
+ Build the signed plugin repository (release workflow steps)
23
+ --repository owner/name --out .repo --current .repo-current/index.json
24
+ `;
25
+ async function main(argv) {
26
+ const [command, ...rest] = argv;
27
+ switch (command) {
28
+ case 'new': {
29
+ const { values } = parseArgs({ args: rest, options: PLUGIN_FLAGS });
30
+ const root = findRepoRoot();
31
+ const opts = await askPluginOptions(root, values);
32
+ const files = newPlugin(opts);
33
+ prompts.log.success(`Created plugins/${opts.id} (${files.length} files)`);
34
+ if (!values['skip-install'])
35
+ install(root);
36
+ prompts.outro(`Next: describe ${opts.name}'s operations in plugins/${opts.id}/plugin.yaml, then run pnpm --filter ./plugins/${opts.id} test`);
37
+ return 0;
38
+ }
39
+ case 'build': {
40
+ const { values, positionals } = parseArgs({
41
+ args: rest,
42
+ allowPositionals: true,
43
+ options: { conditions: { type: 'string' } },
44
+ });
45
+ const dir = path.resolve(positionals[0] ?? '.');
46
+ await buildPlugin({ dir, conditions: values.conditions?.split(',').filter(Boolean) });
47
+ return 0;
48
+ }
49
+ case 'check': {
50
+ const { positionals } = parseArgs({ args: rest, allowPositionals: true, options: {} });
51
+ let dirs = positionals.map((d) => path.resolve(d));
52
+ if (!dirs.length) {
53
+ if (existsSync('manifest.json'))
54
+ dirs = [process.cwd()];
55
+ else {
56
+ const plugins = path.join(findRepoRoot(), 'plugins');
57
+ dirs = readdirSync(plugins)
58
+ .map((d) => path.join(plugins, d))
59
+ .filter((d) => existsSync(path.join(d, 'manifest.json')));
60
+ }
61
+ }
62
+ let failed = false;
63
+ for (const dir of dirs) {
64
+ const issues = checkPlugin(dir);
65
+ for (const issue of issues)
66
+ console.error(`${path.basename(dir)}: ${issue}`);
67
+ failed ||= issues.length > 0;
68
+ }
69
+ if (!failed)
70
+ console.log(`checked ${dirs.length} plugin(s)`);
71
+ return failed ? 1 : 0;
72
+ }
73
+ case 'repo': {
74
+ const { values, positionals } = parseArgs({
75
+ args: rest,
76
+ allowPositionals: true,
77
+ options: { repository: { type: 'string' }, out: { type: 'string' }, current: { type: 'string' } },
78
+ });
79
+ const tool = createRepoTool({ root: findRepoRoot(), ...values });
80
+ const step = positionals[0];
81
+ if (step === 'pack')
82
+ tool.pack();
83
+ else if (step === 'index')
84
+ tool.index();
85
+ else if (step === 'verify')
86
+ await tool.verify();
87
+ else if (step === 'publish')
88
+ tool.publish();
89
+ else {
90
+ console.error(USAGE);
91
+ return 2;
92
+ }
93
+ return 0;
94
+ }
95
+ default:
96
+ console.error(USAGE);
97
+ return command === undefined || command === '--help' || command === 'help' ? 0 : 2;
98
+ }
99
+ }
100
+ main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
101
+ console.error(err instanceof Error ? err.message : err);
102
+ process.exit(1);
103
+ });
104
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/create.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import { parseArgs } from 'node:util';
4
+ import { askPluginOptions, PLUGIN_FLAGS, prompts } from './prompts.js';
5
+ import { createRepo, install, newPlugin } from './scaffold.js';
6
+ /**
7
+ * `pnpm create @synoikia/plugin <dir>`: a new plugin repository, then its first plugin.
8
+ */
9
+ const USAGE = `create-plugin <dir> [--repository owner/name] [--release] [--claude] [--no-plugin]
10
+ [plugin flags, see: synoikia-plugin new]`;
11
+ async function main(argv) {
12
+ const { values, positionals } = parseArgs({
13
+ args: argv,
14
+ allowPositionals: true,
15
+ options: {
16
+ ...PLUGIN_FLAGS,
17
+ repository: { type: 'string' },
18
+ release: { type: 'boolean' },
19
+ claude: { type: 'boolean' },
20
+ 'no-plugin': { type: 'boolean' },
21
+ help: { type: 'boolean', short: 'h' },
22
+ },
23
+ });
24
+ if (values.help || positionals.length !== 1) {
25
+ console.error(USAGE);
26
+ return values.help ? 0 : 2;
27
+ }
28
+ const dir = path.resolve(positionals[0]);
29
+ prompts.intro(`Creating a Synoikia plugin repository in ${dir}`);
30
+ const ask = !values.yes && process.stdin.isTTY === true;
31
+ const release = values.release ??
32
+ (ask
33
+ ? await prompts.confirm({ message: 'Add the signed-release workflow (publishes a plugin repository)?' })
34
+ : false);
35
+ const claude = values.claude ??
36
+ (ask
37
+ ? await prompts.confirm({ message: 'Add the Claude Code setup (CLAUDE.md, skills, security reviewer)?' })
38
+ : false);
39
+ if (prompts.isCancel(release) || prompts.isCancel(claude))
40
+ return 1;
41
+ createRepo({ dir, repository: values.repository, release, claude });
42
+ if (!values['no-plugin']) {
43
+ const opts = await askPluginOptions(dir, values);
44
+ newPlugin(opts);
45
+ prompts.log.success(`Created plugins/${opts.id}`);
46
+ }
47
+ if (!values['skip-install'])
48
+ install(dir);
49
+ prompts.outro(`Done. cd ${path.relative(process.cwd(), dir) || '.'} && pnpm test`);
50
+ return 0;
51
+ }
52
+ main(process.argv.slice(2)).then((code) => process.exit(code), (err) => {
53
+ console.error(err instanceof Error ? err.message : err);
54
+ process.exit(1);
55
+ });
56
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * How a plugin's data files become part of its bundle: `plugin.yaml` (and any other `.yaml`) is
3
+ * parsed at build time and inlined as JSON, and `.md` files are inlined as text. Nothing is read
4
+ * from disk at runtime, where the permission model would refuse it anyway. `plugin.yaml` is also
5
+ * validated against the SDK schema, so a malformed one fails the build, not the install.
6
+ */
7
+ /** Parses YAML as plain data: no custom tags, and aliases capped against expansion bombs. */
8
+ export declare function parseYamlFile(file: string): unknown;
9
+ /** Parses a YAML file; a file named `plugin.yaml` must also be valid plugin settings. */
10
+ export declare function loadYamlModule(file: string): unknown;
11
+ /** The ES module source a data file compiles to. */
12
+ export declare function moduleSource(file: string, contents?: string): string | undefined;
package/dist/files.js ADDED
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parsePluginSettings } from '@synoikia/plugin-sdk';
4
+ import { parse } from 'yaml';
5
+ /**
6
+ * How a plugin's data files become part of its bundle: `plugin.yaml` (and any other `.yaml`) is
7
+ * parsed at build time and inlined as JSON, and `.md` files are inlined as text. Nothing is read
8
+ * from disk at runtime, where the permission model would refuse it anyway. `plugin.yaml` is also
9
+ * validated against the SDK schema, so a malformed one fails the build, not the install.
10
+ */
11
+ /** Parses YAML as plain data: no custom tags, and aliases capped against expansion bombs. */
12
+ export function parseYamlFile(file) {
13
+ const text = readFileSync(file, 'utf8');
14
+ try {
15
+ return parse(text, { maxAliasCount: 100 }) ?? {};
16
+ }
17
+ catch (err) {
18
+ throw new Error(`${file}: ${err.message}`, { cause: err });
19
+ }
20
+ }
21
+ /** Parses a YAML file; a file named `plugin.yaml` must also be valid plugin settings. */
22
+ export function loadYamlModule(file) {
23
+ const data = parseYamlFile(file);
24
+ if (path.basename(file) === 'plugin.yaml') {
25
+ try {
26
+ parsePluginSettings(data);
27
+ }
28
+ catch (err) {
29
+ throw new Error(`${file} is not valid plugin settings:\n${err.message}`, { cause: err });
30
+ }
31
+ }
32
+ return data;
33
+ }
34
+ /** The ES module source a data file compiles to. */
35
+ export function moduleSource(file, contents) {
36
+ if (/\.ya?ml$/.test(file))
37
+ return `export default ${JSON.stringify(loadYamlModule(file))};\n`;
38
+ if (file.endsWith('.md'))
39
+ return `export default ${JSON.stringify(contents ?? readFileSync(file, 'utf8'))};\n`;
40
+ return undefined;
41
+ }
42
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAE7B;;;;;GAKG;AAEH,6FAA6F;AAC7F,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,KAAK,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,KAAM,GAAa,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,aAAa,EAAE,CAAC;QAC1C,IAAI,CAAC;YACH,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,mCAAoC,GAAa,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACtG,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,QAAiB;IAC1D,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,kBAAkB,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;IAC9F,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,kBAAkB,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC;IAC/G,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,10 @@
1
+ export { buildPlugin } from './build.js';
2
+ export type { BuildOptions } from './build.js';
3
+ export { checkPlugin } from './check.js';
4
+ export { loadYamlModule, moduleSource, parseYamlFile } from './files.js';
5
+ export { createRepoTool, repositoryFromRemote } from './repo.js';
6
+ export type { RepoOptions } from './repo.js';
7
+ export { findRepoRoot } from './repo-root.js';
8
+ export { ARCHETYPES, AUTH_KINDS, createRepo, fill, manifestFor, newPlugin, renderTemplate, validateId, validateNamespace, } from './scaffold.js';
9
+ export type { Archetype, AuthKind, CreateRepoOptions, NewPluginOptions } from './scaffold.js';
10
+ export { pluginFiles } from './vitest.js';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { buildPlugin } from './build.js';
2
+ export { checkPlugin } from './check.js';
3
+ export { loadYamlModule, moduleSource, parseYamlFile } from './files.js';
4
+ export { createRepoTool, repositoryFromRemote } from './repo.js';
5
+ export { findRepoRoot } from './repo-root.js';
6
+ export { ARCHETYPES, AUTH_KINDS, createRepo, fill, manifestFor, newPlugin, renderTemplate, validateId, validateNamespace, } from './scaffold.js';
7
+ export { pluginFiles } from './vitest.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,UAAU,EACV,UAAU,EACV,UAAU,EACV,IAAI,EACJ,WAAW,EACX,SAAS,EACT,cAAc,EACd,UAAU,EACV,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,46 @@
1
+ import * as p from '@clack/prompts';
2
+ import type { NewPluginOptions } from './scaffold.js';
3
+ /** Flags shared by `create-plugin` and `synoikia-plugin new`. */
4
+ export declare const PLUGIN_FLAGS: {
5
+ readonly id: {
6
+ readonly type: "string";
7
+ };
8
+ readonly name: {
9
+ readonly type: "string";
10
+ };
11
+ readonly namespace: {
12
+ readonly type: "string";
13
+ };
14
+ readonly description: {
15
+ readonly type: "string";
16
+ };
17
+ readonly archetype: {
18
+ readonly type: "string";
19
+ };
20
+ readonly auth: {
21
+ readonly type: "string";
22
+ };
23
+ readonly 'api-key-header': {
24
+ readonly type: "string";
25
+ };
26
+ readonly yes: {
27
+ readonly type: "boolean";
28
+ readonly short: "y";
29
+ };
30
+ readonly 'skip-install': {
31
+ readonly type: "boolean";
32
+ };
33
+ };
34
+ export interface PluginFlags {
35
+ id?: string;
36
+ name?: string;
37
+ namespace?: string;
38
+ description?: string;
39
+ archetype?: string;
40
+ auth?: string;
41
+ 'api-key-header'?: string;
42
+ yes?: boolean;
43
+ }
44
+ /** Fills in plugin options from flags, asking for what's missing when run in a terminal. */
45
+ export declare function askPluginOptions(root: string, flags: PluginFlags): Promise<NewPluginOptions>;
46
+ export { p as prompts };
@@ -0,0 +1,112 @@
1
+ import * as p from '@clack/prompts';
2
+ import { ARCHETYPES, AUTH_KINDS, camelCase, validateDescription, validateId, validateName, validateNamespace, } from './scaffold.js';
3
+ /** Flags shared by `create-plugin` and `synoikia-plugin new`. */
4
+ export const PLUGIN_FLAGS = {
5
+ id: { type: 'string' },
6
+ name: { type: 'string' },
7
+ namespace: { type: 'string' },
8
+ description: { type: 'string' },
9
+ archetype: { type: 'string' },
10
+ auth: { type: 'string' },
11
+ 'api-key-header': { type: 'string' },
12
+ yes: { type: 'boolean', short: 'y' },
13
+ 'skip-install': { type: 'boolean' },
14
+ };
15
+ const interactive = (flags) => !flags.yes && process.stdin.isTTY === true;
16
+ function done(value) {
17
+ if (typeof value === 'symbol') {
18
+ p.cancel('Cancelled');
19
+ process.exit(1);
20
+ }
21
+ return value;
22
+ }
23
+ const titleCase = (id) => id
24
+ .split('-')
25
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
26
+ .join(' ');
27
+ /** Fills in plugin options from flags, asking for what's missing when run in a terminal. */
28
+ export async function askPluginOptions(root, flags) {
29
+ const ask = interactive(flags);
30
+ const need = (what) => {
31
+ throw new Error(`--${what} is required when not running interactively`);
32
+ };
33
+ const check = (problem) => {
34
+ if (problem)
35
+ throw new Error(problem);
36
+ };
37
+ let id = flags.id;
38
+ if (id === undefined) {
39
+ if (!ask)
40
+ need('id');
41
+ id = done(await p.text({
42
+ message: 'Plugin id (lowercase, the directory name)',
43
+ placeholder: 'acme',
44
+ validate: (v) => validateId(v ?? '', root),
45
+ }));
46
+ }
47
+ check(validateId(id, root));
48
+ let name = flags.name;
49
+ if (name === undefined) {
50
+ name = ask
51
+ ? done(await p.text({
52
+ message: 'Display name',
53
+ initialValue: titleCase(id),
54
+ validate: (v) => (v ? undefined : 'Required'),
55
+ }))
56
+ : titleCase(id);
57
+ }
58
+ check(validateName(name));
59
+ if (flags.description !== undefined)
60
+ check(validateDescription(flags.description));
61
+ let namespace = flags.namespace;
62
+ if (namespace === undefined) {
63
+ namespace = ask
64
+ ? done(await p.text({
65
+ message: 'Sandbox namespace (what code calls: <namespace>.call(...))',
66
+ initialValue: camelCase(id),
67
+ validate: (v) => validateNamespace(v ?? ''),
68
+ }))
69
+ : camelCase(id);
70
+ }
71
+ const nsProblem = validateNamespace(namespace);
72
+ if (nsProblem)
73
+ throw new Error(`Namespace "${namespace}": ${nsProblem} (pass --namespace)`);
74
+ let archetype = flags.archetype;
75
+ if (archetype === undefined) {
76
+ if (!ask)
77
+ need('archetype');
78
+ archetype = done(await p.select({
79
+ message: 'What kind of upstream API?',
80
+ options: Object.entries(ARCHETYPES).map(([value, hint]) => ({ value: value, label: value, hint })),
81
+ }));
82
+ }
83
+ if (!(archetype in ARCHETYPES))
84
+ throw new Error(`--archetype must be one of ${Object.keys(ARCHETYPES).join(', ')}`);
85
+ let auth = flags.auth;
86
+ if (auth === undefined) {
87
+ if (!ask)
88
+ need('auth');
89
+ auth = done(await p.select({
90
+ message: 'How does it authenticate?',
91
+ options: Object.entries(AUTH_KINDS).map(([value, hint]) => ({ value: value, label: value, hint })),
92
+ }));
93
+ }
94
+ if (!(auth in AUTH_KINDS))
95
+ throw new Error(`--auth must be one of ${Object.keys(AUTH_KINDS).join(', ')}`);
96
+ let apiKeyHeader = flags['api-key-header'];
97
+ if (auth === 'api-key' && apiKeyHeader === undefined && ask) {
98
+ apiKeyHeader = done(await p.text({ message: 'API key header', initialValue: 'X-Api-Key' }));
99
+ }
100
+ return {
101
+ root,
102
+ id: id,
103
+ name: name,
104
+ namespace: namespace,
105
+ ...(flags.description ? { description: flags.description } : {}),
106
+ archetype: archetype,
107
+ auth: auth,
108
+ ...(apiKeyHeader ? { apiKeyHeader } : {}),
109
+ };
110
+ }
111
+ export { p as prompts };
112
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /** The plugin repository root: the nearest directory up from `start` with a `pnpm-workspace.yaml`. */
2
+ export declare function findRepoRoot(start?: string): string;
3
+ /** This package's directory. */
4
+ export declare const PACKAGE_DIR: string;
5
+ export declare const TEMPLATES_DIR: string;
6
+ /**
7
+ * The range generated packages should use for one of this tool's own dependencies: as published
8
+ * (`pnpm pack` turns `workspace:` ranges into real ones), else from the installed version.
9
+ */
10
+ export declare function dependencyRange(name: string): string;