@kybernesis/arp-create-adapter 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kybernesis AI
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,45 @@
1
+ # @kybernesis/arp-create-adapter
2
+
3
+ Scaffolds a conformance-passing ARP framework adapter in TypeScript or Python.
4
+
5
+ ## Install + run
6
+
7
+ ```bash
8
+ npx @kybernesis/arp-create-adapter \
9
+ --framework my-framework \
10
+ --language ts \
11
+ --out ./adapters/my-framework
12
+ ```
13
+
14
+ Generates:
15
+ - `package.json` (or `pyproject.toml`) with correct ARP dependencies
16
+ - `src/` / `arp_adapter_<slug>/` skeleton wiring the 5 ARP integration points
17
+ - A placeholder `MyFrameworkLike` structural type covering the public extension surface you need to depend on (replace with the real framework's shape)
18
+ - `tests/conformance.test.ts` (or `tests/test_conformance.py`) wired for the `@kybernesis/arp-testkit` audit
19
+ - `README.md` + `MIGRATION.md` ready to publish
20
+
21
+ ## Why a structural-type scaffold?
22
+
23
+ Per Phase-6 Rule 2, adapters **must not fork framework source** — they wrap a documented public extension API. The scaffold picks that up explicitly: you describe your framework as a protocol, wire your framework's real hooks to that protocol, and the adapter's logic is portable across framework versions.
24
+
25
+ ## Programmatic API
26
+
27
+ ```ts
28
+ import { scaffoldAdapter } from '@kybernesis/arp-create-adapter';
29
+
30
+ await scaffoldAdapter({
31
+ framework: 'my-framework',
32
+ displayName: 'My Framework',
33
+ language: 'ts',
34
+ out: './adapters/my-framework',
35
+ });
36
+ ```
37
+
38
+ ## After scaffolding
39
+
40
+ 1. Replace the `{FrameworkName}Like` placeholder type with your framework's real public interface.
41
+ 2. Wire the ARP hooks (`agent.check`, `agent.egress`, etc.) to the framework's actual hook names.
42
+ 3. Run the shipped conformance test; add a real integration test that pairs the generated adapter with the ARP reference agents in `examples/`.
43
+ 4. See [`docs/ARP-adapter-authoring-guide.md`](../../docs/ARP-adapter-authoring-guide.md) for the full contract + anti-patterns.
44
+
45
+ Phase-6 in the ARP monorepo validates all five required adapters with this same scaffold structure.
package/dist/cli.cjs ADDED
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ var commander = require('commander');
5
+ var path = require('path');
6
+ var fs = require('fs');
7
+ var url = require('url');
8
+ var Handlebars = require('handlebars');
9
+
10
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
14
+
15
+ var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
16
+ var __dirname$1 = path.dirname(__filename$1);
17
+ var DEFAULT_TEMPLATES_DIR = path.resolve(__dirname$1, "..", "templates");
18
+ async function scaffoldAdapter(options) {
19
+ const framework = options.framework.trim().toLowerCase();
20
+ if (!/^[a-z][a-z0-9-]*$/.test(framework)) {
21
+ throw new Error(
22
+ `framework slug "${options.framework}" must match /^[a-z][a-z0-9-]*$/`
23
+ );
24
+ }
25
+ const displayName = options.displayName ?? toDisplayName(framework);
26
+ const arpVersion = options.arpVersion ?? "^0.1.0";
27
+ const templateRoot = options.templatesDir ? path.resolve(options.templatesDir, options.language) : path.join(DEFAULT_TEMPLATES_DIR, options.language);
28
+ if (!fs.existsSync(templateRoot)) {
29
+ throw new Error(`template directory missing: ${templateRoot}`);
30
+ }
31
+ const context = {
32
+ framework,
33
+ frameworkPascal: toPascal(framework),
34
+ frameworkCamel: toCamel(framework),
35
+ frameworkSnake: framework.replace(/-/g, "_"),
36
+ frameworkUpper: framework.toUpperCase().replace(/-/g, "_"),
37
+ displayName,
38
+ arpVersion,
39
+ /** Best-effort Python-compatible version range — strip leading `^`. */
40
+ pythonArpVersion: arpVersion.startsWith("^") ? `>=${arpVersion.slice(1)}` : arpVersion,
41
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
42
+ };
43
+ const created = [];
44
+ const skipped = [];
45
+ for (const rel of walk(templateRoot)) {
46
+ const src = path.join(templateRoot, rel);
47
+ const dst = path.join(options.out, renderFilename(rel, context));
48
+ if (fs.existsSync(dst) && !options.force) {
49
+ skipped.push(dst);
50
+ continue;
51
+ }
52
+ const raw = fs.readFileSync(src, "utf8");
53
+ const rendered = rel.endsWith(".hbs") ? Handlebars__default.default.compile(raw, { noEscape: true })(context) : raw;
54
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
55
+ fs.writeFileSync(dst, rendered);
56
+ created.push(dst);
57
+ }
58
+ const summary = [
59
+ `Scaffolded @kybernesis/arp-adapter-${framework} (${options.language}) at ${options.out}.`,
60
+ `Created ${created.length} file(s), skipped ${skipped.length}.`,
61
+ "",
62
+ "Next steps:",
63
+ ` 1. cd ${path.relative(process.cwd(), options.out) || options.out}`,
64
+ options.language === "ts" ? " 2. pnpm install" : " 2. uv sync # or: python -m pip install -e .",
65
+ " 3. Implement src/* to map your framework's public extension points to ArpAgent.",
66
+ " 4. Run the conformance test: pnpm test (or: uv run pytest)",
67
+ " 5. See docs/ARP-adapter-authoring-guide.md for the full contract."
68
+ ].join("\n");
69
+ return { createdFiles: created, skippedFiles: skipped, summary };
70
+ }
71
+ function walk(dir) {
72
+ const out = [];
73
+ function recurse(sub) {
74
+ const abs = path.join(dir, sub);
75
+ for (const entry of fs.readdirSync(abs)) {
76
+ const fullRel = sub ? path.join(sub, entry) : entry;
77
+ const fullAbs = path.join(dir, fullRel);
78
+ const st = fs.statSync(fullAbs);
79
+ if (st.isDirectory()) recurse(fullRel);
80
+ else out.push(fullRel);
81
+ }
82
+ }
83
+ recurse("");
84
+ return out;
85
+ }
86
+ function renderFilename(rel, ctx) {
87
+ const withoutHbs = rel.endsWith(".hbs") ? rel.slice(0, -".hbs".length) : rel;
88
+ return Handlebars__default.default.compile(withoutHbs, { noEscape: true })(ctx);
89
+ }
90
+ function toPascal(slug) {
91
+ return slug.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
92
+ }
93
+ function toCamel(slug) {
94
+ const pascal = toPascal(slug);
95
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
96
+ }
97
+ function toDisplayName(slug) {
98
+ return slug.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
99
+ }
100
+
101
+ // src/cli.ts
102
+ var program = new commander.Command();
103
+ program.name("create-arp-adapter").description("Scaffold a conformance-passing ARP framework adapter.").requiredOption("-f, --framework <slug>", 'Framework slug (kebab-case), e.g. "my-framework"').option("-n, --name <name>", 'Display name (e.g. "My Framework"). Defaults to title-cased slug.').requiredOption("-l, --language <ts|python>", "Target language", "ts").option("-o, --out <path>", "Destination directory (default ./adapters/<slug>)").option("--arp-version <range>", "ARP spec/SDK version range", "^0.1.0").option("--force", "Overwrite existing files", false).action(async (opts) => {
104
+ if (opts.language !== "ts" && opts.language !== "python") {
105
+ console.error(`--language must be "ts" or "python", got "${opts.language}"`);
106
+ process.exit(1);
107
+ }
108
+ const out = path.resolve(process.cwd(), opts.out ?? `./adapters/${opts.framework}`);
109
+ try {
110
+ const result = await scaffoldAdapter({
111
+ framework: opts.framework,
112
+ ...opts.name ? { displayName: opts.name } : {},
113
+ language: opts.language,
114
+ out,
115
+ arpVersion: opts.arpVersion,
116
+ force: opts.force
117
+ });
118
+ console.log(result.summary);
119
+ if (result.skippedFiles.length > 0) {
120
+ console.log("\nSkipped (use --force to overwrite):");
121
+ for (const f of result.skippedFiles) {
122
+ console.log(` ${f}`);
123
+ }
124
+ }
125
+ } catch (err) {
126
+ console.error("create-arp-adapter failed:", err.message);
127
+ process.exit(2);
128
+ }
129
+ });
130
+ program.parseAsync(process.argv).catch((err) => {
131
+ console.error(err);
132
+ process.exit(3);
133
+ });
134
+ //# sourceMappingURL=cli.cjs.map
135
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scaffold.ts","../src/cli.ts"],"names":["__filename","fileURLToPath","__dirname","dirname","resolve","join","existsSync","readFileSync","Handlebars","mkdirSync","writeFileSync","relative","readdirSync","statSync","Command"],"mappings":"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","file":"cli.cjs","sourcesContent":["/**\n * Core scaffolder. Reads a template directory, renders each file through\n * Handlebars, and writes to the target path.\n *\n * Templates live under `packages/create-adapter/templates/<lang>/` and the\n * scaffolder resolves them relative to the compiled `dist/` via\n * `fileURLToPath(import.meta.url)`. Consumers using the programmatic API\n * in a non-standard layout can pass `templatesDir` directly.\n */\n\nimport { readdirSync, readFileSync, statSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport Handlebars from 'handlebars';\n\nexport type SupportedLanguage = 'ts' | 'python';\n\nexport interface ScaffoldOptions {\n /** Framework slug (kebab-case). Used in file paths + imports. */\n framework: string;\n /** Human-readable name (e.g. \"KyberBot\"). Used in README, error strings. */\n displayName?: string;\n /** Target language. */\n language: SupportedLanguage;\n /** Destination directory — will be created if missing. */\n out: string;\n /** Override the template root. Default: bundled templates. */\n templatesDir?: string;\n /** ARP spec version pinned in the generated package.json. */\n arpVersion?: string;\n /** When true, overwrite existing files. Default false. */\n force?: boolean;\n}\n\nexport interface ScaffoldResult {\n createdFiles: string[];\n skippedFiles: string[];\n summary: string;\n}\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst DEFAULT_TEMPLATES_DIR = resolve(__dirname, '..', 'templates');\n\nexport async function scaffoldAdapter(\n options: ScaffoldOptions,\n): Promise<ScaffoldResult> {\n const framework = options.framework.trim().toLowerCase();\n if (!/^[a-z][a-z0-9-]*$/.test(framework)) {\n throw new Error(\n `framework slug \"${options.framework}\" must match /^[a-z][a-z0-9-]*$/`,\n );\n }\n const displayName = options.displayName ?? toDisplayName(framework);\n const arpVersion = options.arpVersion ?? '^0.1.0';\n\n const templateRoot = options.templatesDir\n ? resolve(options.templatesDir, options.language)\n : join(DEFAULT_TEMPLATES_DIR, options.language);\n if (!existsSync(templateRoot)) {\n throw new Error(`template directory missing: ${templateRoot}`);\n }\n\n const context = {\n framework,\n frameworkPascal: toPascal(framework),\n frameworkCamel: toCamel(framework),\n frameworkSnake: framework.replace(/-/g, '_'),\n frameworkUpper: framework.toUpperCase().replace(/-/g, '_'),\n displayName,\n arpVersion,\n /** Best-effort Python-compatible version range — strip leading `^`. */\n pythonArpVersion: arpVersion.startsWith('^')\n ? `>=${arpVersion.slice(1)}`\n : arpVersion,\n generatedAt: new Date().toISOString(),\n };\n\n const created: string[] = [];\n const skipped: string[] = [];\n\n for (const rel of walk(templateRoot)) {\n const src = join(templateRoot, rel);\n const dst = join(options.out, renderFilename(rel, context));\n if (existsSync(dst) && !options.force) {\n skipped.push(dst);\n continue;\n }\n const raw = readFileSync(src, 'utf8');\n const rendered = rel.endsWith('.hbs')\n ? Handlebars.compile(raw, { noEscape: true })(context)\n : raw;\n mkdirSync(dirname(dst), { recursive: true });\n writeFileSync(dst, rendered);\n created.push(dst);\n }\n\n const summary = [\n `Scaffolded @kybernesis/arp-adapter-${framework} (${options.language}) at ${options.out}.`,\n `Created ${created.length} file(s), skipped ${skipped.length}.`,\n '',\n 'Next steps:',\n ` 1. cd ${relative(process.cwd(), options.out) || options.out}`,\n options.language === 'ts'\n ? ' 2. pnpm install'\n : ' 2. uv sync # or: python -m pip install -e .',\n ' 3. Implement src/* to map your framework\\'s public extension points to ArpAgent.',\n ' 4. Run the conformance test: pnpm test (or: uv run pytest)',\n ' 5. See docs/ARP-adapter-authoring-guide.md for the full contract.',\n ].join('\\n');\n\n return { createdFiles: created, skippedFiles: skipped, summary };\n}\n\nfunction walk(dir: string): string[] {\n const out: string[] = [];\n function recurse(sub: string) {\n const abs = join(dir, sub);\n for (const entry of readdirSync(abs)) {\n const fullRel = sub ? join(sub, entry) : entry;\n const fullAbs = join(dir, fullRel);\n const st = statSync(fullAbs);\n if (st.isDirectory()) recurse(fullRel);\n else out.push(fullRel);\n }\n }\n recurse('');\n return out;\n}\n\nfunction renderFilename(rel: string, ctx: Record<string, string>): string {\n // Trim trailing `.hbs` and run Handlebars on the path (for filenames\n // like `src/{{framework}}.ts.hbs`).\n const withoutHbs = rel.endsWith('.hbs') ? rel.slice(0, -'.hbs'.length) : rel;\n return Handlebars.compile(withoutHbs, { noEscape: true })(ctx);\n}\n\nfunction toPascal(slug: string): string {\n return slug\n .split('-')\n .filter(Boolean)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join('');\n}\n\nfunction toCamel(slug: string): string {\n const pascal = toPascal(slug);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nfunction toDisplayName(slug: string): string {\n return slug\n .split('-')\n .filter(Boolean)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(' ');\n}\n","#!/usr/bin/env node\n/**\n * `create-arp-adapter` CLI.\n *\n * npx @kybernesis/arp-create-adapter \\\n * --framework my-framework \\\n * --language ts \\\n * --out ./adapters/my-framework\n */\n\nimport { Command } from 'commander';\nimport { resolve } from 'node:path';\nimport { scaffoldAdapter, type SupportedLanguage } from './scaffold.js';\n\nconst program = new Command();\n\nprogram\n .name('create-arp-adapter')\n .description('Scaffold a conformance-passing ARP framework adapter.')\n .requiredOption('-f, --framework <slug>', 'Framework slug (kebab-case), e.g. \"my-framework\"')\n .option('-n, --name <name>', 'Display name (e.g. \"My Framework\"). Defaults to title-cased slug.')\n .requiredOption('-l, --language <ts|python>', 'Target language', 'ts')\n .option('-o, --out <path>', 'Destination directory (default ./adapters/<slug>)')\n .option('--arp-version <range>', 'ARP spec/SDK version range', '^0.1.0')\n .option('--force', 'Overwrite existing files', false)\n .action(async (opts: {\n framework: string;\n name?: string;\n language: string;\n out?: string;\n arpVersion: string;\n force: boolean;\n }) => {\n if (opts.language !== 'ts' && opts.language !== 'python') {\n // eslint-disable-next-line no-console\n console.error(`--language must be \"ts\" or \"python\", got \"${opts.language}\"`);\n process.exit(1);\n }\n const out = resolve(process.cwd(), opts.out ?? `./adapters/${opts.framework}`);\n try {\n const result = await scaffoldAdapter({\n framework: opts.framework,\n ...(opts.name ? { displayName: opts.name } : {}),\n language: opts.language as SupportedLanguage,\n out,\n arpVersion: opts.arpVersion,\n force: opts.force,\n });\n // eslint-disable-next-line no-console\n console.log(result.summary);\n if (result.skippedFiles.length > 0) {\n // eslint-disable-next-line no-console\n console.log('\\nSkipped (use --force to overwrite):');\n for (const f of result.skippedFiles) {\n // eslint-disable-next-line no-console\n console.log(` ${f}`);\n }\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('create-arp-adapter failed:', (err as Error).message);\n process.exit(2);\n }\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n // eslint-disable-next-line no-console\n console.error(err);\n process.exit(3);\n});\n"]}
package/dist/cli.js ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { dirname, resolve, join, relative } from 'path';
4
+ import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, statSync } from 'fs';
5
+ import { fileURLToPath } from 'url';
6
+ import Handlebars from 'handlebars';
7
+
8
+ var __filename$1 = fileURLToPath(import.meta.url);
9
+ var __dirname$1 = dirname(__filename$1);
10
+ var DEFAULT_TEMPLATES_DIR = resolve(__dirname$1, "..", "templates");
11
+ async function scaffoldAdapter(options) {
12
+ const framework = options.framework.trim().toLowerCase();
13
+ if (!/^[a-z][a-z0-9-]*$/.test(framework)) {
14
+ throw new Error(
15
+ `framework slug "${options.framework}" must match /^[a-z][a-z0-9-]*$/`
16
+ );
17
+ }
18
+ const displayName = options.displayName ?? toDisplayName(framework);
19
+ const arpVersion = options.arpVersion ?? "^0.1.0";
20
+ const templateRoot = options.templatesDir ? resolve(options.templatesDir, options.language) : join(DEFAULT_TEMPLATES_DIR, options.language);
21
+ if (!existsSync(templateRoot)) {
22
+ throw new Error(`template directory missing: ${templateRoot}`);
23
+ }
24
+ const context = {
25
+ framework,
26
+ frameworkPascal: toPascal(framework),
27
+ frameworkCamel: toCamel(framework),
28
+ frameworkSnake: framework.replace(/-/g, "_"),
29
+ frameworkUpper: framework.toUpperCase().replace(/-/g, "_"),
30
+ displayName,
31
+ arpVersion,
32
+ /** Best-effort Python-compatible version range — strip leading `^`. */
33
+ pythonArpVersion: arpVersion.startsWith("^") ? `>=${arpVersion.slice(1)}` : arpVersion,
34
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
35
+ };
36
+ const created = [];
37
+ const skipped = [];
38
+ for (const rel of walk(templateRoot)) {
39
+ const src = join(templateRoot, rel);
40
+ const dst = join(options.out, renderFilename(rel, context));
41
+ if (existsSync(dst) && !options.force) {
42
+ skipped.push(dst);
43
+ continue;
44
+ }
45
+ const raw = readFileSync(src, "utf8");
46
+ const rendered = rel.endsWith(".hbs") ? Handlebars.compile(raw, { noEscape: true })(context) : raw;
47
+ mkdirSync(dirname(dst), { recursive: true });
48
+ writeFileSync(dst, rendered);
49
+ created.push(dst);
50
+ }
51
+ const summary = [
52
+ `Scaffolded @kybernesis/arp-adapter-${framework} (${options.language}) at ${options.out}.`,
53
+ `Created ${created.length} file(s), skipped ${skipped.length}.`,
54
+ "",
55
+ "Next steps:",
56
+ ` 1. cd ${relative(process.cwd(), options.out) || options.out}`,
57
+ options.language === "ts" ? " 2. pnpm install" : " 2. uv sync # or: python -m pip install -e .",
58
+ " 3. Implement src/* to map your framework's public extension points to ArpAgent.",
59
+ " 4. Run the conformance test: pnpm test (or: uv run pytest)",
60
+ " 5. See docs/ARP-adapter-authoring-guide.md for the full contract."
61
+ ].join("\n");
62
+ return { createdFiles: created, skippedFiles: skipped, summary };
63
+ }
64
+ function walk(dir) {
65
+ const out = [];
66
+ function recurse(sub) {
67
+ const abs = join(dir, sub);
68
+ for (const entry of readdirSync(abs)) {
69
+ const fullRel = sub ? join(sub, entry) : entry;
70
+ const fullAbs = join(dir, fullRel);
71
+ const st = statSync(fullAbs);
72
+ if (st.isDirectory()) recurse(fullRel);
73
+ else out.push(fullRel);
74
+ }
75
+ }
76
+ recurse("");
77
+ return out;
78
+ }
79
+ function renderFilename(rel, ctx) {
80
+ const withoutHbs = rel.endsWith(".hbs") ? rel.slice(0, -".hbs".length) : rel;
81
+ return Handlebars.compile(withoutHbs, { noEscape: true })(ctx);
82
+ }
83
+ function toPascal(slug) {
84
+ return slug.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
85
+ }
86
+ function toCamel(slug) {
87
+ const pascal = toPascal(slug);
88
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
89
+ }
90
+ function toDisplayName(slug) {
91
+ return slug.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
92
+ }
93
+
94
+ // src/cli.ts
95
+ var program = new Command();
96
+ program.name("create-arp-adapter").description("Scaffold a conformance-passing ARP framework adapter.").requiredOption("-f, --framework <slug>", 'Framework slug (kebab-case), e.g. "my-framework"').option("-n, --name <name>", 'Display name (e.g. "My Framework"). Defaults to title-cased slug.').requiredOption("-l, --language <ts|python>", "Target language", "ts").option("-o, --out <path>", "Destination directory (default ./adapters/<slug>)").option("--arp-version <range>", "ARP spec/SDK version range", "^0.1.0").option("--force", "Overwrite existing files", false).action(async (opts) => {
97
+ if (opts.language !== "ts" && opts.language !== "python") {
98
+ console.error(`--language must be "ts" or "python", got "${opts.language}"`);
99
+ process.exit(1);
100
+ }
101
+ const out = resolve(process.cwd(), opts.out ?? `./adapters/${opts.framework}`);
102
+ try {
103
+ const result = await scaffoldAdapter({
104
+ framework: opts.framework,
105
+ ...opts.name ? { displayName: opts.name } : {},
106
+ language: opts.language,
107
+ out,
108
+ arpVersion: opts.arpVersion,
109
+ force: opts.force
110
+ });
111
+ console.log(result.summary);
112
+ if (result.skippedFiles.length > 0) {
113
+ console.log("\nSkipped (use --force to overwrite):");
114
+ for (const f of result.skippedFiles) {
115
+ console.log(` ${f}`);
116
+ }
117
+ }
118
+ } catch (err) {
119
+ console.error("create-arp-adapter failed:", err.message);
120
+ process.exit(2);
121
+ }
122
+ });
123
+ program.parseAsync(process.argv).catch((err) => {
124
+ console.error(err);
125
+ process.exit(3);
126
+ });
127
+ //# sourceMappingURL=cli.js.map
128
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scaffold.ts","../src/cli.ts"],"names":["__filename","__dirname","resolve"],"mappings":"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","file":"cli.js","sourcesContent":["/**\n * Core scaffolder. Reads a template directory, renders each file through\n * Handlebars, and writes to the target path.\n *\n * Templates live under `packages/create-adapter/templates/<lang>/` and the\n * scaffolder resolves them relative to the compiled `dist/` via\n * `fileURLToPath(import.meta.url)`. Consumers using the programmatic API\n * in a non-standard layout can pass `templatesDir` directly.\n */\n\nimport { readdirSync, readFileSync, statSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport Handlebars from 'handlebars';\n\nexport type SupportedLanguage = 'ts' | 'python';\n\nexport interface ScaffoldOptions {\n /** Framework slug (kebab-case). Used in file paths + imports. */\n framework: string;\n /** Human-readable name (e.g. \"KyberBot\"). Used in README, error strings. */\n displayName?: string;\n /** Target language. */\n language: SupportedLanguage;\n /** Destination directory — will be created if missing. */\n out: string;\n /** Override the template root. Default: bundled templates. */\n templatesDir?: string;\n /** ARP spec version pinned in the generated package.json. */\n arpVersion?: string;\n /** When true, overwrite existing files. Default false. */\n force?: boolean;\n}\n\nexport interface ScaffoldResult {\n createdFiles: string[];\n skippedFiles: string[];\n summary: string;\n}\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst DEFAULT_TEMPLATES_DIR = resolve(__dirname, '..', 'templates');\n\nexport async function scaffoldAdapter(\n options: ScaffoldOptions,\n): Promise<ScaffoldResult> {\n const framework = options.framework.trim().toLowerCase();\n if (!/^[a-z][a-z0-9-]*$/.test(framework)) {\n throw new Error(\n `framework slug \"${options.framework}\" must match /^[a-z][a-z0-9-]*$/`,\n );\n }\n const displayName = options.displayName ?? toDisplayName(framework);\n const arpVersion = options.arpVersion ?? '^0.1.0';\n\n const templateRoot = options.templatesDir\n ? resolve(options.templatesDir, options.language)\n : join(DEFAULT_TEMPLATES_DIR, options.language);\n if (!existsSync(templateRoot)) {\n throw new Error(`template directory missing: ${templateRoot}`);\n }\n\n const context = {\n framework,\n frameworkPascal: toPascal(framework),\n frameworkCamel: toCamel(framework),\n frameworkSnake: framework.replace(/-/g, '_'),\n frameworkUpper: framework.toUpperCase().replace(/-/g, '_'),\n displayName,\n arpVersion,\n /** Best-effort Python-compatible version range — strip leading `^`. */\n pythonArpVersion: arpVersion.startsWith('^')\n ? `>=${arpVersion.slice(1)}`\n : arpVersion,\n generatedAt: new Date().toISOString(),\n };\n\n const created: string[] = [];\n const skipped: string[] = [];\n\n for (const rel of walk(templateRoot)) {\n const src = join(templateRoot, rel);\n const dst = join(options.out, renderFilename(rel, context));\n if (existsSync(dst) && !options.force) {\n skipped.push(dst);\n continue;\n }\n const raw = readFileSync(src, 'utf8');\n const rendered = rel.endsWith('.hbs')\n ? Handlebars.compile(raw, { noEscape: true })(context)\n : raw;\n mkdirSync(dirname(dst), { recursive: true });\n writeFileSync(dst, rendered);\n created.push(dst);\n }\n\n const summary = [\n `Scaffolded @kybernesis/arp-adapter-${framework} (${options.language}) at ${options.out}.`,\n `Created ${created.length} file(s), skipped ${skipped.length}.`,\n '',\n 'Next steps:',\n ` 1. cd ${relative(process.cwd(), options.out) || options.out}`,\n options.language === 'ts'\n ? ' 2. pnpm install'\n : ' 2. uv sync # or: python -m pip install -e .',\n ' 3. Implement src/* to map your framework\\'s public extension points to ArpAgent.',\n ' 4. Run the conformance test: pnpm test (or: uv run pytest)',\n ' 5. See docs/ARP-adapter-authoring-guide.md for the full contract.',\n ].join('\\n');\n\n return { createdFiles: created, skippedFiles: skipped, summary };\n}\n\nfunction walk(dir: string): string[] {\n const out: string[] = [];\n function recurse(sub: string) {\n const abs = join(dir, sub);\n for (const entry of readdirSync(abs)) {\n const fullRel = sub ? join(sub, entry) : entry;\n const fullAbs = join(dir, fullRel);\n const st = statSync(fullAbs);\n if (st.isDirectory()) recurse(fullRel);\n else out.push(fullRel);\n }\n }\n recurse('');\n return out;\n}\n\nfunction renderFilename(rel: string, ctx: Record<string, string>): string {\n // Trim trailing `.hbs` and run Handlebars on the path (for filenames\n // like `src/{{framework}}.ts.hbs`).\n const withoutHbs = rel.endsWith('.hbs') ? rel.slice(0, -'.hbs'.length) : rel;\n return Handlebars.compile(withoutHbs, { noEscape: true })(ctx);\n}\n\nfunction toPascal(slug: string): string {\n return slug\n .split('-')\n .filter(Boolean)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join('');\n}\n\nfunction toCamel(slug: string): string {\n const pascal = toPascal(slug);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nfunction toDisplayName(slug: string): string {\n return slug\n .split('-')\n .filter(Boolean)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(' ');\n}\n","#!/usr/bin/env node\n/**\n * `create-arp-adapter` CLI.\n *\n * npx @kybernesis/arp-create-adapter \\\n * --framework my-framework \\\n * --language ts \\\n * --out ./adapters/my-framework\n */\n\nimport { Command } from 'commander';\nimport { resolve } from 'node:path';\nimport { scaffoldAdapter, type SupportedLanguage } from './scaffold.js';\n\nconst program = new Command();\n\nprogram\n .name('create-arp-adapter')\n .description('Scaffold a conformance-passing ARP framework adapter.')\n .requiredOption('-f, --framework <slug>', 'Framework slug (kebab-case), e.g. \"my-framework\"')\n .option('-n, --name <name>', 'Display name (e.g. \"My Framework\"). Defaults to title-cased slug.')\n .requiredOption('-l, --language <ts|python>', 'Target language', 'ts')\n .option('-o, --out <path>', 'Destination directory (default ./adapters/<slug>)')\n .option('--arp-version <range>', 'ARP spec/SDK version range', '^0.1.0')\n .option('--force', 'Overwrite existing files', false)\n .action(async (opts: {\n framework: string;\n name?: string;\n language: string;\n out?: string;\n arpVersion: string;\n force: boolean;\n }) => {\n if (opts.language !== 'ts' && opts.language !== 'python') {\n // eslint-disable-next-line no-console\n console.error(`--language must be \"ts\" or \"python\", got \"${opts.language}\"`);\n process.exit(1);\n }\n const out = resolve(process.cwd(), opts.out ?? `./adapters/${opts.framework}`);\n try {\n const result = await scaffoldAdapter({\n framework: opts.framework,\n ...(opts.name ? { displayName: opts.name } : {}),\n language: opts.language as SupportedLanguage,\n out,\n arpVersion: opts.arpVersion,\n force: opts.force,\n });\n // eslint-disable-next-line no-console\n console.log(result.summary);\n if (result.skippedFiles.length > 0) {\n // eslint-disable-next-line no-console\n console.log('\\nSkipped (use --force to overwrite):');\n for (const f of result.skippedFiles) {\n // eslint-disable-next-line no-console\n console.log(` ${f}`);\n }\n }\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('create-arp-adapter failed:', (err as Error).message);\n process.exit(2);\n }\n });\n\nprogram.parseAsync(process.argv).catch((err: unknown) => {\n // eslint-disable-next-line no-console\n console.error(err);\n process.exit(3);\n});\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+ var url = require('url');
6
+ var Handlebars = require('handlebars');
7
+
8
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
12
+
13
+ // src/scaffold.ts
14
+ var __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
15
+ var __dirname$1 = path.dirname(__filename$1);
16
+ var DEFAULT_TEMPLATES_DIR = path.resolve(__dirname$1, "..", "templates");
17
+ async function scaffoldAdapter(options) {
18
+ const framework = options.framework.trim().toLowerCase();
19
+ if (!/^[a-z][a-z0-9-]*$/.test(framework)) {
20
+ throw new Error(
21
+ `framework slug "${options.framework}" must match /^[a-z][a-z0-9-]*$/`
22
+ );
23
+ }
24
+ const displayName = options.displayName ?? toDisplayName(framework);
25
+ const arpVersion = options.arpVersion ?? "^0.1.0";
26
+ const templateRoot = options.templatesDir ? path.resolve(options.templatesDir, options.language) : path.join(DEFAULT_TEMPLATES_DIR, options.language);
27
+ if (!fs.existsSync(templateRoot)) {
28
+ throw new Error(`template directory missing: ${templateRoot}`);
29
+ }
30
+ const context = {
31
+ framework,
32
+ frameworkPascal: toPascal(framework),
33
+ frameworkCamel: toCamel(framework),
34
+ frameworkSnake: framework.replace(/-/g, "_"),
35
+ frameworkUpper: framework.toUpperCase().replace(/-/g, "_"),
36
+ displayName,
37
+ arpVersion,
38
+ /** Best-effort Python-compatible version range — strip leading `^`. */
39
+ pythonArpVersion: arpVersion.startsWith("^") ? `>=${arpVersion.slice(1)}` : arpVersion,
40
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
41
+ };
42
+ const created = [];
43
+ const skipped = [];
44
+ for (const rel of walk(templateRoot)) {
45
+ const src = path.join(templateRoot, rel);
46
+ const dst = path.join(options.out, renderFilename(rel, context));
47
+ if (fs.existsSync(dst) && !options.force) {
48
+ skipped.push(dst);
49
+ continue;
50
+ }
51
+ const raw = fs.readFileSync(src, "utf8");
52
+ const rendered = rel.endsWith(".hbs") ? Handlebars__default.default.compile(raw, { noEscape: true })(context) : raw;
53
+ fs.mkdirSync(path.dirname(dst), { recursive: true });
54
+ fs.writeFileSync(dst, rendered);
55
+ created.push(dst);
56
+ }
57
+ const summary = [
58
+ `Scaffolded @kybernesis/arp-adapter-${framework} (${options.language}) at ${options.out}.`,
59
+ `Created ${created.length} file(s), skipped ${skipped.length}.`,
60
+ "",
61
+ "Next steps:",
62
+ ` 1. cd ${path.relative(process.cwd(), options.out) || options.out}`,
63
+ options.language === "ts" ? " 2. pnpm install" : " 2. uv sync # or: python -m pip install -e .",
64
+ " 3. Implement src/* to map your framework's public extension points to ArpAgent.",
65
+ " 4. Run the conformance test: pnpm test (or: uv run pytest)",
66
+ " 5. See docs/ARP-adapter-authoring-guide.md for the full contract."
67
+ ].join("\n");
68
+ return { createdFiles: created, skippedFiles: skipped, summary };
69
+ }
70
+ function walk(dir) {
71
+ const out = [];
72
+ function recurse(sub) {
73
+ const abs = path.join(dir, sub);
74
+ for (const entry of fs.readdirSync(abs)) {
75
+ const fullRel = sub ? path.join(sub, entry) : entry;
76
+ const fullAbs = path.join(dir, fullRel);
77
+ const st = fs.statSync(fullAbs);
78
+ if (st.isDirectory()) recurse(fullRel);
79
+ else out.push(fullRel);
80
+ }
81
+ }
82
+ recurse("");
83
+ return out;
84
+ }
85
+ function renderFilename(rel, ctx) {
86
+ const withoutHbs = rel.endsWith(".hbs") ? rel.slice(0, -".hbs".length) : rel;
87
+ return Handlebars__default.default.compile(withoutHbs, { noEscape: true })(ctx);
88
+ }
89
+ function toPascal(slug) {
90
+ return slug.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
91
+ }
92
+ function toCamel(slug) {
93
+ const pascal = toPascal(slug);
94
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
95
+ }
96
+ function toDisplayName(slug) {
97
+ return slug.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" ");
98
+ }
99
+
100
+ exports.scaffoldAdapter = scaffoldAdapter;
101
+ //# sourceMappingURL=index.cjs.map
102
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scaffold.ts"],"names":["__filename","fileURLToPath","__dirname","dirname","resolve","join","existsSync","readFileSync","Handlebars","mkdirSync","writeFileSync","relative","readdirSync","statSync"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Core scaffolder. Reads a template directory, renders each file through\n * Handlebars, and writes to the target path.\n *\n * Templates live under `packages/create-adapter/templates/<lang>/` and the\n * scaffolder resolves them relative to the compiled `dist/` via\n * `fileURLToPath(import.meta.url)`. Consumers using the programmatic API\n * in a non-standard layout can pass `templatesDir` directly.\n */\n\nimport { readdirSync, readFileSync, statSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport Handlebars from 'handlebars';\n\nexport type SupportedLanguage = 'ts' | 'python';\n\nexport interface ScaffoldOptions {\n /** Framework slug (kebab-case). Used in file paths + imports. */\n framework: string;\n /** Human-readable name (e.g. \"KyberBot\"). Used in README, error strings. */\n displayName?: string;\n /** Target language. */\n language: SupportedLanguage;\n /** Destination directory — will be created if missing. */\n out: string;\n /** Override the template root. Default: bundled templates. */\n templatesDir?: string;\n /** ARP spec version pinned in the generated package.json. */\n arpVersion?: string;\n /** When true, overwrite existing files. Default false. */\n force?: boolean;\n}\n\nexport interface ScaffoldResult {\n createdFiles: string[];\n skippedFiles: string[];\n summary: string;\n}\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst DEFAULT_TEMPLATES_DIR = resolve(__dirname, '..', 'templates');\n\nexport async function scaffoldAdapter(\n options: ScaffoldOptions,\n): Promise<ScaffoldResult> {\n const framework = options.framework.trim().toLowerCase();\n if (!/^[a-z][a-z0-9-]*$/.test(framework)) {\n throw new Error(\n `framework slug \"${options.framework}\" must match /^[a-z][a-z0-9-]*$/`,\n );\n }\n const displayName = options.displayName ?? toDisplayName(framework);\n const arpVersion = options.arpVersion ?? '^0.1.0';\n\n const templateRoot = options.templatesDir\n ? resolve(options.templatesDir, options.language)\n : join(DEFAULT_TEMPLATES_DIR, options.language);\n if (!existsSync(templateRoot)) {\n throw new Error(`template directory missing: ${templateRoot}`);\n }\n\n const context = {\n framework,\n frameworkPascal: toPascal(framework),\n frameworkCamel: toCamel(framework),\n frameworkSnake: framework.replace(/-/g, '_'),\n frameworkUpper: framework.toUpperCase().replace(/-/g, '_'),\n displayName,\n arpVersion,\n /** Best-effort Python-compatible version range — strip leading `^`. */\n pythonArpVersion: arpVersion.startsWith('^')\n ? `>=${arpVersion.slice(1)}`\n : arpVersion,\n generatedAt: new Date().toISOString(),\n };\n\n const created: string[] = [];\n const skipped: string[] = [];\n\n for (const rel of walk(templateRoot)) {\n const src = join(templateRoot, rel);\n const dst = join(options.out, renderFilename(rel, context));\n if (existsSync(dst) && !options.force) {\n skipped.push(dst);\n continue;\n }\n const raw = readFileSync(src, 'utf8');\n const rendered = rel.endsWith('.hbs')\n ? Handlebars.compile(raw, { noEscape: true })(context)\n : raw;\n mkdirSync(dirname(dst), { recursive: true });\n writeFileSync(dst, rendered);\n created.push(dst);\n }\n\n const summary = [\n `Scaffolded @kybernesis/arp-adapter-${framework} (${options.language}) at ${options.out}.`,\n `Created ${created.length} file(s), skipped ${skipped.length}.`,\n '',\n 'Next steps:',\n ` 1. cd ${relative(process.cwd(), options.out) || options.out}`,\n options.language === 'ts'\n ? ' 2. pnpm install'\n : ' 2. uv sync # or: python -m pip install -e .',\n ' 3. Implement src/* to map your framework\\'s public extension points to ArpAgent.',\n ' 4. Run the conformance test: pnpm test (or: uv run pytest)',\n ' 5. See docs/ARP-adapter-authoring-guide.md for the full contract.',\n ].join('\\n');\n\n return { createdFiles: created, skippedFiles: skipped, summary };\n}\n\nfunction walk(dir: string): string[] {\n const out: string[] = [];\n function recurse(sub: string) {\n const abs = join(dir, sub);\n for (const entry of readdirSync(abs)) {\n const fullRel = sub ? join(sub, entry) : entry;\n const fullAbs = join(dir, fullRel);\n const st = statSync(fullAbs);\n if (st.isDirectory()) recurse(fullRel);\n else out.push(fullRel);\n }\n }\n recurse('');\n return out;\n}\n\nfunction renderFilename(rel: string, ctx: Record<string, string>): string {\n // Trim trailing `.hbs` and run Handlebars on the path (for filenames\n // like `src/{{framework}}.ts.hbs`).\n const withoutHbs = rel.endsWith('.hbs') ? rel.slice(0, -'.hbs'.length) : rel;\n return Handlebars.compile(withoutHbs, { noEscape: true })(ctx);\n}\n\nfunction toPascal(slug: string): string {\n return slug\n .split('-')\n .filter(Boolean)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join('');\n}\n\nfunction toCamel(slug: string): string {\n const pascal = toPascal(slug);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nfunction toDisplayName(slug: string): string {\n return slug\n .split('-')\n .filter(Boolean)\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(' ');\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Core scaffolder. Reads a template directory, renders each file through
3
+ * Handlebars, and writes to the target path.
4
+ *
5
+ * Templates live under `packages/create-adapter/templates/<lang>/` and the
6
+ * scaffolder resolves them relative to the compiled `dist/` via
7
+ * `fileURLToPath(import.meta.url)`. Consumers using the programmatic API
8
+ * in a non-standard layout can pass `templatesDir` directly.
9
+ */
10
+ type SupportedLanguage = 'ts' | 'python';
11
+ interface ScaffoldOptions {
12
+ /** Framework slug (kebab-case). Used in file paths + imports. */
13
+ framework: string;
14
+ /** Human-readable name (e.g. "KyberBot"). Used in README, error strings. */
15
+ displayName?: string;
16
+ /** Target language. */
17
+ language: SupportedLanguage;
18
+ /** Destination directory — will be created if missing. */
19
+ out: string;
20
+ /** Override the template root. Default: bundled templates. */
21
+ templatesDir?: string;
22
+ /** ARP spec version pinned in the generated package.json. */
23
+ arpVersion?: string;
24
+ /** When true, overwrite existing files. Default false. */
25
+ force?: boolean;
26
+ }
27
+ interface ScaffoldResult {
28
+ createdFiles: string[];
29
+ skippedFiles: string[];
30
+ summary: string;
31
+ }
32
+ declare function scaffoldAdapter(options: ScaffoldOptions): Promise<ScaffoldResult>;
33
+
34
+ export { type ScaffoldOptions, type ScaffoldResult, type SupportedLanguage, scaffoldAdapter };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Core scaffolder. Reads a template directory, renders each file through
3
+ * Handlebars, and writes to the target path.
4
+ *
5
+ * Templates live under `packages/create-adapter/templates/<lang>/` and the
6
+ * scaffolder resolves them relative to the compiled `dist/` via
7
+ * `fileURLToPath(import.meta.url)`. Consumers using the programmatic API
8
+ * in a non-standard layout can pass `templatesDir` directly.
9
+ */
10
+ type SupportedLanguage = 'ts' | 'python';
11
+ interface ScaffoldOptions {
12
+ /** Framework slug (kebab-case). Used in file paths + imports. */
13
+ framework: string;
14
+ /** Human-readable name (e.g. "KyberBot"). Used in README, error strings. */
15
+ displayName?: string;
16
+ /** Target language. */
17
+ language: SupportedLanguage;
18
+ /** Destination directory — will be created if missing. */
19
+ out: string;
20
+ /** Override the template root. Default: bundled templates. */
21
+ templatesDir?: string;
22
+ /** ARP spec version pinned in the generated package.json. */
23
+ arpVersion?: string;
24
+ /** When true, overwrite existing files. Default false. */
25
+ force?: boolean;
26
+ }
27
+ interface ScaffoldResult {
28
+ createdFiles: string[];
29
+ skippedFiles: string[];
30
+ summary: string;
31
+ }
32
+ declare function scaffoldAdapter(options: ScaffoldOptions): Promise<ScaffoldResult>;
33
+
34
+ export { type ScaffoldOptions, type ScaffoldResult, type SupportedLanguage, scaffoldAdapter };