create-theokit 1.0.15 → 1.0.16
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 +201 -0
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/package.json +7 -8
- package/templates/default/CLAUDE.md +2 -2
- package/templates/default/README.md.tmpl +7 -6
- package/templates/default/agents/chat.ts +22 -0
- package/templates/default/app/layout.tsx +3 -11
- package/templates/default/app/page.tsx +54 -39
- package/templates/default/dot-claude/skills/theokit-agents/SKILL.md +78 -44
- package/templates/default/dot-claude/skills/theokit-frontend/SKILL.md +24 -26
- package/templates/default/dot-claude/skills/theokit-ui/SKILL.md +32 -28
- package/templates/default/package.json.tmpl +10 -4
- package/templates/default/tsconfig.json +1 -1
- package/templates/default/server/routes/chat.ts +0 -69
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for describing the origin of the Work and
|
|
141
|
+
reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 usetheo.dev
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/dist/cli.js
CHANGED
|
@@ -297,6 +297,7 @@ function applyBareTransform(targetDir, options = {}) {
|
|
|
297
297
|
if (pkg.dependencies) {
|
|
298
298
|
delete pkg.dependencies["@theokit/ui"];
|
|
299
299
|
delete pkg.dependencies["@theokit/sdk"];
|
|
300
|
+
delete pkg.dependencies["@theokit/agents"];
|
|
300
301
|
delete pkg.dependencies["lucide-react"];
|
|
301
302
|
}
|
|
302
303
|
if (pkg.devDependencies) {
|
|
@@ -312,7 +313,7 @@ function applyBareTransform(targetDir, options = {}) {
|
|
|
312
313
|
if (existsSync2(pagePath)) {
|
|
313
314
|
writeFileSync2(pagePath, HELLO_PAGE);
|
|
314
315
|
}
|
|
315
|
-
const chatPath = join2(targetDir, "
|
|
316
|
+
const chatPath = join2(targetDir, "agents/chat.ts");
|
|
316
317
|
if (existsSync2(chatPath)) {
|
|
317
318
|
unlinkSync2(chatPath);
|
|
318
319
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/install.ts","../src/pkg-manager.ts","../src/preflight-node.ts","../src/prompts.ts","../src/scaffold-services.ts","../src/index.ts","../src/bare-transform.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport {\n readFileSync,\n writeFileSync,\n unlinkSync,\n mkdirSync,\n renameSync,\n existsSync,\n rmSync,\n} from 'node:fs'\nimport { resolve, join } from 'node:path'\n\nimport { runInstall } from './install.js'\nimport { detectPkgManager, type PkgManager } from './pkg-manager.js'\nimport { assertNodeVersion } from './preflight-node.js'\nimport { runPrompts, getDefaults, type ProjectOptions } from './prompts.js'\nimport { parseBackendFlags, scaffoldServices, type BackendKind } from './scaffold-services.js'\n\nimport { scaffold } from './index.js'\n\nconst VERSION = '0.8.0'\n\nfunction getFlag(args: string[], name: string): string | undefined {\n const flag = args.find((a) => a.startsWith(`--${name}=`))\n return flag ? flag.split('=')[1] : undefined\n}\n\nfunction hasFlag(args: string[], ...names: string[]): boolean {\n return names.some((n) => args.includes(`--${n}`) || args.includes(`-${n}`))\n}\n\nfunction showHelp(): void {\n console.log(`\n create-theokit v${VERSION}\n\n Usage: create-theokit <project-name> [options]\n\n Options:\n -h, --help Show this help message\n -v, --version Show version number\n --yes Use recommended defaults (skip prompts)\n --template=<name> Template to use (default: \"default\")\n --bare Minimal app (no @theokit/* deps)\n --skip-install Scaffold files only, skip package install\n --disable-git Skip git init\n --use-npm Use npm as package manager\n --use-pnpm Use pnpm as package manager\n --use-yarn Use yarn as package manager\n --use-bun Use bun as package manager\n --import-alias=<alias> Import alias (default: \"@/*\")\n --example=<name|github-url> Bootstrap from a GitHub example\n --biome Use Biome instead of ESLint\n --agents-md Include AGENTS.md (default: true)\n\n Examples:\n npx create-theokit my-app --yes\n npx create-theokit my-app\n npx create-theokit my-app --bare --skip-install\n npx create-theokit my-app --use-bun --biome\n npx create-theokit my-app --example=https://github.com/user/repo\n npx create-theokit my-app --import-alias=\"~/*\"\n`)\n}\n\nexport async function main(): Promise<void> {\n try {\n assertNodeVersion(process.version)\n } catch (err) {\n console.error('')\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n const args = process.argv.slice(2)\n\n // -h / --help\n if (hasFlag(args, 'h', 'help')) {\n showHelp()\n process.exit(0)\n }\n\n // -v / --version\n if (hasFlag(args, 'v', 'version')) {\n console.log(VERSION)\n process.exit(0)\n }\n\n const positionalArgs = args.filter((a) => !a.startsWith('--') && !a.startsWith('-'))\n const projectName = positionalArgs[0]\n\n if (!projectName) {\n showHelp()\n process.exit(1)\n }\n\n // Parse flags\n const templateName = getFlag(args, 'template') ?? 'default'\n const bare = hasFlag(args, 'bare')\n const skipInstall = hasFlag(args, 'skip-install')\n const useDefaults = hasFlag(args, 'yes')\n const disableGit = hasFlag(args, 'disable-git')\n const useBiome = hasFlag(args, 'biome')\n const exampleFlag = getFlag(args, 'example')\n const importAlias = getFlag(args, 'import-alias') ?? '@/*'\n\n // --use-npm/pnpm/yarn/bun override\n let pkgManagerOverride: PkgManager | undefined\n if (hasFlag(args, 'use-npm')) pkgManagerOverride = 'npm'\n else if (hasFlag(args, 'use-pnpm')) pkgManagerOverride = 'pnpm'\n else if (hasFlag(args, 'use-yarn')) pkgManagerOverride = 'yarn'\n else if (hasFlag(args, 'use-bun')) pkgManagerOverride = 'bun'\n\n let backends: BackendKind[] = []\n try {\n backends = parseBackendFlags(args)\n } catch (err) {\n console.error('')\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n const targetDir = resolve(process.cwd(), projectName)\n\n // --example: clone from GitHub\n if (exampleFlag) {\n try {\n console.log(`\\nCloning example \"${exampleFlag}\" into ${projectName}...\\n`)\n cloneExample(exampleFlag, targetDir)\n const pkgManager = pkgManagerOverride ?? detectPkgManager()\n if (!skipInstall) {\n console.log(`Installing dependencies with ${pkgManager}...\\n`)\n runInstall(targetDir, pkgManager)\n }\n if (!disableGit) initGit(targetDir)\n console.log(`\\n ✓ Example cloned to ${targetDir}\\n`)\n console.log(` cd ${projectName}`)\n console.log(` ${pkgManager === 'npm' ? 'npm run' : pkgManager} dev\\n`)\n process.exit(0)\n } catch (err) {\n console.error(`\\n ✗ ${(err as Error).message}\\n`)\n process.exit(1)\n }\n }\n\n // Interactive prompts (skipped with --yes or --bare)\n let options: ProjectOptions\n if (useDefaults || bare) {\n options = getDefaults(projectName)\n if (useBiome) options.eslint = false // Biome replaces ESLint\n } else {\n options = await runPrompts(projectName)\n }\n\n try {\n const suffix = bare ? ' [--bare]' : ''\n const backendsSuffix = backends.length > 0 ? ` [+services: ${backends.join(', ')}]` : ''\n console.log(\n `\\nCreating TheoKit project \"${projectName}\" (template: ${templateName})${suffix}${backendsSuffix}...\\n`,\n )\n\n scaffold(targetDir, projectName, templateName, { bare })\n\n // Post-scaffold: apply user options\n applyOptions(targetDir, options, { importAlias, useBiome })\n\n if (backends.length > 0) {\n scaffoldServices({ targetDir, projectName, backends })\n console.log(` ✓ Scaffolded ${String(backends.length)} service(s): ${backends.join(', ')}\\n`)\n }\n\n const pkgManager = pkgManagerOverride ?? detectPkgManager()\n if (skipInstall) {\n console.log(`Skipping install (--skip-install). Run \\`${pkgManager} install\\` manually.\\n`)\n } else {\n console.log(`Installing dependencies with ${pkgManager}...\\n`)\n runInstall(targetDir, pkgManager)\n }\n\n // git init (unless --disable-git)\n if (!disableGit) initGit(targetDir)\n\n console.log(`\\n ✓ Project created at ${targetDir}\\n`)\n console.log(' Next steps:\\n')\n console.log(` cd ${projectName}`)\n if (skipInstall) console.log(` ${pkgManager} install`)\n console.log(` ${pkgManager === 'npm' ? 'npm run' : pkgManager} dev\\n`)\n } catch (err) {\n console.error(`\\n ✗ ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ── Post-scaffold transforms ──\n\ninterface ApplyOpts {\n importAlias: string\n useBiome: boolean\n}\n\nfunction applyOptions(targetDir: string, options: ProjectOptions, opts: ApplyOpts): void {\n const pkgPath = resolve(targetDir, 'package.json')\n\n // Biome: replace ESLint with Biome\n if (opts.useBiome) {\n const eslintPath = resolve(targetDir, 'eslint.config.mjs')\n if (existsSync(eslintPath)) unlinkSync(eslintPath)\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n delete pkg.devDependencies.eslint\n delete pkg.devDependencies['eslint-config-prettier']\n delete pkg.devDependencies['typescript-eslint']\n pkg.devDependencies['@biomejs/biome'] = '^1.9.0'\n pkg.scripts.lint = 'biome check .'\n pkg.scripts['lint:fix'] = 'biome check . --fix'\n pkg.scripts.format = 'biome format . --write'\n pkg.scripts['format:check'] = 'biome format .'\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n')\n\n // Write biome.json\n writeFileSync(\n resolve(targetDir, 'biome.json'),\n JSON.stringify(\n {\n $schema: 'https://biomejs.dev/schemas/1.9.0/schema.json',\n organizeImports: { enabled: true },\n linter: { enabled: true, rules: { recommended: true } },\n formatter: { enabled: true, indentStyle: 'space', indentWidth: 2, lineWidth: 100 },\n },\n null,\n 2,\n ) + '\\n',\n )\n } else if (!options.eslint) {\n // No linter\n const eslintPath = resolve(targetDir, 'eslint.config.mjs')\n if (existsSync(eslintPath)) unlinkSync(eslintPath)\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n delete pkg.scripts.lint\n delete pkg.scripts['lint:fix']\n delete pkg.devDependencies.eslint\n delete pkg.devDependencies['eslint-config-prettier']\n delete pkg.devDependencies['typescript-eslint']\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n')\n }\n\n // Tailwind\n if (options.tailwind) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n pkg.devDependencies = pkg.devDependencies ?? {}\n pkg.devDependencies.tailwindcss = '^4.0.0'\n pkg.devDependencies['@tailwindcss/vite'] = '^4.0.0'\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n')\n const cssDir = existsSync(resolve(targetDir, 'src/app')) ? 'src/app' : 'app'\n const cssPath = resolve(targetDir, `${cssDir}/globals.css`)\n const existing = existsSync(cssPath) ? readFileSync(cssPath, 'utf-8') : ''\n writeFileSync(cssPath, '@import \"tailwindcss\";\\n\\n' + existing)\n }\n\n // src/ directory\n if (options.srcDir) {\n const srcDir = resolve(targetDir, 'src')\n mkdirSync(srcDir, { recursive: true })\n for (const dir of ['app', 'server']) {\n const from = resolve(targetDir, dir)\n const to = resolve(srcDir, dir)\n if (existsSync(from)) renameSync(from, to)\n }\n // Move theo.config.ts into src/ for --src-dir mode\n const configFrom = resolve(targetDir, 'theo.config.ts')\n if (existsSync(configFrom)) renameSync(configFrom, resolve(srcDir, 'theo.config.ts'))\n // index.html stays at root — Vite expects it there\n\n const tscPath = resolve(targetDir, 'tsconfig.json')\n const tsc = JSON.parse(readFileSync(tscPath, 'utf-8'))\n tsc.compilerOptions.baseUrl = 'src'\n tsc.include = ['src/**/*.ts', 'src/**/*.tsx']\n writeFileSync(tscPath, JSON.stringify(tsc, null, 2) + '\\n')\n }\n\n // Import alias (custom or disabled)\n const tscPath = resolve(targetDir, 'tsconfig.json')\n const tsc = JSON.parse(readFileSync(tscPath, 'utf-8'))\n if (!options.aliases) {\n delete tsc.compilerOptions.baseUrl\n delete tsc.compilerOptions.paths\n } else if (opts.importAlias !== '@/*') {\n // Custom alias: replace @/* with user choice\n const prefix = opts.importAlias.replace('/*', '')\n tsc.compilerOptions.paths = {\n [`${prefix}/*`]: ['./*'],\n [`${prefix}/server/*`]: ['./server/*'],\n [`${prefix}/app/*`]: ['./app/*'],\n }\n }\n writeFileSync(tscPath, JSON.stringify(tsc, null, 2) + '\\n')\n\n // AGENTS.md\n if (!options.agentsMd) {\n const agentsPath = resolve(targetDir, 'AGENTS.md')\n if (existsSync(agentsPath)) unlinkSync(agentsPath)\n }\n}\n\n// ── Git init ──\n\nfunction initGit(targetDir: string): void {\n try {\n execSync('git init', { cwd: targetDir, stdio: 'ignore' })\n execSync('git add -A', { cwd: targetDir, stdio: 'ignore' })\n execSync('git commit -m \"Initial commit from create-theokit\" --no-verify', {\n cwd: targetDir,\n stdio: 'ignore',\n })\n } catch {\n // git not installed — silently skip\n }\n}\n\n// ── Example cloning ──\n\nfunction cloneExample(example: string, targetDir: string): void {\n const isUrl = example.startsWith('http://') || example.startsWith('https://')\n const repo = isUrl\n ? example\n : `https://github.com/usetheodev/theokit-examples/tree/main/${example}`\n\n if (isUrl) {\n // Full repo URL — shallow clone\n execSync(`git clone --depth 1 ${repo} ${targetDir}`, { stdio: 'inherit' })\n // Remove .git to let user init fresh\n rmSync(join(targetDir, '.git'), { recursive: true, force: true })\n } else {\n // Named example from theokit-examples repo — use degit pattern\n try {\n execSync(`npx --yes degit usetheodev/theokit-examples/${example} ${targetDir}`, {\n stdio: 'inherit',\n })\n } catch {\n throw new Error(\n `Example \"${example}\" not found. ` +\n `Browse available examples at https://github.com/usetheodev/theokit-examples`,\n )\n }\n }\n}\n\n// Auto-execute\nmain().catch((err: unknown) => {\n console.error(err)\n process.exit(1)\n})\n","import spawn from 'cross-spawn'\n\nimport type { PkgManager } from './pkg-manager.js'\n\nexport function runInstall(cwd: string, pkgManager: PkgManager): void {\n const result = spawn.sync(pkgManager, ['install'], {\n cwd,\n stdio: 'inherit',\n env: { ...process.env, NODE_ENV: 'development' },\n })\n\n if (result.status !== 0) {\n throw new Error(`Failed to install dependencies with ${pkgManager}`)\n }\n}\n","export type PkgManager = 'npm' | 'pnpm' | 'yarn' | 'bun'\n\nexport function detectPkgManager(): PkgManager {\n const ua = process.env.npm_config_user_agent ?? ''\n if (ua.startsWith('yarn')) return 'yarn'\n if (ua.startsWith('pnpm')) return 'pnpm'\n if (ua.startsWith('bun')) return 'bun'\n return 'npm'\n}\n","/**\n * Node version preflight for `create-theokit`.\n *\n * `@theokit/sdk` declares `engines.node: \">=22.12.0\"`. Users on older Node\n * hit cryptic `node:sqlite` / `better-sqlite3` ABI errors mid-chat without\n * any actionable diagnostic. This preflight runs at scaffold start, prints\n * a clear error, and exits before any FS write (ADR D4 of the plan).\n *\n * Zero-dep semver comparator — no `semver` package needed.\n */\n\n/** Minimum Node version required by `@theokit/sdk`. */\nexport const MIN_NODE_VERSION = '22.12.0'\n\n/**\n * Compare two semver strings (major.minor.patch). Strips leading `v` from\n * either side. Pre-release suffixes (e.g., `-rc.1`, `-nightly...`) are\n * parsed as the base patch — acceptable for a preflight (we're not gating\n * on exact pre-release ordering).\n *\n * Returns:\n * <0 when a < b\n * 0 when a == b\n * >0 when a > b\n */\nexport function compareSemver(a: string, b: string): number {\n const parse = (raw: string): [number, number, number] => {\n const stripped = raw.replace(/^v/, '').split('-')[0] ?? '0.0.0'\n const parts = stripped.split('.').map((s) => Number.parseInt(s, 10) || 0)\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0]\n }\n const [aMaj, aMin, aPat] = parse(a)\n const [bMaj, bMin, bPat] = parse(b)\n if (aMaj !== bMaj) return aMaj - bMaj\n if (aMin !== bMin) return aMin - bMin\n return aPat - bPat\n}\n\n/**\n * Throw with an actionable message if `currentRaw` is below `minimum`.\n * Default minimum is `MIN_NODE_VERSION` (22.12.0).\n *\n * Called as the first line of `create-theokit`'s `main` so the preflight\n * fires before any directory is written.\n */\nexport function assertNodeVersion(currentRaw: string, minimum: string = MIN_NODE_VERSION): void {\n const current = currentRaw.replace(/^v/, '')\n if (compareSemver(currentRaw, minimum) < 0) {\n throw new Error(\n `create-theokit requires Node ${minimum} or later (the @theokit/sdk peer engines floor).\\n` +\n ` Detected: Node ${current}\\n` +\n ` Fix: nvm install 22 && nvm use 22\\n` +\n ` (or your version manager equivalent — fnm, volta, asdf, nvs)\\n`,\n )\n }\n}\n","/**\n * Interactive prompts for create-theokit.\n * Skipped when --yes flag is passed (uses defaults).\n *\n * Uses Node.js readline (zero deps) — not inquirer/prompts.\n */\nimport { createInterface } from 'node:readline'\n\nexport interface ProjectOptions {\n projectName: string\n typescript: boolean\n eslint: boolean\n tailwind: boolean\n srcDir: boolean\n aliases: boolean\n agentsMd: boolean\n}\n\nconst DEFAULTS: Omit<ProjectOptions, 'projectName'> = {\n typescript: true,\n eslint: true,\n tailwind: true,\n srcDir: false,\n aliases: true,\n agentsMd: true,\n}\n\nasync function ask(rl: ReturnType<typeof createInterface>, question: string, defaultVal: boolean): Promise<boolean> {\n return new Promise((resolve) => {\n const suffix = defaultVal ? '(Y/n)' : '(y/N)'\n rl.question(` ${question} ${suffix} `, (answer) => {\n const a = answer.trim().toLowerCase()\n if (a === '') resolve(defaultVal)\n else resolve(a === 'y' || a === 'yes')\n })\n })\n}\n\nasync function askChoice(rl: ReturnType<typeof createInterface>, question: string, choices: string[], defaultIdx = 0): Promise<number> {\n return new Promise((resolve) => {\n console.log(` ${question}`)\n for (let i = 0; i < choices.length; i++) {\n const marker = i === defaultIdx ? '❯' : ' '\n console.log(` ${marker} ${choices[i]}`)\n }\n rl.question(' Choice (number): ', (answer) => {\n const n = parseInt(answer.trim(), 10)\n resolve(n >= 1 && n <= choices.length ? n - 1 : defaultIdx)\n })\n })\n}\n\nexport async function runPrompts(projectName: string): Promise<ProjectOptions> {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n\n try {\n console.log('')\n const mode = await askChoice(rl, 'Would you like to use the recommended TheoKit defaults?', [\n 'Yes, use recommended defaults — TypeScript, ESLint, Tailwind CSS, AGENTS.md',\n 'No, customize settings',\n ], 0)\n\n if (mode === 0) {\n return { projectName, ...DEFAULTS }\n }\n\n const typescript = await ask(rl, 'Would you like to use TypeScript?', true)\n const eslint = await ask(rl, 'Would you like to use ESLint?', true)\n const tailwind = await ask(rl, 'Would you like to use Tailwind CSS?', true)\n const srcDir = await ask(rl, 'Would you like your code inside a `src/` directory?', false)\n const aliases = await ask(rl, 'Would you like to use import alias (@/*)?', true)\n const agentsMd = await ask(rl, 'Would you like to include AGENTS.md for coding agents?', true)\n\n return { projectName, typescript, eslint, tailwind, srcDir, aliases, agentsMd }\n } finally {\n rl.close()\n }\n}\n\nexport function getDefaults(projectName: string): ProjectOptions {\n return { projectName, ...DEFAULTS }\n}\n","/**\n * Build-time scaffold helper. All write paths derived from the trusted\n * targetDir (CLI argument, resolved absolute). Read paths are the\n * bundled service templates shipped with this package.\n */\n/**\n * Phase 4 — `--backend python|node` scaffolding (T4.1, T4.2).\n *\n * After the main TheoKit scaffold runs, this helper:\n * - Copies the requested service template(s) under `<target>/services/<name>/`\n * - Substitutes `{{name}}` in `.tmpl` files\n * - Renames `.tmpl` files to drop the suffix\n * - Injects services config into the user's `theo.config.ts`\n * - Injects `@hey-api/client-fetch` into the user's package.json (EC-10)\n */\nimport {\n cpSync,\n existsSync,\n readFileSync,\n readdirSync,\n statSync,\n writeFileSync,\n unlinkSync,\n} from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n// node-only polyglot backend (decision 2026-06-17). Python scaffolding was\n// deferred — only the Node (Hono) sidecar template ships. Re-adding Python\n// requires restoring its template + an ADR with demand evidence.\nexport type BackendKind = 'node'\n\nconst VALID_BACKENDS = ['node'] as const\n\n/**\n * Parse `--backend node` (multi-value) from argv.\n *\n * Accepts:\n * --backend node\n * --backend=node\n *\n * Throws on unknown backend name (incl. `python`, which is not shipped).\n */\nexport function parseBackendFlags(args: string[]): BackendKind[] {\n const out: BackendKind[] = []\n for (let i = 0; i < args.length; i++) {\n const a = args[i] ?? ''\n let value: string | undefined\n if (a === '--backend') {\n value = args[i + 1]\n i++\n } else if (a.startsWith('--backend=')) {\n value = a.slice('--backend='.length)\n }\n if (value === undefined) continue\n if (!(VALID_BACKENDS as readonly string[]).includes(value)) {\n throw new Error(\n `unknown --backend value: '${value}'. Valid options: ${VALID_BACKENDS.join(', ')}.`,\n )\n }\n out.push(value as BackendKind)\n }\n return out\n}\n\nconst BACKEND_CONFIG: Record<\n BackendKind,\n {\n templateDir: string\n serviceName: string\n port: number\n proxy: string\n dev: string\n start: string\n }\n> = {\n node: {\n templateDir: 'agent-node',\n serviceName: 'worker',\n port: 8002,\n proxy: '/api/worker',\n dev: 'pnpm dev',\n start: 'pnpm start',\n },\n}\n\nfunction getServiceTemplateDir(kind: BackendKind): string {\n return resolve(__dirname, '../templates/services', BACKEND_CONFIG[kind].templateDir)\n}\n\nfunction substituteTmpls(dir: string, projectName: string): void {\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const stat = statSync(full)\n if (stat.isDirectory()) {\n substituteTmpls(full, projectName)\n continue\n }\n if (entry.endsWith('.tmpl')) {\n const content = readFileSync(full, 'utf-8').replace(/\\{\\{name\\}\\}/g, projectName)\n const dest = full.replace(/\\.tmpl$/, '')\n writeFileSync(dest, content)\n unlinkSync(full)\n }\n }\n}\n\ninterface ServiceEntry {\n runtime: BackendKind\n port: number\n proxy: string\n dev: string\n start: string\n}\n\n/**\n * Build the `services: {}` snippet to inject into `theo.config.ts`.\n * Returns the inner record literal — caller wraps in `services: { ... }`.\n */\nexport function buildServicesSnippet(selections: { name: string; entry: ServiceEntry }[]): string {\n if (selections.length === 0) return ''\n const blocks = selections.map(({ name, entry }) => {\n return ` ${name}: {\n runtime: '${entry.runtime}',\n port: ${String(entry.port)},\n proxy: '${entry.proxy}',\n dev: ${JSON.stringify(entry.dev)},\n start: ${JSON.stringify(entry.start)},\n },`\n })\n return ` services: {\\n${blocks.join('\\n')}\\n },\\n`\n}\n\n/**\n * Insert the services block into an existing `theo.config.ts`.\n * Strategy: find `defineConfig({` and append the services block before the closing brace.\n */\nexport function injectServicesIntoConfig(configSource: string, snippet: string): string {\n if (snippet.length === 0) return configSource\n if (configSource.includes('services:')) return configSource // already present\n\n // Match the LAST `}` before the closing of defineConfig({...})\n const re = /defineConfig\\(\\{([\\s\\S]*?)\\}\\)/m\n const match = re.exec(configSource)\n if (!match) return configSource\n\n const inner = match[1]\n // Strip trailing whitespace without backtracking-prone \\s+$ regex.\n let trimEnd = inner.length\n while (trimEnd > 0 && /\\s/.test(inner.charAt(trimEnd - 1))) {\n trimEnd--\n }\n const trimmed = inner.slice(0, trimEnd)\n const sep = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\\n' : '\\n'\n const newInner = `${trimmed}${sep}${snippet}`\n return configSource.replace(re, `defineConfig({${newInner}})`)\n}\n\nexport function injectHeyApiDep(packageJsonSource: string): string {\n const pkg = JSON.parse(packageJsonSource) as {\n dependencies?: Record<string, string>\n }\n pkg.dependencies = pkg.dependencies ?? {}\n if (!('@hey-api/client-fetch' in pkg.dependencies)) {\n pkg.dependencies['@hey-api/client-fetch'] = '^0.6.0'\n }\n return JSON.stringify(pkg, null, 2) + '\\n'\n}\n\nexport interface ScaffoldServicesOptions {\n /** Target project directory (already scaffolded with the TS template). */\n targetDir: string\n /** Project name (substituted into .tmpl files). */\n projectName: string\n /** Which backends to scaffold. */\n backends: BackendKind[]\n}\n\nexport function scaffoldServices(options: ScaffoldServicesOptions): void {\n if (options.backends.length === 0) return\n\n const selections: { name: string; entry: ServiceEntry }[] = []\n\n for (const kind of options.backends) {\n const cfg = BACKEND_CONFIG[kind]\n const src = getServiceTemplateDir(kind)\n // Schema contract: dev/start commands run from `services/<serviceName>/` cwd\n // (services/schema/schema.ts line 35). The destination MUST equal serviceName\n // so the orchestrator's cwd resolution matches the on-disk directory.\n const dest = join(options.targetDir, 'services', cfg.serviceName)\n if (!existsSync(src)) {\n throw new Error(`service template not found: ${src}`)\n }\n cpSync(src, dest, { recursive: true })\n substituteTmpls(dest, options.projectName)\n\n selections.push({\n name: cfg.serviceName,\n entry: {\n runtime: kind,\n port: cfg.port,\n proxy: cfg.proxy,\n dev: cfg.dev,\n start: cfg.start,\n },\n })\n }\n\n // Inject services into theo.config.ts\n const configPath = join(options.targetDir, 'theo.config.ts')\n if (existsSync(configPath)) {\n const cfgSrc = readFileSync(configPath, 'utf-8')\n const snippet = buildServicesSnippet(selections)\n const updated = injectServicesIntoConfig(cfgSrc, snippet)\n if (updated !== cfgSrc) {\n writeFileSync(configPath, updated)\n }\n }\n\n // EC-10: inject @hey-api/client-fetch into user's package.json\n const pkgPath = join(options.targetDir, 'package.json')\n if (existsSync(pkgPath)) {\n const pkgSrc = readFileSync(pkgPath, 'utf-8')\n const updated = injectHeyApiDep(pkgSrc)\n writeFileSync(pkgPath, updated)\n }\n\n // Rename .gitignore for services if needed (none currently shipped, but reserved hook)\n}\n","/**\n * `create-theokit` scaffold tool. All write paths are derived from the\n * user-supplied target directory (CLI argument, resolved to absolute).\n * Read paths are the bundled `templates/` shipped with this package.\n * Build-time tool — no HTTP input.\n */\nimport {\n existsSync,\n cpSync,\n readFileSync,\n writeFileSync,\n renameSync,\n unlinkSync,\n readdirSync,\n rmSync,\n statSync,\n} from 'node:fs'\nimport { resolve, join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { applyBareTransform } from './bare-transform.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nfunction getTemplateDir(templateName = 'default'): string {\n return resolve(__dirname, '../templates', templateName)\n}\n\nfunction isValidProjectName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name)\n}\n\nexport interface ScaffoldOptions {\n bare?: boolean\n /** Test-only — force the bare transform to throw to validate EC-4 rollback. */\n _testForceTransformError?: string\n}\n\nexport function scaffold(\n targetDir: string,\n projectName: string,\n templateName = 'default',\n options: ScaffoldOptions = {},\n): void {\n // EC-4 + ADR D5: --bare only applies to default template\n if (options.bare && templateName !== 'default') {\n throw new Error(\n `--bare flag only applies to the default template; got \"${templateName}\". ` +\n `Use \\`npx create-theokit <name> --template=default --bare\\` or pick a different template.`,\n )\n }\n\n const templateDir = getTemplateDir(templateName)\n\n if (!existsSync(templateDir)) {\n throw new Error(`Template \"${templateName}\" not found. Available templates: default`)\n }\n\n // Bug #1 fix: \".\" means current directory — use dir basename as project name\n const resolvedName =\n projectName === '.' ? (resolve(targetDir).split('/').pop() ?? 'my-app') : projectName\n\n if (!isValidProjectName(resolvedName)) {\n throw new Error(\n `Invalid project name \"${resolvedName}\". ` +\n `Use lowercase letters, numbers, hyphens, and dots. Must start with a letter or number.`,\n )\n }\n\n if (existsSync(targetDir)) {\n const contents = readdirSync(targetDir)\n if (contents.length > 0) {\n throw new Error(`Directory \"${targetDir}\" is not empty. Please use an empty directory.`)\n }\n }\n\n cpSync(templateDir, targetDir, { recursive: true })\n\n const gitignoreSrc = join(targetDir, '_gitignore')\n const gitignoreDest = join(targetDir, '.gitignore')\n if (existsSync(gitignoreSrc)) {\n renameSync(gitignoreSrc, gitignoreDest)\n }\n\n // dot-claude/ → .claude/ (npm ignores dotfolders in published packages)\n const dotClaudeSrc = join(targetDir, 'dot-claude')\n const dotClaudeDest = join(targetDir, '.claude')\n if (existsSync(dotClaudeSrc)) {\n renameSync(dotClaudeSrc, dotClaudeDest)\n }\n\n // Apply {{name}} substitution to every `*.tmpl` file in the target dir.\n // Each `foo.tmpl` becomes `foo` with placeholders replaced. Walks only\n // the project root (deeper subfolders don't currently need templating).\n for (const entry of readdirSync(targetDir)) {\n if (!entry.endsWith('.tmpl')) continue\n const src = join(targetDir, entry)\n const stat = statSync(src)\n if (!stat.isFile()) continue\n const dst = join(targetDir, entry.slice(0, -'.tmpl'.length))\n const content = readFileSync(src, 'utf-8')\n const replaced = content.replace(/\\{\\{name\\}\\}/g, resolvedName)\n writeFileSync(dst, replaced)\n unlinkSync(src)\n }\n\n // T4.1 — Apply --bare transform with EC-4 atomic rollback\n if (options.bare) {\n try {\n applyBareTransform(targetDir, {\n _testForceError: options._testForceTransformError,\n })\n } catch (err) {\n // EC-4: roll back partial state\n rmSync(targetDir, { recursive: true, force: true })\n const original = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Scaffold rolled back: bare transform failed. Check filesystem perms.\\nOriginal error: ${original}`,\n )\n }\n }\n}\n","/**\n * Scaffold transform. Mutates files inside the freshly-created target\n * directory whose absolute path is the function input. No HTTP input.\n */\n/**\n * T4.1 — `--bare` transformation.\n *\n * Applied AFTER the default template is copied. Removes:\n * - `@theokit/ui` from `package.json` dependencies (TheoUI bundled components)\n * - `@theokit/sdk` from `package.json` dependencies (agent SDK — see below)\n * - `app/page.tsx` agent-surface content (replaces with Hello Theo)\n * - `server/routes/chat.ts` (mock chat — depends on SDK + TheoUI events)\n * - `tailwind.config.ts` + `postcss.config.js` (Tailwind toolchain — only\n * needed by the @theokit/ui-driven default surface)\n * - tailwind* + postcss* from devDependencies (toolchain cleanup)\n *\n * Why SDK removal is in --bare:\n * `@theokit/sdk` is not yet on the public npm registry (operator-deferred\n * publish per macro roadmap item #3). A user running `npx create-theokit`\n * without `--bare` hits `npm install` → 404. The `--bare` path produces a\n * scaffold that ALWAYS works without registry dependencies — Hello Theo\n * with a clean structure to grow into.\n *\n * EC-4: callers MUST wrap this in try/catch + `rmSync` rollback so a partial\n * transform never leaves the target dir in a broken state.\n */\n\nimport { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs'\nimport { join } from 'node:path'\n\nconst HELLO_PAGE = `export default function Page() {\n return <h1>Hello Theo</h1>\n}\n`\n\nexport interface BareTransformOptions {\n /** Test-only — force a synthetic write failure to validate rollback path. */\n _testForceError?: string\n}\n\nexport function applyBareTransform(targetDir: string, options: BareTransformOptions = {}): void {\n if (options._testForceError) {\n throw new Error(`Forced transform failure: ${options._testForceError}`)\n }\n\n // 1. Remove @theokit/ui + @theokit/sdk + tailwind toolchain from deps\n const pkgPath = join(targetDir, 'package.json')\n if (existsSync(pkgPath)) {\n interface PartialPackageJson {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n [key: string]: unknown\n }\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as PartialPackageJson\n if (pkg.dependencies) {\n delete pkg.dependencies['@theokit/ui']\n // Drop SDK — operator-deferred npm publish (macro roadmap item #3).\n // Without removal, `npm install` hits 404 for any consumer outside\n // the workspace.\n delete pkg.dependencies['@theokit/sdk']\n // lucide-react ships with the TheoUI surface; --bare doesn't render\n // any icons so it's safe to drop.\n delete pkg.dependencies['lucide-react']\n }\n if (pkg.devDependencies) {\n // Tailwind toolchain is only needed by the @theokit/ui-driven default\n // surface. --bare ships unstyled Hello Theo; no Tailwind required.\n delete pkg.devDependencies.tailwindcss\n delete pkg.devDependencies['tailwindcss-animate']\n delete pkg.devDependencies.postcss\n delete pkg.devDependencies.autoprefixer\n }\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`)\n }\n\n // 2. Replace app/page.tsx with Hello Theo\n const pagePath = join(targetDir, 'app/page.tsx')\n if (existsSync(pagePath)) {\n writeFileSync(pagePath, HELLO_PAGE)\n }\n\n // 3. Remove mock chat route (depends on AgentEvent type and TheoUI deps)\n const chatPath = join(targetDir, 'server/routes/chat.ts')\n if (existsSync(chatPath)) {\n unlinkSync(chatPath)\n }\n\n // 4. Remove tailwind + postcss config files (toolchain dropped from devDeps)\n const tailwindCfg = join(targetDir, 'tailwind.config.ts')\n if (existsSync(tailwindCfg)) {\n unlinkSync(tailwindCfg)\n }\n const postcssCfg = join(targetDir, 'postcss.config.js')\n if (existsSync(postcssCfg)) {\n unlinkSync(postcssCfg)\n }\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","unlinkSync","renameSync","existsSync","rmSync","resolve","join","resolve","existsSync","cpSync","readFileSync","writeFileSync","unlinkSync","readdirSync","statSync","resolve","join","dirname","fileURLToPath","existsSync","readFileSync","writeFileSync","unlinkSync","join","__dirname","dirname","fileURLToPath","resolve","existsSync","readdirSync","cpSync","join","statSync","readFileSync","writeFileSync","unlinkSync","resolve","existsSync","unlinkSync","readFileSync","writeFileSync","renameSync","tscPath","tsc","rmSync","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/install.ts","../src/pkg-manager.ts","../src/preflight-node.ts","../src/prompts.ts","../src/scaffold-services.ts","../src/index.ts","../src/bare-transform.ts"],"sourcesContent":["import { execSync } from 'node:child_process'\nimport {\n readFileSync,\n writeFileSync,\n unlinkSync,\n mkdirSync,\n renameSync,\n existsSync,\n rmSync,\n} from 'node:fs'\nimport { resolve, join } from 'node:path'\n\nimport { runInstall } from './install.js'\nimport { detectPkgManager, type PkgManager } from './pkg-manager.js'\nimport { assertNodeVersion } from './preflight-node.js'\nimport { runPrompts, getDefaults, type ProjectOptions } from './prompts.js'\nimport { parseBackendFlags, scaffoldServices, type BackendKind } from './scaffold-services.js'\n\nimport { scaffold } from './index.js'\n\nconst VERSION = '0.8.0'\n\nfunction getFlag(args: string[], name: string): string | undefined {\n const flag = args.find((a) => a.startsWith(`--${name}=`))\n return flag ? flag.split('=')[1] : undefined\n}\n\nfunction hasFlag(args: string[], ...names: string[]): boolean {\n return names.some((n) => args.includes(`--${n}`) || args.includes(`-${n}`))\n}\n\nfunction showHelp(): void {\n console.log(`\n create-theokit v${VERSION}\n\n Usage: create-theokit <project-name> [options]\n\n Options:\n -h, --help Show this help message\n -v, --version Show version number\n --yes Use recommended defaults (skip prompts)\n --template=<name> Template to use (default: \"default\")\n --bare Minimal app (no @theokit/* deps)\n --skip-install Scaffold files only, skip package install\n --disable-git Skip git init\n --use-npm Use npm as package manager\n --use-pnpm Use pnpm as package manager\n --use-yarn Use yarn as package manager\n --use-bun Use bun as package manager\n --import-alias=<alias> Import alias (default: \"@/*\")\n --example=<name|github-url> Bootstrap from a GitHub example\n --biome Use Biome instead of ESLint\n --agents-md Include AGENTS.md (default: true)\n\n Examples:\n npx create-theokit my-app --yes\n npx create-theokit my-app\n npx create-theokit my-app --bare --skip-install\n npx create-theokit my-app --use-bun --biome\n npx create-theokit my-app --example=https://github.com/user/repo\n npx create-theokit my-app --import-alias=\"~/*\"\n`)\n}\n\nexport async function main(): Promise<void> {\n try {\n assertNodeVersion(process.version)\n } catch (err) {\n console.error('')\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n const args = process.argv.slice(2)\n\n // -h / --help\n if (hasFlag(args, 'h', 'help')) {\n showHelp()\n process.exit(0)\n }\n\n // -v / --version\n if (hasFlag(args, 'v', 'version')) {\n console.log(VERSION)\n process.exit(0)\n }\n\n const positionalArgs = args.filter((a) => !a.startsWith('--') && !a.startsWith('-'))\n const projectName = positionalArgs[0]\n\n if (!projectName) {\n showHelp()\n process.exit(1)\n }\n\n // Parse flags\n const templateName = getFlag(args, 'template') ?? 'default'\n const bare = hasFlag(args, 'bare')\n const skipInstall = hasFlag(args, 'skip-install')\n const useDefaults = hasFlag(args, 'yes')\n const disableGit = hasFlag(args, 'disable-git')\n const useBiome = hasFlag(args, 'biome')\n const exampleFlag = getFlag(args, 'example')\n const importAlias = getFlag(args, 'import-alias') ?? '@/*'\n\n // --use-npm/pnpm/yarn/bun override\n let pkgManagerOverride: PkgManager | undefined\n if (hasFlag(args, 'use-npm')) pkgManagerOverride = 'npm'\n else if (hasFlag(args, 'use-pnpm')) pkgManagerOverride = 'pnpm'\n else if (hasFlag(args, 'use-yarn')) pkgManagerOverride = 'yarn'\n else if (hasFlag(args, 'use-bun')) pkgManagerOverride = 'bun'\n\n let backends: BackendKind[] = []\n try {\n backends = parseBackendFlags(args)\n } catch (err) {\n console.error('')\n console.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n }\n\n const targetDir = resolve(process.cwd(), projectName)\n\n // --example: clone from GitHub\n if (exampleFlag) {\n try {\n console.log(`\\nCloning example \"${exampleFlag}\" into ${projectName}...\\n`)\n cloneExample(exampleFlag, targetDir)\n const pkgManager = pkgManagerOverride ?? detectPkgManager()\n if (!skipInstall) {\n console.log(`Installing dependencies with ${pkgManager}...\\n`)\n runInstall(targetDir, pkgManager)\n }\n if (!disableGit) initGit(targetDir)\n console.log(`\\n ✓ Example cloned to ${targetDir}\\n`)\n console.log(` cd ${projectName}`)\n console.log(` ${pkgManager === 'npm' ? 'npm run' : pkgManager} dev\\n`)\n process.exit(0)\n } catch (err) {\n console.error(`\\n ✗ ${(err as Error).message}\\n`)\n process.exit(1)\n }\n }\n\n // Interactive prompts (skipped with --yes or --bare)\n let options: ProjectOptions\n if (useDefaults || bare) {\n options = getDefaults(projectName)\n if (useBiome) options.eslint = false // Biome replaces ESLint\n } else {\n options = await runPrompts(projectName)\n }\n\n try {\n const suffix = bare ? ' [--bare]' : ''\n const backendsSuffix = backends.length > 0 ? ` [+services: ${backends.join(', ')}]` : ''\n console.log(\n `\\nCreating TheoKit project \"${projectName}\" (template: ${templateName})${suffix}${backendsSuffix}...\\n`,\n )\n\n scaffold(targetDir, projectName, templateName, { bare })\n\n // Post-scaffold: apply user options\n applyOptions(targetDir, options, { importAlias, useBiome })\n\n if (backends.length > 0) {\n scaffoldServices({ targetDir, projectName, backends })\n console.log(` ✓ Scaffolded ${String(backends.length)} service(s): ${backends.join(', ')}\\n`)\n }\n\n const pkgManager = pkgManagerOverride ?? detectPkgManager()\n if (skipInstall) {\n console.log(`Skipping install (--skip-install). Run \\`${pkgManager} install\\` manually.\\n`)\n } else {\n console.log(`Installing dependencies with ${pkgManager}...\\n`)\n runInstall(targetDir, pkgManager)\n }\n\n // git init (unless --disable-git)\n if (!disableGit) initGit(targetDir)\n\n console.log(`\\n ✓ Project created at ${targetDir}\\n`)\n console.log(' Next steps:\\n')\n console.log(` cd ${projectName}`)\n if (skipInstall) console.log(` ${pkgManager} install`)\n console.log(` ${pkgManager === 'npm' ? 'npm run' : pkgManager} dev\\n`)\n } catch (err) {\n console.error(`\\n ✗ ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ── Post-scaffold transforms ──\n\ninterface ApplyOpts {\n importAlias: string\n useBiome: boolean\n}\n\nfunction applyOptions(targetDir: string, options: ProjectOptions, opts: ApplyOpts): void {\n const pkgPath = resolve(targetDir, 'package.json')\n\n // Biome: replace ESLint with Biome\n if (opts.useBiome) {\n const eslintPath = resolve(targetDir, 'eslint.config.mjs')\n if (existsSync(eslintPath)) unlinkSync(eslintPath)\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n delete pkg.devDependencies.eslint\n delete pkg.devDependencies['eslint-config-prettier']\n delete pkg.devDependencies['typescript-eslint']\n pkg.devDependencies['@biomejs/biome'] = '^1.9.0'\n pkg.scripts.lint = 'biome check .'\n pkg.scripts['lint:fix'] = 'biome check . --fix'\n pkg.scripts.format = 'biome format . --write'\n pkg.scripts['format:check'] = 'biome format .'\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n')\n\n // Write biome.json\n writeFileSync(\n resolve(targetDir, 'biome.json'),\n JSON.stringify(\n {\n $schema: 'https://biomejs.dev/schemas/1.9.0/schema.json',\n organizeImports: { enabled: true },\n linter: { enabled: true, rules: { recommended: true } },\n formatter: { enabled: true, indentStyle: 'space', indentWidth: 2, lineWidth: 100 },\n },\n null,\n 2,\n ) + '\\n',\n )\n } else if (!options.eslint) {\n // No linter\n const eslintPath = resolve(targetDir, 'eslint.config.mjs')\n if (existsSync(eslintPath)) unlinkSync(eslintPath)\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n delete pkg.scripts.lint\n delete pkg.scripts['lint:fix']\n delete pkg.devDependencies.eslint\n delete pkg.devDependencies['eslint-config-prettier']\n delete pkg.devDependencies['typescript-eslint']\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n')\n }\n\n // Tailwind\n if (options.tailwind) {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'))\n pkg.devDependencies = pkg.devDependencies ?? {}\n pkg.devDependencies.tailwindcss = '^4.0.0'\n pkg.devDependencies['@tailwindcss/vite'] = '^4.0.0'\n writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\\n')\n const cssDir = existsSync(resolve(targetDir, 'src/app')) ? 'src/app' : 'app'\n const cssPath = resolve(targetDir, `${cssDir}/globals.css`)\n const existing = existsSync(cssPath) ? readFileSync(cssPath, 'utf-8') : ''\n writeFileSync(cssPath, '@import \"tailwindcss\";\\n\\n' + existing)\n }\n\n // src/ directory\n if (options.srcDir) {\n const srcDir = resolve(targetDir, 'src')\n mkdirSync(srcDir, { recursive: true })\n for (const dir of ['app', 'server']) {\n const from = resolve(targetDir, dir)\n const to = resolve(srcDir, dir)\n if (existsSync(from)) renameSync(from, to)\n }\n // Move theo.config.ts into src/ for --src-dir mode\n const configFrom = resolve(targetDir, 'theo.config.ts')\n if (existsSync(configFrom)) renameSync(configFrom, resolve(srcDir, 'theo.config.ts'))\n // index.html stays at root — Vite expects it there\n\n const tscPath = resolve(targetDir, 'tsconfig.json')\n const tsc = JSON.parse(readFileSync(tscPath, 'utf-8'))\n tsc.compilerOptions.baseUrl = 'src'\n tsc.include = ['src/**/*.ts', 'src/**/*.tsx']\n writeFileSync(tscPath, JSON.stringify(tsc, null, 2) + '\\n')\n }\n\n // Import alias (custom or disabled)\n const tscPath = resolve(targetDir, 'tsconfig.json')\n const tsc = JSON.parse(readFileSync(tscPath, 'utf-8'))\n if (!options.aliases) {\n delete tsc.compilerOptions.baseUrl\n delete tsc.compilerOptions.paths\n } else if (opts.importAlias !== '@/*') {\n // Custom alias: replace @/* with user choice\n const prefix = opts.importAlias.replace('/*', '')\n tsc.compilerOptions.paths = {\n [`${prefix}/*`]: ['./*'],\n [`${prefix}/server/*`]: ['./server/*'],\n [`${prefix}/app/*`]: ['./app/*'],\n }\n }\n writeFileSync(tscPath, JSON.stringify(tsc, null, 2) + '\\n')\n\n // AGENTS.md\n if (!options.agentsMd) {\n const agentsPath = resolve(targetDir, 'AGENTS.md')\n if (existsSync(agentsPath)) unlinkSync(agentsPath)\n }\n}\n\n// ── Git init ──\n\nfunction initGit(targetDir: string): void {\n try {\n execSync('git init', { cwd: targetDir, stdio: 'ignore' })\n execSync('git add -A', { cwd: targetDir, stdio: 'ignore' })\n execSync('git commit -m \"Initial commit from create-theokit\" --no-verify', {\n cwd: targetDir,\n stdio: 'ignore',\n })\n } catch {\n // git not installed — silently skip\n }\n}\n\n// ── Example cloning ──\n\nfunction cloneExample(example: string, targetDir: string): void {\n const isUrl = example.startsWith('http://') || example.startsWith('https://')\n const repo = isUrl\n ? example\n : `https://github.com/usetheodev/theokit-examples/tree/main/${example}`\n\n if (isUrl) {\n // Full repo URL — shallow clone\n execSync(`git clone --depth 1 ${repo} ${targetDir}`, { stdio: 'inherit' })\n // Remove .git to let user init fresh\n rmSync(join(targetDir, '.git'), { recursive: true, force: true })\n } else {\n // Named example from theokit-examples repo — use degit pattern\n try {\n execSync(`npx --yes degit usetheodev/theokit-examples/${example} ${targetDir}`, {\n stdio: 'inherit',\n })\n } catch {\n throw new Error(\n `Example \"${example}\" not found. ` +\n `Browse available examples at https://github.com/usetheodev/theokit-examples`,\n )\n }\n }\n}\n\n// Auto-execute\nmain().catch((err: unknown) => {\n console.error(err)\n process.exit(1)\n})\n","import spawn from 'cross-spawn'\n\nimport type { PkgManager } from './pkg-manager.js'\n\nexport function runInstall(cwd: string, pkgManager: PkgManager): void {\n const result = spawn.sync(pkgManager, ['install'], {\n cwd,\n stdio: 'inherit',\n env: { ...process.env, NODE_ENV: 'development' },\n })\n\n if (result.status !== 0) {\n throw new Error(`Failed to install dependencies with ${pkgManager}`)\n }\n}\n","export type PkgManager = 'npm' | 'pnpm' | 'yarn' | 'bun'\n\nexport function detectPkgManager(): PkgManager {\n const ua = process.env.npm_config_user_agent ?? ''\n if (ua.startsWith('yarn')) return 'yarn'\n if (ua.startsWith('pnpm')) return 'pnpm'\n if (ua.startsWith('bun')) return 'bun'\n return 'npm'\n}\n","/**\n * Node version preflight for `create-theokit`.\n *\n * `@theokit/sdk` declares `engines.node: \">=22.12.0\"`. Users on older Node\n * hit cryptic `node:sqlite` / `better-sqlite3` ABI errors mid-chat without\n * any actionable diagnostic. This preflight runs at scaffold start, prints\n * a clear error, and exits before any FS write (ADR D4 of the plan).\n *\n * Zero-dep semver comparator — no `semver` package needed.\n */\n\n/** Minimum Node version required by `@theokit/sdk`. */\nexport const MIN_NODE_VERSION = '22.12.0'\n\n/**\n * Compare two semver strings (major.minor.patch). Strips leading `v` from\n * either side. Pre-release suffixes (e.g., `-rc.1`, `-nightly...`) are\n * parsed as the base patch — acceptable for a preflight (we're not gating\n * on exact pre-release ordering).\n *\n * Returns:\n * <0 when a < b\n * 0 when a == b\n * >0 when a > b\n */\nexport function compareSemver(a: string, b: string): number {\n const parse = (raw: string): [number, number, number] => {\n const stripped = raw.replace(/^v/, '').split('-')[0] ?? '0.0.0'\n const parts = stripped.split('.').map((s) => Number.parseInt(s, 10) || 0)\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0]\n }\n const [aMaj, aMin, aPat] = parse(a)\n const [bMaj, bMin, bPat] = parse(b)\n if (aMaj !== bMaj) return aMaj - bMaj\n if (aMin !== bMin) return aMin - bMin\n return aPat - bPat\n}\n\n/**\n * Throw with an actionable message if `currentRaw` is below `minimum`.\n * Default minimum is `MIN_NODE_VERSION` (22.12.0).\n *\n * Called as the first line of `create-theokit`'s `main` so the preflight\n * fires before any directory is written.\n */\nexport function assertNodeVersion(currentRaw: string, minimum: string = MIN_NODE_VERSION): void {\n const current = currentRaw.replace(/^v/, '')\n if (compareSemver(currentRaw, minimum) < 0) {\n throw new Error(\n `create-theokit requires Node ${minimum} or later (the @theokit/sdk peer engines floor).\\n` +\n ` Detected: Node ${current}\\n` +\n ` Fix: nvm install 22 && nvm use 22\\n` +\n ` (or your version manager equivalent — fnm, volta, asdf, nvs)\\n`,\n )\n }\n}\n","/**\n * Interactive prompts for create-theokit.\n * Skipped when --yes flag is passed (uses defaults).\n *\n * Uses Node.js readline (zero deps) — not inquirer/prompts.\n */\nimport { createInterface } from 'node:readline'\n\nexport interface ProjectOptions {\n projectName: string\n typescript: boolean\n eslint: boolean\n tailwind: boolean\n srcDir: boolean\n aliases: boolean\n agentsMd: boolean\n}\n\nconst DEFAULTS: Omit<ProjectOptions, 'projectName'> = {\n typescript: true,\n eslint: true,\n tailwind: true,\n srcDir: false,\n aliases: true,\n agentsMd: true,\n}\n\nasync function ask(rl: ReturnType<typeof createInterface>, question: string, defaultVal: boolean): Promise<boolean> {\n return new Promise((resolve) => {\n const suffix = defaultVal ? '(Y/n)' : '(y/N)'\n rl.question(` ${question} ${suffix} `, (answer) => {\n const a = answer.trim().toLowerCase()\n if (a === '') resolve(defaultVal)\n else resolve(a === 'y' || a === 'yes')\n })\n })\n}\n\nasync function askChoice(rl: ReturnType<typeof createInterface>, question: string, choices: string[], defaultIdx = 0): Promise<number> {\n return new Promise((resolve) => {\n console.log(` ${question}`)\n for (let i = 0; i < choices.length; i++) {\n const marker = i === defaultIdx ? '❯' : ' '\n console.log(` ${marker} ${choices[i]}`)\n }\n rl.question(' Choice (number): ', (answer) => {\n const n = parseInt(answer.trim(), 10)\n resolve(n >= 1 && n <= choices.length ? n - 1 : defaultIdx)\n })\n })\n}\n\nexport async function runPrompts(projectName: string): Promise<ProjectOptions> {\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n\n try {\n console.log('')\n const mode = await askChoice(rl, 'Would you like to use the recommended TheoKit defaults?', [\n 'Yes, use recommended defaults — TypeScript, ESLint, Tailwind CSS, AGENTS.md',\n 'No, customize settings',\n ], 0)\n\n if (mode === 0) {\n return { projectName, ...DEFAULTS }\n }\n\n const typescript = await ask(rl, 'Would you like to use TypeScript?', true)\n const eslint = await ask(rl, 'Would you like to use ESLint?', true)\n const tailwind = await ask(rl, 'Would you like to use Tailwind CSS?', true)\n const srcDir = await ask(rl, 'Would you like your code inside a `src/` directory?', false)\n const aliases = await ask(rl, 'Would you like to use import alias (@/*)?', true)\n const agentsMd = await ask(rl, 'Would you like to include AGENTS.md for coding agents?', true)\n\n return { projectName, typescript, eslint, tailwind, srcDir, aliases, agentsMd }\n } finally {\n rl.close()\n }\n}\n\nexport function getDefaults(projectName: string): ProjectOptions {\n return { projectName, ...DEFAULTS }\n}\n","/**\n * Build-time scaffold helper. All write paths derived from the trusted\n * targetDir (CLI argument, resolved absolute). Read paths are the\n * bundled service templates shipped with this package.\n */\n/**\n * Phase 4 — `--backend python|node` scaffolding (T4.1, T4.2).\n *\n * After the main TheoKit scaffold runs, this helper:\n * - Copies the requested service template(s) under `<target>/services/<name>/`\n * - Substitutes `{{name}}` in `.tmpl` files\n * - Renames `.tmpl` files to drop the suffix\n * - Injects services config into the user's `theo.config.ts`\n * - Injects `@hey-api/client-fetch` into the user's package.json (EC-10)\n */\nimport {\n cpSync,\n existsSync,\n readFileSync,\n readdirSync,\n statSync,\n writeFileSync,\n unlinkSync,\n} from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\n// node-only polyglot backend (decision 2026-06-17). Python scaffolding was\n// deferred — only the Node (Hono) sidecar template ships. Re-adding Python\n// requires restoring its template + an ADR with demand evidence.\nexport type BackendKind = 'node'\n\nconst VALID_BACKENDS = ['node'] as const\n\n/**\n * Parse `--backend node` (multi-value) from argv.\n *\n * Accepts:\n * --backend node\n * --backend=node\n *\n * Throws on unknown backend name (incl. `python`, which is not shipped).\n */\nexport function parseBackendFlags(args: string[]): BackendKind[] {\n const out: BackendKind[] = []\n for (let i = 0; i < args.length; i++) {\n const a = args[i] ?? ''\n let value: string | undefined\n if (a === '--backend') {\n value = args[i + 1]\n i++\n } else if (a.startsWith('--backend=')) {\n value = a.slice('--backend='.length)\n }\n if (value === undefined) continue\n if (!(VALID_BACKENDS as readonly string[]).includes(value)) {\n throw new Error(\n `unknown --backend value: '${value}'. Valid options: ${VALID_BACKENDS.join(', ')}.`,\n )\n }\n out.push(value as BackendKind)\n }\n return out\n}\n\nconst BACKEND_CONFIG: Record<\n BackendKind,\n {\n templateDir: string\n serviceName: string\n port: number\n proxy: string\n dev: string\n start: string\n }\n> = {\n node: {\n templateDir: 'agent-node',\n serviceName: 'worker',\n port: 8002,\n proxy: '/api/worker',\n dev: 'pnpm dev',\n start: 'pnpm start',\n },\n}\n\nfunction getServiceTemplateDir(kind: BackendKind): string {\n return resolve(__dirname, '../templates/services', BACKEND_CONFIG[kind].templateDir)\n}\n\nfunction substituteTmpls(dir: string, projectName: string): void {\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const stat = statSync(full)\n if (stat.isDirectory()) {\n substituteTmpls(full, projectName)\n continue\n }\n if (entry.endsWith('.tmpl')) {\n const content = readFileSync(full, 'utf-8').replace(/\\{\\{name\\}\\}/g, projectName)\n const dest = full.replace(/\\.tmpl$/, '')\n writeFileSync(dest, content)\n unlinkSync(full)\n }\n }\n}\n\ninterface ServiceEntry {\n runtime: BackendKind\n port: number\n proxy: string\n dev: string\n start: string\n}\n\n/**\n * Build the `services: {}` snippet to inject into `theo.config.ts`.\n * Returns the inner record literal — caller wraps in `services: { ... }`.\n */\nexport function buildServicesSnippet(selections: { name: string; entry: ServiceEntry }[]): string {\n if (selections.length === 0) return ''\n const blocks = selections.map(({ name, entry }) => {\n return ` ${name}: {\n runtime: '${entry.runtime}',\n port: ${String(entry.port)},\n proxy: '${entry.proxy}',\n dev: ${JSON.stringify(entry.dev)},\n start: ${JSON.stringify(entry.start)},\n },`\n })\n return ` services: {\\n${blocks.join('\\n')}\\n },\\n`\n}\n\n/**\n * Insert the services block into an existing `theo.config.ts`.\n * Strategy: find `defineConfig({` and append the services block before the closing brace.\n */\nexport function injectServicesIntoConfig(configSource: string, snippet: string): string {\n if (snippet.length === 0) return configSource\n if (configSource.includes('services:')) return configSource // already present\n\n // Match the LAST `}` before the closing of defineConfig({...})\n const re = /defineConfig\\(\\{([\\s\\S]*?)\\}\\)/m\n const match = re.exec(configSource)\n if (!match) return configSource\n\n const inner = match[1]\n // Strip trailing whitespace without backtracking-prone \\s+$ regex.\n let trimEnd = inner.length\n while (trimEnd > 0 && /\\s/.test(inner.charAt(trimEnd - 1))) {\n trimEnd--\n }\n const trimmed = inner.slice(0, trimEnd)\n const sep = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\\n' : '\\n'\n const newInner = `${trimmed}${sep}${snippet}`\n return configSource.replace(re, `defineConfig({${newInner}})`)\n}\n\nexport function injectHeyApiDep(packageJsonSource: string): string {\n const pkg = JSON.parse(packageJsonSource) as {\n dependencies?: Record<string, string>\n }\n pkg.dependencies = pkg.dependencies ?? {}\n if (!('@hey-api/client-fetch' in pkg.dependencies)) {\n pkg.dependencies['@hey-api/client-fetch'] = '^0.6.0'\n }\n return JSON.stringify(pkg, null, 2) + '\\n'\n}\n\nexport interface ScaffoldServicesOptions {\n /** Target project directory (already scaffolded with the TS template). */\n targetDir: string\n /** Project name (substituted into .tmpl files). */\n projectName: string\n /** Which backends to scaffold. */\n backends: BackendKind[]\n}\n\nexport function scaffoldServices(options: ScaffoldServicesOptions): void {\n if (options.backends.length === 0) return\n\n const selections: { name: string; entry: ServiceEntry }[] = []\n\n for (const kind of options.backends) {\n const cfg = BACKEND_CONFIG[kind]\n const src = getServiceTemplateDir(kind)\n // Schema contract: dev/start commands run from `services/<serviceName>/` cwd\n // (services/schema/schema.ts line 35). The destination MUST equal serviceName\n // so the orchestrator's cwd resolution matches the on-disk directory.\n const dest = join(options.targetDir, 'services', cfg.serviceName)\n if (!existsSync(src)) {\n throw new Error(`service template not found: ${src}`)\n }\n cpSync(src, dest, { recursive: true })\n substituteTmpls(dest, options.projectName)\n\n selections.push({\n name: cfg.serviceName,\n entry: {\n runtime: kind,\n port: cfg.port,\n proxy: cfg.proxy,\n dev: cfg.dev,\n start: cfg.start,\n },\n })\n }\n\n // Inject services into theo.config.ts\n const configPath = join(options.targetDir, 'theo.config.ts')\n if (existsSync(configPath)) {\n const cfgSrc = readFileSync(configPath, 'utf-8')\n const snippet = buildServicesSnippet(selections)\n const updated = injectServicesIntoConfig(cfgSrc, snippet)\n if (updated !== cfgSrc) {\n writeFileSync(configPath, updated)\n }\n }\n\n // EC-10: inject @hey-api/client-fetch into user's package.json\n const pkgPath = join(options.targetDir, 'package.json')\n if (existsSync(pkgPath)) {\n const pkgSrc = readFileSync(pkgPath, 'utf-8')\n const updated = injectHeyApiDep(pkgSrc)\n writeFileSync(pkgPath, updated)\n }\n\n // Rename .gitignore for services if needed (none currently shipped, but reserved hook)\n}\n","/**\n * `create-theokit` scaffold tool. All write paths are derived from the\n * user-supplied target directory (CLI argument, resolved to absolute).\n * Read paths are the bundled `templates/` shipped with this package.\n * Build-time tool — no HTTP input.\n */\nimport {\n existsSync,\n cpSync,\n readFileSync,\n writeFileSync,\n renameSync,\n unlinkSync,\n readdirSync,\n rmSync,\n statSync,\n} from 'node:fs'\nimport { resolve, join, dirname } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { applyBareTransform } from './bare-transform.js'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nfunction getTemplateDir(templateName = 'default'): string {\n return resolve(__dirname, '../templates', templateName)\n}\n\nfunction isValidProjectName(name: string): boolean {\n return /^[a-z0-9][a-z0-9._-]*$/.test(name)\n}\n\nexport interface ScaffoldOptions {\n bare?: boolean\n /** Test-only — force the bare transform to throw to validate EC-4 rollback. */\n _testForceTransformError?: string\n}\n\nexport function scaffold(\n targetDir: string,\n projectName: string,\n templateName = 'default',\n options: ScaffoldOptions = {},\n): void {\n // EC-4 + ADR D5: --bare only applies to default template\n if (options.bare && templateName !== 'default') {\n throw new Error(\n `--bare flag only applies to the default template; got \"${templateName}\". ` +\n `Use \\`npx create-theokit <name> --template=default --bare\\` or pick a different template.`,\n )\n }\n\n const templateDir = getTemplateDir(templateName)\n\n if (!existsSync(templateDir)) {\n throw new Error(`Template \"${templateName}\" not found. Available templates: default`)\n }\n\n // Bug #1 fix: \".\" means current directory — use dir basename as project name\n const resolvedName =\n projectName === '.' ? (resolve(targetDir).split('/').pop() ?? 'my-app') : projectName\n\n if (!isValidProjectName(resolvedName)) {\n throw new Error(\n `Invalid project name \"${resolvedName}\". ` +\n `Use lowercase letters, numbers, hyphens, and dots. Must start with a letter or number.`,\n )\n }\n\n if (existsSync(targetDir)) {\n const contents = readdirSync(targetDir)\n if (contents.length > 0) {\n throw new Error(`Directory \"${targetDir}\" is not empty. Please use an empty directory.`)\n }\n }\n\n cpSync(templateDir, targetDir, { recursive: true })\n\n const gitignoreSrc = join(targetDir, '_gitignore')\n const gitignoreDest = join(targetDir, '.gitignore')\n if (existsSync(gitignoreSrc)) {\n renameSync(gitignoreSrc, gitignoreDest)\n }\n\n // dot-claude/ → .claude/ (npm ignores dotfolders in published packages)\n const dotClaudeSrc = join(targetDir, 'dot-claude')\n const dotClaudeDest = join(targetDir, '.claude')\n if (existsSync(dotClaudeSrc)) {\n renameSync(dotClaudeSrc, dotClaudeDest)\n }\n\n // Apply {{name}} substitution to every `*.tmpl` file in the target dir.\n // Each `foo.tmpl` becomes `foo` with placeholders replaced. Walks only\n // the project root (deeper subfolders don't currently need templating).\n for (const entry of readdirSync(targetDir)) {\n if (!entry.endsWith('.tmpl')) continue\n const src = join(targetDir, entry)\n const stat = statSync(src)\n if (!stat.isFile()) continue\n const dst = join(targetDir, entry.slice(0, -'.tmpl'.length))\n const content = readFileSync(src, 'utf-8')\n const replaced = content.replace(/\\{\\{name\\}\\}/g, resolvedName)\n writeFileSync(dst, replaced)\n unlinkSync(src)\n }\n\n // T4.1 — Apply --bare transform with EC-4 atomic rollback\n if (options.bare) {\n try {\n applyBareTransform(targetDir, {\n _testForceError: options._testForceTransformError,\n })\n } catch (err) {\n // EC-4: roll back partial state\n rmSync(targetDir, { recursive: true, force: true })\n const original = err instanceof Error ? err.message : String(err)\n throw new Error(\n `Scaffold rolled back: bare transform failed. Check filesystem perms.\\nOriginal error: ${original}`,\n )\n }\n }\n}\n","/**\n * Scaffold transform. Mutates files inside the freshly-created target\n * directory whose absolute path is the function input. No HTTP input.\n */\n/**\n * T4.1 — `--bare` transformation.\n *\n * Applied AFTER the default template is copied. Removes:\n * - `@theokit/ui` from `package.json` dependencies (TheoUI bundled components)\n * - `@theokit/sdk` from `package.json` dependencies (agent SDK — see below)\n * - `@theokit/agents` from `package.json` dependencies (only agents/chat.ts used it)\n * - `app/page.tsx` agent-surface content (replaces with Hello Theo)\n * - `agents/chat.ts` (the demo agent — depends on the SDK + TheoUI page)\n * - `tailwind.config.ts` + `postcss.config.js` (Tailwind toolchain — only\n * needed by the @theokit/ui-driven default surface)\n * - tailwind* + postcss* from devDependencies (toolchain cleanup)\n *\n * Why SDK removal is in --bare:\n * `@theokit/sdk` is not yet on the public npm registry (operator-deferred\n * publish per macro roadmap item #3). A user running `npx create-theokit`\n * without `--bare` hits `npm install` → 404. The `--bare` path produces a\n * scaffold that ALWAYS works without registry dependencies — Hello Theo\n * with a clean structure to grow into.\n *\n * EC-4: callers MUST wrap this in try/catch + `rmSync` rollback so a partial\n * transform never leaves the target dir in a broken state.\n */\n\nimport { existsSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs'\nimport { join } from 'node:path'\n\nconst HELLO_PAGE = `export default function Page() {\n return <h1>Hello Theo</h1>\n}\n`\n\nexport interface BareTransformOptions {\n /** Test-only — force a synthetic write failure to validate rollback path. */\n _testForceError?: string\n}\n\nexport function applyBareTransform(targetDir: string, options: BareTransformOptions = {}): void {\n if (options._testForceError) {\n throw new Error(`Forced transform failure: ${options._testForceError}`)\n }\n\n // 1. Remove @theokit/ui + @theokit/sdk + tailwind toolchain from deps\n const pkgPath = join(targetDir, 'package.json')\n if (existsSync(pkgPath)) {\n interface PartialPackageJson {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n [key: string]: unknown\n }\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as PartialPackageJson\n if (pkg.dependencies) {\n delete pkg.dependencies['@theokit/ui']\n // Drop SDK — operator-deferred npm publish (macro roadmap item #3).\n // Without removal, `npm install` hits 404 for any consumer outside\n // the workspace.\n delete pkg.dependencies['@theokit/sdk']\n // --bare removes the demo agent (agents/chat.ts), so its runtime dep\n // (@theokit/agents, imported only by that file) goes with it.\n delete pkg.dependencies['@theokit/agents']\n // lucide-react ships with the TheoUI surface; --bare doesn't render\n // any icons so it's safe to drop.\n delete pkg.dependencies['lucide-react']\n }\n if (pkg.devDependencies) {\n // Tailwind toolchain is only needed by the @theokit/ui-driven default\n // surface. --bare ships unstyled Hello Theo; no Tailwind required.\n delete pkg.devDependencies.tailwindcss\n delete pkg.devDependencies['tailwindcss-animate']\n delete pkg.devDependencies.postcss\n delete pkg.devDependencies.autoprefixer\n }\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`)\n }\n\n // 2. Replace app/page.tsx with Hello Theo\n const pagePath = join(targetDir, 'app/page.tsx')\n if (existsSync(pagePath)) {\n writeFileSync(pagePath, HELLO_PAGE)\n }\n\n // 3. Remove the demo chat agent (the `agents/chat.ts` file + its TheoUI page)\n const chatPath = join(targetDir, 'agents/chat.ts')\n if (existsSync(chatPath)) {\n unlinkSync(chatPath)\n }\n\n // 4. Remove tailwind + postcss config files (toolchain dropped from devDeps)\n const tailwindCfg = join(targetDir, 'tailwind.config.ts')\n if (existsSync(tailwindCfg)) {\n unlinkSync(tailwindCfg)\n }\n const postcssCfg = join(targetDir, 'postcss.config.js')\n if (existsSync(postcssCfg)) {\n unlinkSync(postcssCfg)\n }\n}\n"],"mappings":";;;AAAA,SAAS,gBAAgB;AACzB;AAAA,EACE,gBAAAA;AAAA,EACA,iBAAAC;AAAA,EACA,cAAAC;AAAA,EACA;AAAA,EACA,cAAAC;AAAA,EACA,cAAAC;AAAA,EACA,UAAAC;AAAA,OACK;AACP,SAAS,WAAAC,UAAS,QAAAC,aAAY;;;ACV9B,OAAO,WAAW;AAIX,SAAS,WAAW,KAAa,YAA8B;AACpE,QAAM,SAAS,MAAM,KAAK,YAAY,CAAC,SAAS,GAAG;AAAA,IACjD;AAAA,IACA,OAAO;AAAA,IACP,KAAK,EAAE,GAAG,QAAQ,KAAK,UAAU,cAAc;AAAA,EACjD,CAAC;AAED,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,MAAM,uCAAuC,UAAU,EAAE;AAAA,EACrE;AACF;;;ACZO,SAAS,mBAA+B;AAC7C,QAAM,KAAK,QAAQ,IAAI,yBAAyB;AAChD,MAAI,GAAG,WAAW,MAAM,EAAG,QAAO;AAClC,MAAI,GAAG,WAAW,MAAM,EAAG,QAAO;AAClC,MAAI,GAAG,WAAW,KAAK,EAAG,QAAO;AACjC,SAAO;AACT;;;ACIO,IAAM,mBAAmB;AAazB,SAAS,cAAc,GAAW,GAAmB;AAC1D,QAAM,QAAQ,CAAC,QAA0C;AACvD,UAAM,WAAW,IAAI,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC,KAAK;AACxD,UAAM,QAAQ,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,OAAO,SAAS,GAAG,EAAE,KAAK,CAAC;AACxE,WAAO,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;AAAA,EACrD;AACA,QAAM,CAAC,MAAM,MAAM,IAAI,IAAI,MAAM,CAAC;AAClC,QAAM,CAAC,MAAM,MAAM,IAAI,IAAI,MAAM,CAAC;AAClC,MAAI,SAAS,KAAM,QAAO,OAAO;AACjC,MAAI,SAAS,KAAM,QAAO,OAAO;AACjC,SAAO,OAAO;AAChB;AASO,SAAS,kBAAkB,YAAoB,UAAkB,kBAAwB;AAC9F,QAAM,UAAU,WAAW,QAAQ,MAAM,EAAE;AAC3C,MAAI,cAAc,YAAY,OAAO,IAAI,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,gCAAgC,OAAO;AAAA,mBACjB,OAAO;AAAA;AAAA;AAAA;AAAA,IAG/B;AAAA,EACF;AACF;;;ACjDA,SAAS,uBAAuB;AAYhC,IAAM,WAAgD;AAAA,EACpD,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AACZ;AAEA,eAAe,IAAI,IAAwC,UAAkB,YAAuC;AAClH,SAAO,IAAI,QAAQ,CAACC,aAAY;AAC9B,UAAM,SAAS,aAAa,UAAU;AACtC,OAAG,SAAS,KAAK,QAAQ,IAAI,MAAM,KAAK,CAAC,WAAW;AAClD,YAAM,IAAI,OAAO,KAAK,EAAE,YAAY;AACpC,UAAI,MAAM,GAAI,CAAAA,SAAQ,UAAU;AAAA,UAC3B,CAAAA,SAAQ,MAAM,OAAO,MAAM,KAAK;AAAA,IACvC,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAe,UAAU,IAAwC,UAAkB,SAAmB,aAAa,GAAoB;AACrI,SAAO,IAAI,QAAQ,CAACA,aAAY;AAC9B,YAAQ,IAAI,KAAK,QAAQ,EAAE;AAC3B,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,SAAS,MAAM,aAAa,WAAM;AACxC,cAAQ,IAAI,OAAO,MAAM,IAAI,QAAQ,CAAC,CAAC,EAAE;AAAA,IAC3C;AACA,OAAG,SAAS,uBAAuB,CAAC,WAAW;AAC7C,YAAM,IAAI,SAAS,OAAO,KAAK,GAAG,EAAE;AACpC,MAAAA,SAAQ,KAAK,KAAK,KAAK,QAAQ,SAAS,IAAI,IAAI,UAAU;AAAA,IAC5D,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,WAAW,aAA8C;AAC7E,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAE3E,MAAI;AACF,YAAQ,IAAI,EAAE;AACd,UAAM,OAAO,MAAM,UAAU,IAAI,2DAA2D;AAAA,MAC1F;AAAA,MACA;AAAA,IACF,GAAG,CAAC;AAEJ,QAAI,SAAS,GAAG;AACd,aAAO,EAAE,aAAa,GAAG,SAAS;AAAA,IACpC;AAEA,UAAM,aAAa,MAAM,IAAI,IAAI,qCAAqC,IAAI;AAC1E,UAAM,SAAS,MAAM,IAAI,IAAI,iCAAiC,IAAI;AAClE,UAAM,WAAW,MAAM,IAAI,IAAI,uCAAuC,IAAI;AAC1E,UAAM,SAAS,MAAM,IAAI,IAAI,uDAAuD,KAAK;AACzF,UAAM,UAAU,MAAM,IAAI,IAAI,6CAA6C,IAAI;AAC/E,UAAM,WAAW,MAAM,IAAI,IAAI,0DAA0D,IAAI;AAE7F,WAAO,EAAE,aAAa,YAAY,QAAQ,UAAU,QAAQ,SAAS,SAAS;AAAA,EAChF,UAAE;AACA,OAAG,MAAM;AAAA,EACX;AACF;AAEO,SAAS,YAAY,aAAqC;AAC/D,SAAO,EAAE,aAAa,GAAG,SAAS;AACpC;;;AClEA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS,MAAM,eAAe;AACvC,SAAS,qBAAqB;AAE9B,IAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAOxD,IAAM,iBAAiB,CAAC,MAAM;AAWvB,SAAS,kBAAkB,MAA+B;AAC/D,QAAM,MAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,KAAK,CAAC,KAAK;AACrB,QAAI;AACJ,QAAI,MAAM,aAAa;AACrB,cAAQ,KAAK,IAAI,CAAC;AAClB;AAAA,IACF,WAAW,EAAE,WAAW,YAAY,GAAG;AACrC,cAAQ,EAAE,MAAM,aAAa,MAAM;AAAA,IACrC;AACA,QAAI,UAAU,OAAW;AACzB,QAAI,CAAE,eAAqC,SAAS,KAAK,GAAG;AAC1D,YAAM,IAAI;AAAA,QACR,6BAA6B,KAAK,qBAAqB,eAAe,KAAK,IAAI,CAAC;AAAA,MAClF;AAAA,IACF;AACA,QAAI,KAAK,KAAoB;AAAA,EAC/B;AACA,SAAO;AACT;AAEA,IAAM,iBAUF;AAAA,EACF,MAAM;AAAA,IACJ,aAAa;AAAA,IACb,aAAa;AAAA,IACb,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,IACL,OAAO;AAAA,EACT;AACF;AAEA,SAAS,sBAAsB,MAA2B;AACxD,SAAO,QAAQ,WAAW,yBAAyB,eAAe,IAAI,EAAE,WAAW;AACrF;AAEA,SAAS,gBAAgB,KAAa,aAA2B;AAC/D,aAAW,SAAS,YAAY,GAAG,GAAG;AACpC,UAAM,OAAO,KAAK,KAAK,KAAK;AAC5B,UAAM,OAAO,SAAS,IAAI;AAC1B,QAAI,KAAK,YAAY,GAAG;AACtB,sBAAgB,MAAM,WAAW;AACjC;AAAA,IACF;AACA,QAAI,MAAM,SAAS,OAAO,GAAG;AAC3B,YAAM,UAAU,aAAa,MAAM,OAAO,EAAE,QAAQ,iBAAiB,WAAW;AAChF,YAAM,OAAO,KAAK,QAAQ,WAAW,EAAE;AACvC,oBAAc,MAAM,OAAO;AAC3B,iBAAW,IAAI;AAAA,IACjB;AAAA,EACF;AACF;AAcO,SAAS,qBAAqB,YAA6D;AAChG,MAAI,WAAW,WAAW,EAAG,QAAO;AACpC,QAAM,SAAS,WAAW,IAAI,CAAC,EAAE,MAAM,MAAM,MAAM;AACjD,WAAO,OAAO,IAAI;AAAA,kBACJ,MAAM,OAAO;AAAA,cACjB,OAAO,MAAM,IAAI,CAAC;AAAA,gBAChB,MAAM,KAAK;AAAA,aACd,KAAK,UAAU,MAAM,GAAG,CAAC;AAAA,eACvB,KAAK,UAAU,MAAM,KAAK,CAAC;AAAA;AAAA,EAExC,CAAC;AACD,SAAO;AAAA,EAAkB,OAAO,KAAK,IAAI,CAAC;AAAA;AAAA;AAC5C;AAMO,SAAS,yBAAyB,cAAsB,SAAyB;AACtF,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,aAAa,SAAS,WAAW,EAAG,QAAO;AAG/C,QAAM,KAAK;AACX,QAAM,QAAQ,GAAG,KAAK,YAAY;AAClC,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,MAAM,CAAC;AAErB,MAAI,UAAU,MAAM;AACpB,SAAO,UAAU,KAAK,KAAK,KAAK,MAAM,OAAO,UAAU,CAAC,CAAC,GAAG;AAC1D;AAAA,EACF;AACA,QAAM,UAAU,MAAM,MAAM,GAAG,OAAO;AACtC,QAAM,MAAM,QAAQ,SAAS,KAAK,CAAC,QAAQ,SAAS,GAAG,IAAI,QAAQ;AACnE,QAAM,WAAW,GAAG,OAAO,GAAG,GAAG,GAAG,OAAO;AAC3C,SAAO,aAAa,QAAQ,IAAI,iBAAiB,QAAQ,IAAI;AAC/D;AAEO,SAAS,gBAAgB,mBAAmC;AACjE,QAAM,MAAM,KAAK,MAAM,iBAAiB;AAGxC,MAAI,eAAe,IAAI,gBAAgB,CAAC;AACxC,MAAI,EAAE,2BAA2B,IAAI,eAAe;AAClD,QAAI,aAAa,uBAAuB,IAAI;AAAA,EAC9C;AACA,SAAO,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI;AACxC;AAWO,SAAS,iBAAiB,SAAwC;AACvE,MAAI,QAAQ,SAAS,WAAW,EAAG;AAEnC,QAAM,aAAsD,CAAC;AAE7D,aAAW,QAAQ,QAAQ,UAAU;AACnC,UAAM,MAAM,eAAe,IAAI;AAC/B,UAAM,MAAM,sBAAsB,IAAI;AAItC,UAAM,OAAO,KAAK,QAAQ,WAAW,YAAY,IAAI,WAAW;AAChE,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,YAAM,IAAI,MAAM,+BAA+B,GAAG,EAAE;AAAA,IACtD;AACA,WAAO,KAAK,MAAM,EAAE,WAAW,KAAK,CAAC;AACrC,oBAAgB,MAAM,QAAQ,WAAW;AAEzC,eAAW,KAAK;AAAA,MACd,MAAM,IAAI;AAAA,MACV,OAAO;AAAA,QACL,SAAS;AAAA,QACT,MAAM,IAAI;AAAA,QACV,OAAO,IAAI;AAAA,QACX,KAAK,IAAI;AAAA,QACT,OAAO,IAAI;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,aAAa,KAAK,QAAQ,WAAW,gBAAgB;AAC3D,MAAI,WAAW,UAAU,GAAG;AAC1B,UAAM,SAAS,aAAa,YAAY,OAAO;AAC/C,UAAM,UAAU,qBAAqB,UAAU;AAC/C,UAAM,UAAU,yBAAyB,QAAQ,OAAO;AACxD,QAAI,YAAY,QAAQ;AACtB,oBAAc,YAAY,OAAO;AAAA,IACnC;AAAA,EACF;AAGA,QAAM,UAAU,KAAK,QAAQ,WAAW,cAAc;AACtD,MAAI,WAAW,OAAO,GAAG;AACvB,UAAM,SAAS,aAAa,SAAS,OAAO;AAC5C,UAAM,UAAU,gBAAgB,MAAM;AACtC,kBAAc,SAAS,OAAO;AAAA,EAChC;AAGF;;;AChOA;AAAA,EACE,cAAAC;AAAA,EACA,UAAAC;AAAA,EACA,gBAAAC;AAAA,EACA,iBAAAC;AAAA,EACA;AAAA,EACA,cAAAC;AAAA,EACA,eAAAC;AAAA,EACA;AAAA,EACA,YAAAC;AAAA,OACK;AACP,SAAS,WAAAC,UAAS,QAAAC,OAAM,WAAAC,gBAAe;AACvC,SAAS,iBAAAC,sBAAqB;;;ACU9B,SAAS,cAAAC,aAAY,gBAAAC,eAAc,iBAAAC,gBAAe,cAAAC,mBAAkB;AACpE,SAAS,QAAAC,aAAY;AAErB,IAAM,aAAa;AAAA;AAAA;AAAA;AAUZ,SAAS,mBAAmB,WAAmB,UAAgC,CAAC,GAAS;AAC9F,MAAI,QAAQ,iBAAiB;AAC3B,UAAM,IAAI,MAAM,6BAA6B,QAAQ,eAAe,EAAE;AAAA,EACxE;AAGA,QAAM,UAAUA,MAAK,WAAW,cAAc;AAC9C,MAAIJ,YAAW,OAAO,GAAG;AAMvB,UAAM,MAAM,KAAK,MAAMC,cAAa,SAAS,OAAO,CAAC;AACrD,QAAI,IAAI,cAAc;AACpB,aAAO,IAAI,aAAa,aAAa;AAIrC,aAAO,IAAI,aAAa,cAAc;AAGtC,aAAO,IAAI,aAAa,iBAAiB;AAGzC,aAAO,IAAI,aAAa,cAAc;AAAA,IACxC;AACA,QAAI,IAAI,iBAAiB;AAGvB,aAAO,IAAI,gBAAgB;AAC3B,aAAO,IAAI,gBAAgB,qBAAqB;AAChD,aAAO,IAAI,gBAAgB;AAC3B,aAAO,IAAI,gBAAgB;AAAA,IAC7B;AACA,IAAAC,eAAc,SAAS,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA,CAAI;AAAA,EAC5D;AAGA,QAAM,WAAWE,MAAK,WAAW,cAAc;AAC/C,MAAIJ,YAAW,QAAQ,GAAG;AACxB,IAAAE,eAAc,UAAU,UAAU;AAAA,EACpC;AAGA,QAAM,WAAWE,MAAK,WAAW,gBAAgB;AACjD,MAAIJ,YAAW,QAAQ,GAAG;AACxB,IAAAG,YAAW,QAAQ;AAAA,EACrB;AAGA,QAAM,cAAcC,MAAK,WAAW,oBAAoB;AACxD,MAAIJ,YAAW,WAAW,GAAG;AAC3B,IAAAG,YAAW,WAAW;AAAA,EACxB;AACA,QAAM,aAAaC,MAAK,WAAW,mBAAmB;AACtD,MAAIJ,YAAW,UAAU,GAAG;AAC1B,IAAAG,YAAW,UAAU;AAAA,EACvB;AACF;;;AD9EA,IAAME,aAAYC,SAAQC,eAAc,YAAY,GAAG,CAAC;AAExD,SAAS,eAAe,eAAe,WAAmB;AACxD,SAAOC,SAAQH,YAAW,gBAAgB,YAAY;AACxD;AAEA,SAAS,mBAAmB,MAAuB;AACjD,SAAO,yBAAyB,KAAK,IAAI;AAC3C;AAQO,SAAS,SACd,WACA,aACA,eAAe,WACf,UAA2B,CAAC,GACtB;AAEN,MAAI,QAAQ,QAAQ,iBAAiB,WAAW;AAC9C,UAAM,IAAI;AAAA,MACR,0DAA0D,YAAY;AAAA,IAExE;AAAA,EACF;AAEA,QAAM,cAAc,eAAe,YAAY;AAE/C,MAAI,CAACI,YAAW,WAAW,GAAG;AAC5B,UAAM,IAAI,MAAM,aAAa,YAAY,2CAA2C;AAAA,EACtF;AAGA,QAAM,eACJ,gBAAgB,MAAOD,SAAQ,SAAS,EAAE,MAAM,GAAG,EAAE,IAAI,KAAK,WAAY;AAE5E,MAAI,CAAC,mBAAmB,YAAY,GAAG;AACrC,UAAM,IAAI;AAAA,MACR,yBAAyB,YAAY;AAAA,IAEvC;AAAA,EACF;AAEA,MAAIC,YAAW,SAAS,GAAG;AACzB,UAAM,WAAWC,aAAY,SAAS;AACtC,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,IAAI,MAAM,cAAc,SAAS,gDAAgD;AAAA,IACzF;AAAA,EACF;AAEA,EAAAC,QAAO,aAAa,WAAW,EAAE,WAAW,KAAK,CAAC;AAElD,QAAM,eAAeC,MAAK,WAAW,YAAY;AACjD,QAAM,gBAAgBA,MAAK,WAAW,YAAY;AAClD,MAAIH,YAAW,YAAY,GAAG;AAC5B,eAAW,cAAc,aAAa;AAAA,EACxC;AAGA,QAAM,eAAeG,MAAK,WAAW,YAAY;AACjD,QAAM,gBAAgBA,MAAK,WAAW,SAAS;AAC/C,MAAIH,YAAW,YAAY,GAAG;AAC5B,eAAW,cAAc,aAAa;AAAA,EACxC;AAKA,aAAW,SAASC,aAAY,SAAS,GAAG;AAC1C,QAAI,CAAC,MAAM,SAAS,OAAO,EAAG;AAC9B,UAAM,MAAME,MAAK,WAAW,KAAK;AACjC,UAAM,OAAOC,UAAS,GAAG;AACzB,QAAI,CAAC,KAAK,OAAO,EAAG;AACpB,UAAM,MAAMD,MAAK,WAAW,MAAM,MAAM,GAAG,CAAC,QAAQ,MAAM,CAAC;AAC3D,UAAM,UAAUE,cAAa,KAAK,OAAO;AACzC,UAAM,WAAW,QAAQ,QAAQ,iBAAiB,YAAY;AAC9D,IAAAC,eAAc,KAAK,QAAQ;AAC3B,IAAAC,YAAW,GAAG;AAAA,EAChB;AAGA,MAAI,QAAQ,MAAM;AAChB,QAAI;AACF,yBAAmB,WAAW;AAAA,QAC5B,iBAAiB,QAAQ;AAAA,MAC3B,CAAC;AAAA,IACH,SAAS,KAAK;AAEZ,aAAO,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAClD,YAAM,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAChE,YAAM,IAAI;AAAA,QACR;AAAA,kBAAyF,QAAQ;AAAA,MACnG;AAAA,IACF;AAAA,EACF;AACF;;;ANrGA,IAAM,UAAU;AAEhB,SAAS,QAAQ,MAAgB,MAAkC;AACjE,QAAM,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,CAAC;AACxD,SAAO,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC,IAAI;AACrC;AAEA,SAAS,QAAQ,SAAmB,OAA0B;AAC5D,SAAO,MAAM,KAAK,CAAC,MAAM,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC;AAC5E;AAEA,SAAS,WAAiB;AACxB,UAAQ,IAAI;AAAA,oBACM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CA4B1B;AACD;AAEA,eAAsB,OAAsB;AAC1C,MAAI;AACF,sBAAkB,QAAQ,OAAO;AAAA,EACnC,SAAS,KAAK;AACZ,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AAGjC,MAAI,QAAQ,MAAM,KAAK,MAAM,GAAG;AAC9B,aAAS;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,QAAQ,MAAM,KAAK,SAAS,GAAG;AACjC,YAAQ,IAAI,OAAO;AACnB,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,iBAAiB,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,IAAI,KAAK,CAAC,EAAE,WAAW,GAAG,CAAC;AACnF,QAAM,cAAc,eAAe,CAAC;AAEpC,MAAI,CAAC,aAAa;AAChB,aAAS;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,eAAe,QAAQ,MAAM,UAAU,KAAK;AAClD,QAAM,OAAO,QAAQ,MAAM,MAAM;AACjC,QAAM,cAAc,QAAQ,MAAM,cAAc;AAChD,QAAM,cAAc,QAAQ,MAAM,KAAK;AACvC,QAAM,aAAa,QAAQ,MAAM,aAAa;AAC9C,QAAM,WAAW,QAAQ,MAAM,OAAO;AACtC,QAAM,cAAc,QAAQ,MAAM,SAAS;AAC3C,QAAM,cAAc,QAAQ,MAAM,cAAc,KAAK;AAGrD,MAAI;AACJ,MAAI,QAAQ,MAAM,SAAS,EAAG,sBAAqB;AAAA,WAC1C,QAAQ,MAAM,UAAU,EAAG,sBAAqB;AAAA,WAChD,QAAQ,MAAM,UAAU,EAAG,sBAAqB;AAAA,WAChD,QAAQ,MAAM,SAAS,EAAG,sBAAqB;AAExD,MAAI,WAA0B,CAAC;AAC/B,MAAI;AACF,eAAW,kBAAkB,IAAI;AAAA,EACnC,SAAS,KAAK;AACZ,YAAQ,MAAM,EAAE;AAChB,YAAQ,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,YAAYC,SAAQ,QAAQ,IAAI,GAAG,WAAW;AAGpD,MAAI,aAAa;AACf,QAAI;AACF,cAAQ,IAAI;AAAA,mBAAsB,WAAW,UAAU,WAAW;AAAA,CAAO;AACzE,mBAAa,aAAa,SAAS;AACnC,YAAM,aAAa,sBAAsB,iBAAiB;AAC1D,UAAI,CAAC,aAAa;AAChB,gBAAQ,IAAI,gCAAgC,UAAU;AAAA,CAAO;AAC7D,mBAAW,WAAW,UAAU;AAAA,MAClC;AACA,UAAI,CAAC,WAAY,SAAQ,SAAS;AAClC,cAAQ,IAAI;AAAA,6BAA2B,SAAS;AAAA,CAAI;AACpD,cAAQ,IAAI,UAAU,WAAW,EAAE;AACnC,cAAQ,IAAI,OAAO,eAAe,QAAQ,YAAY,UAAU;AAAA,CAAQ;AACxE,cAAQ,KAAK,CAAC;AAAA,IAChB,SAAS,KAAK;AACZ,cAAQ,MAAM;AAAA,WAAU,IAAc,OAAO;AAAA,CAAI;AACjD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAGA,MAAI;AACJ,MAAI,eAAe,MAAM;AACvB,cAAU,YAAY,WAAW;AACjC,QAAI,SAAU,SAAQ,SAAS;AAAA,EACjC,OAAO;AACL,cAAU,MAAM,WAAW,WAAW;AAAA,EACxC;AAEA,MAAI;AACF,UAAM,SAAS,OAAO,cAAc;AACpC,UAAM,iBAAiB,SAAS,SAAS,IAAI,gBAAgB,SAAS,KAAK,IAAI,CAAC,MAAM;AACtF,YAAQ;AAAA,MACN;AAAA,4BAA+B,WAAW,gBAAgB,YAAY,IAAI,MAAM,GAAG,cAAc;AAAA;AAAA,IACnG;AAEA,aAAS,WAAW,aAAa,cAAc,EAAE,KAAK,CAAC;AAGvD,iBAAa,WAAW,SAAS,EAAE,aAAa,SAAS,CAAC;AAE1D,QAAI,SAAS,SAAS,GAAG;AACvB,uBAAiB,EAAE,WAAW,aAAa,SAAS,CAAC;AACrD,cAAQ,IAAI,uBAAkB,OAAO,SAAS,MAAM,CAAC,gBAAgB,SAAS,KAAK,IAAI,CAAC;AAAA,CAAI;AAAA,IAC9F;AAEA,UAAM,aAAa,sBAAsB,iBAAiB;AAC1D,QAAI,aAAa;AACf,cAAQ,IAAI,4CAA4C,UAAU;AAAA,CAAwB;AAAA,IAC5F,OAAO;AACL,cAAQ,IAAI,gCAAgC,UAAU;AAAA,CAAO;AAC7D,iBAAW,WAAW,UAAU;AAAA,IAClC;AAGA,QAAI,CAAC,WAAY,SAAQ,SAAS;AAElC,YAAQ,IAAI;AAAA,8BAA4B,SAAS;AAAA,CAAI;AACrD,YAAQ,IAAI,iBAAiB;AAC7B,YAAQ,IAAI,UAAU,WAAW,EAAE;AACnC,QAAI,YAAa,SAAQ,IAAI,OAAO,UAAU,UAAU;AACxD,YAAQ,IAAI,OAAO,eAAe,QAAQ,YAAY,UAAU;AAAA,CAAQ;AAAA,EAC1E,SAAS,KAAK;AACZ,YAAQ,MAAM;AAAA,WAAU,IAAc,OAAO;AAAA,CAAI;AACjD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AASA,SAAS,aAAa,WAAmB,SAAyB,MAAuB;AACvF,QAAM,UAAUA,SAAQ,WAAW,cAAc;AAGjD,MAAI,KAAK,UAAU;AACjB,UAAM,aAAaA,SAAQ,WAAW,mBAAmB;AACzD,QAAIC,YAAW,UAAU,EAAG,CAAAC,YAAW,UAAU;AAEjD,UAAM,MAAM,KAAK,MAAMC,cAAa,SAAS,OAAO,CAAC;AACrD,WAAO,IAAI,gBAAgB;AAC3B,WAAO,IAAI,gBAAgB,wBAAwB;AACnD,WAAO,IAAI,gBAAgB,mBAAmB;AAC9C,QAAI,gBAAgB,gBAAgB,IAAI;AACxC,QAAI,QAAQ,OAAO;AACnB,QAAI,QAAQ,UAAU,IAAI;AAC1B,QAAI,QAAQ,SAAS;AACrB,QAAI,QAAQ,cAAc,IAAI;AAC9B,IAAAC,eAAc,SAAS,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAG1D,IAAAA;AAAA,MACEJ,SAAQ,WAAW,YAAY;AAAA,MAC/B,KAAK;AAAA,QACH;AAAA,UACE,SAAS;AAAA,UACT,iBAAiB,EAAE,SAAS,KAAK;AAAA,UACjC,QAAQ,EAAE,SAAS,MAAM,OAAO,EAAE,aAAa,KAAK,EAAE;AAAA,UACtD,WAAW,EAAE,SAAS,MAAM,aAAa,SAAS,aAAa,GAAG,WAAW,IAAI;AAAA,QACnF;AAAA,QACA;AAAA,QACA;AAAA,MACF,IAAI;AAAA,IACN;AAAA,EACF,WAAW,CAAC,QAAQ,QAAQ;AAE1B,UAAM,aAAaA,SAAQ,WAAW,mBAAmB;AACzD,QAAIC,YAAW,UAAU,EAAG,CAAAC,YAAW,UAAU;AACjD,UAAM,MAAM,KAAK,MAAMC,cAAa,SAAS,OAAO,CAAC;AACrD,WAAO,IAAI,QAAQ;AACnB,WAAO,IAAI,QAAQ,UAAU;AAC7B,WAAO,IAAI,gBAAgB;AAC3B,WAAO,IAAI,gBAAgB,wBAAwB;AACnD,WAAO,IAAI,gBAAgB,mBAAmB;AAC9C,IAAAC,eAAc,SAAS,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAAA,EAC5D;AAGA,MAAI,QAAQ,UAAU;AACpB,UAAM,MAAM,KAAK,MAAMD,cAAa,SAAS,OAAO,CAAC;AACrD,QAAI,kBAAkB,IAAI,mBAAmB,CAAC;AAC9C,QAAI,gBAAgB,cAAc;AAClC,QAAI,gBAAgB,mBAAmB,IAAI;AAC3C,IAAAC,eAAc,SAAS,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAC1D,UAAM,SAASH,YAAWD,SAAQ,WAAW,SAAS,CAAC,IAAI,YAAY;AACvE,UAAM,UAAUA,SAAQ,WAAW,GAAG,MAAM,cAAc;AAC1D,UAAM,WAAWC,YAAW,OAAO,IAAIE,cAAa,SAAS,OAAO,IAAI;AACxE,IAAAC,eAAc,SAAS,+BAA+B,QAAQ;AAAA,EAChE;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,SAASJ,SAAQ,WAAW,KAAK;AACvC,cAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AACrC,eAAW,OAAO,CAAC,OAAO,QAAQ,GAAG;AACnC,YAAM,OAAOA,SAAQ,WAAW,GAAG;AACnC,YAAM,KAAKA,SAAQ,QAAQ,GAAG;AAC9B,UAAIC,YAAW,IAAI,EAAG,CAAAI,YAAW,MAAM,EAAE;AAAA,IAC3C;AAEA,UAAM,aAAaL,SAAQ,WAAW,gBAAgB;AACtD,QAAIC,YAAW,UAAU,EAAG,CAAAI,YAAW,YAAYL,SAAQ,QAAQ,gBAAgB,CAAC;AAGpF,UAAMM,WAAUN,SAAQ,WAAW,eAAe;AAClD,UAAMO,OAAM,KAAK,MAAMJ,cAAaG,UAAS,OAAO,CAAC;AACrD,IAAAC,KAAI,gBAAgB,UAAU;AAC9B,IAAAA,KAAI,UAAU,CAAC,eAAe,cAAc;AAC5C,IAAAH,eAAcE,UAAS,KAAK,UAAUC,MAAK,MAAM,CAAC,IAAI,IAAI;AAAA,EAC5D;AAGA,QAAM,UAAUP,SAAQ,WAAW,eAAe;AAClD,QAAM,MAAM,KAAK,MAAMG,cAAa,SAAS,OAAO,CAAC;AACrD,MAAI,CAAC,QAAQ,SAAS;AACpB,WAAO,IAAI,gBAAgB;AAC3B,WAAO,IAAI,gBAAgB;AAAA,EAC7B,WAAW,KAAK,gBAAgB,OAAO;AAErC,UAAM,SAAS,KAAK,YAAY,QAAQ,MAAM,EAAE;AAChD,QAAI,gBAAgB,QAAQ;AAAA,MAC1B,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,KAAK;AAAA,MACvB,CAAC,GAAG,MAAM,WAAW,GAAG,CAAC,YAAY;AAAA,MACrC,CAAC,GAAG,MAAM,QAAQ,GAAG,CAAC,SAAS;AAAA,IACjC;AAAA,EACF;AACA,EAAAC,eAAc,SAAS,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,IAAI;AAG1D,MAAI,CAAC,QAAQ,UAAU;AACrB,UAAM,aAAaJ,SAAQ,WAAW,WAAW;AACjD,QAAIC,YAAW,UAAU,EAAG,CAAAC,YAAW,UAAU;AAAA,EACnD;AACF;AAIA,SAAS,QAAQ,WAAyB;AACxC,MAAI;AACF,aAAS,YAAY,EAAE,KAAK,WAAW,OAAO,SAAS,CAAC;AACxD,aAAS,cAAc,EAAE,KAAK,WAAW,OAAO,SAAS,CAAC;AAC1D,aAAS,kEAAkE;AAAA,MACzE,KAAK;AAAA,MACL,OAAO;AAAA,IACT,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAIA,SAAS,aAAa,SAAiB,WAAyB;AAC9D,QAAM,QAAQ,QAAQ,WAAW,SAAS,KAAK,QAAQ,WAAW,UAAU;AAC5E,QAAM,OAAO,QACT,UACA,4DAA4D,OAAO;AAEvE,MAAI,OAAO;AAET,aAAS,uBAAuB,IAAI,IAAI,SAAS,IAAI,EAAE,OAAO,UAAU,CAAC;AAEzE,IAAAM,QAAOC,MAAK,WAAW,MAAM,GAAG,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EAClE,OAAO;AAEL,QAAI;AACF,eAAS,+CAA+C,OAAO,IAAI,SAAS,IAAI;AAAA,QAC9E,OAAO;AAAA,MACT,CAAC;AAAA,IACH,QAAQ;AACN,YAAM,IAAI;AAAA,QACR,YAAY,OAAO;AAAA,MAErB;AAAA,IACF;AAAA,EACF;AACF;AAGA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["readFileSync","writeFileSync","unlinkSync","renameSync","existsSync","rmSync","resolve","join","resolve","existsSync","cpSync","readFileSync","writeFileSync","unlinkSync","readdirSync","statSync","resolve","join","dirname","fileURLToPath","existsSync","readFileSync","writeFileSync","unlinkSync","join","__dirname","dirname","fileURLToPath","resolve","existsSync","readdirSync","cpSync","join","statSync","readFileSync","writeFileSync","unlinkSync","resolve","existsSync","unlinkSync","readFileSync","writeFileSync","renameSync","tscPath","tsc","rmSync","join"]}
|
package/package.json
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-theokit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Scaffold a new TheoKit project",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"build": "tsup",
|
|
9
|
-
"test": "vitest run",
|
|
10
|
-
"test:watch": "vitest"
|
|
11
|
-
},
|
|
12
7
|
"bin": {
|
|
13
8
|
"create-theokit": "./dist/cli.js"
|
|
14
9
|
},
|
|
@@ -20,10 +15,14 @@
|
|
|
20
15
|
"cross-spawn": "^7.0.6"
|
|
21
16
|
},
|
|
22
17
|
"publishConfig": {
|
|
23
|
-
"provenance": true,
|
|
24
18
|
"access": "public"
|
|
25
19
|
},
|
|
26
20
|
"engines": {
|
|
27
21
|
"node": ">=22.12.0"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest"
|
|
28
27
|
}
|
|
29
|
-
}
|
|
28
|
+
}
|
|
@@ -11,8 +11,8 @@ This project includes TheoKit-aware skills that activate automatically when you
|
|
|
11
11
|
| theokit-routes | `server/routes/**` | defineRoute API, Zod validation, HTTP methods, dynamic params |
|
|
12
12
|
| theokit-agents | `**/*agent*`, `**/*tool*`, `**/*Agent*`, `**/*Tool*` | @Agent, @Tool, @Toolbox decorators, LLM integration |
|
|
13
13
|
| theokit-database | `**/*schema*`, `**/*db*`, `**/drizzle*`, `**/*migration*`, `**/*seed*` | Drizzle ORM, SQLite, schema patterns, migrations |
|
|
14
|
-
| theokit-frontend | `app/**` | File-based routing, layouts, theoFetch,
|
|
15
|
-
| theokit-ui | `app/**`, `**/*Chat*`, `**/*Sidebar*`, `**/*Theme*` | @theokit/ui components: ChatThread, ChatMessage,
|
|
14
|
+
| theokit-frontend | `app/**` | File-based routing, layouts, theoFetch, useAgent |
|
|
15
|
+
| theokit-ui | `app/**`, `**/*Chat*`, `**/*Sidebar*`, `**/*Theme*` | @theokit/ui AI components: ChatThread, ChatMessage, ToolCallCard, theming (generic primitives like CodeBlock/Sidebar come from @usetheo/ui) |
|
|
16
16
|
| theokit-config | `theo.config*`, `**/*config*` | defineConfig options, plugins, security, storage |
|
|
17
17
|
|
|
18
18
|
### Settings
|
|
@@ -16,9 +16,9 @@ npm install
|
|
|
16
16
|
npm run dev
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Open the app and chat with the agent.
|
|
20
|
-
|
|
21
|
-
Ollama / OpenAI from env vars
|
|
19
|
+
Open the app and chat with the agent. Edit the agent at `agents/chat.ts`
|
|
20
|
+
(pick your model / add tools) — the `@theokit/sdk` runtime resolves OpenRouter /
|
|
21
|
+
Anthropic / Ollama / OpenAI from env vars (see the file's header comment).
|
|
22
22
|
|
|
23
23
|
## Project Structure
|
|
24
24
|
|
|
@@ -40,9 +40,10 @@ Ollama / OpenAI from env vars — see the file's header comment).
|
|
|
40
40
|
|
|
41
41
|
- **Agent chat surface** — `app/page.tsx` renders `@theokit/ui` chat components
|
|
42
42
|
driven by the streaming endpoint.
|
|
43
|
-
- **`
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
- **`agents/chat.ts` (`defineAgent`)** — one file auto-served at `POST /api/agents/chat`,
|
|
44
|
+
streaming the ai-sdk `UIMessageStream` wire; `useAgent('/api/agents/chat')` on the client
|
|
45
|
+
reconstructs `UIMessage[]` you render via `message.parts`. `@theokit/sdk` owns
|
|
46
|
+
conversation persistence and provider resolution.
|
|
46
47
|
- **`defineAgentTool`** — declare typed tools (the template ships a
|
|
47
48
|
`current_time` example).
|
|
48
49
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineAgent } from '@theokit/agents'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Chat agent — the zero-config `agents/*.ts` convention.
|
|
6
|
+
*
|
|
7
|
+
* This one file is auto-served at `POST /api/agents/chat` (dev + build), streaming the
|
|
8
|
+
* ai-sdk `UIMessageStream` that `useAgent('chat')` consumes on the client. No manual route,
|
|
9
|
+
* no manual client wiring. `@theokit/sdk` runs it; conversation turns auto-persist per
|
|
10
|
+
* session (the SDK owns storage).
|
|
11
|
+
*
|
|
12
|
+
* Provider: resolved from the environment — OPENROUTER_API_KEY (preferred — gateway to many
|
|
13
|
+
* models) OR ANTHROPIC_API_KEY / OPENAI_API_KEY. The `model` id is prefixed with the provider
|
|
14
|
+
* namespace so OpenRouter routes it upstream (see https://openrouter.ai/models).
|
|
15
|
+
*
|
|
16
|
+
* Add tools with `defineAgentTool` (from `theokit/server`) and pass them to `tools: [...]`.
|
|
17
|
+
*/
|
|
18
|
+
export default defineAgent({
|
|
19
|
+
input: z.object({ message: z.string() }),
|
|
20
|
+
model: 'openai/gpt-4o-mini',
|
|
21
|
+
system: 'You are a helpful assistant.',
|
|
22
|
+
})
|
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
import '@theokit/ui/styles.css'
|
|
2
2
|
|
|
3
3
|
import { Outlet } from 'react-router'
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
TopNav,
|
|
7
|
-
AgentProfile,
|
|
8
|
-
ThemeSwitcher,
|
|
9
|
-
Tooltip,
|
|
10
|
-
Badge,
|
|
11
|
-
CostMeter,
|
|
12
|
-
type AgentProfileDescriptor,
|
|
13
|
-
} from '@theokit/ui'
|
|
4
|
+
import { AgentProfile, ThemeSwitcher, CostMeter, type AgentProfileDescriptor } from '@theokit/ui'
|
|
5
|
+
import { Sidebar, TopNav, Tooltip, Badge } from '@usetheo/ui'
|
|
14
6
|
import { Bot, MessageSquare, History, Settings } from 'lucide-react'
|
|
15
7
|
|
|
16
8
|
const AGENTS: AgentProfileDescriptor[] = [
|
|
17
9
|
{
|
|
18
10
|
id: 'theo',
|
|
19
11
|
name: 'Theo',
|
|
20
|
-
description: '
|
|
12
|
+
description: 'Edit the agent at agents/chat.ts.',
|
|
21
13
|
tone: 'primary',
|
|
22
14
|
initials: 'TH',
|
|
23
15
|
badge: 'Online',
|
|
@@ -8,21 +8,23 @@ import {
|
|
|
8
8
|
ToolCallCard,
|
|
9
9
|
AgentStreaming,
|
|
10
10
|
AgentErrorCard,
|
|
11
|
-
EmptyState,
|
|
12
11
|
QuickActionChips,
|
|
13
12
|
ContextWindowBar,
|
|
13
|
+
type Message,
|
|
14
|
+
type QuickAction,
|
|
15
|
+
type ToolCallStatus,
|
|
16
|
+
} from '@theokit/ui'
|
|
17
|
+
import {
|
|
18
|
+
EmptyState,
|
|
14
19
|
CommandPalette,
|
|
15
20
|
Avatar,
|
|
16
21
|
Tooltip,
|
|
17
22
|
Button,
|
|
18
23
|
ScrollArea,
|
|
19
|
-
type Message,
|
|
20
|
-
type QuickAction,
|
|
21
24
|
type CommandItem,
|
|
22
|
-
|
|
23
|
-
} from '@theokit/ui'
|
|
25
|
+
} from '@usetheo/ui'
|
|
24
26
|
import { Sparkles, Wrench, RotateCcw, Command } from 'lucide-react'
|
|
25
|
-
import {
|
|
27
|
+
import { useAgent } from 'theokit/client'
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
30
|
* Default scaffold — an Agent Surface, composed entirely from TheoUI.
|
|
@@ -38,9 +40,9 @@ import { useAgentStream } from 'theokit/client'
|
|
|
38
40
|
* Avatar → assistant face in messages
|
|
39
41
|
* Tooltip → hints on icons
|
|
40
42
|
*
|
|
41
|
-
* `
|
|
42
|
-
*
|
|
43
|
-
*
|
|
43
|
+
* `useAgent` binds to the `agents/chat.ts` endpoint, consumes the ai-sdk
|
|
44
|
+
* `UIMessageStream`, and handles AbortController cleanup + StrictMode safety.
|
|
45
|
+
* Edit `agents/chat.ts` to pick your model / add tools.
|
|
44
46
|
*/
|
|
45
47
|
|
|
46
48
|
type ConversationItem =
|
|
@@ -69,10 +71,11 @@ const COMMAND_ITEMS: CommandItem[] = QUICK_ACTIONS.map((a) => ({
|
|
|
69
71
|
group: 'Quick actions',
|
|
70
72
|
}))
|
|
71
73
|
|
|
72
|
-
//
|
|
74
|
+
// Display-only context-window hint. The agent's real model lives in `agents/chat.ts`
|
|
75
|
+
// (`model: 'openai/gpt-4o-mini'`); wire real token counts from the stream when you need them.
|
|
73
76
|
const CONTEXT_USED = 4_200
|
|
74
77
|
const CONTEXT_TOTAL = 200_000
|
|
75
|
-
const MODEL_NAME = '
|
|
78
|
+
const MODEL_NAME = 'gpt-4o-mini'
|
|
76
79
|
|
|
77
80
|
const ASSISTANT_AVATAR = (
|
|
78
81
|
<Avatar size="sm" tone="primary">
|
|
@@ -89,7 +92,7 @@ export default function Page() {
|
|
|
89
92
|
const [composerValue, setComposerValue] = useState('')
|
|
90
93
|
const [userMessages, setUserMessages] = useState<ConversationItem[]>([])
|
|
91
94
|
const [paletteOpen, setPaletteOpen] = useState(false)
|
|
92
|
-
const {
|
|
95
|
+
const { messages, send, status, reset } = useAgent<{ message: string }>('/api/agents/chat')
|
|
93
96
|
|
|
94
97
|
// ⌘K / Ctrl+K opens the CommandPalette.
|
|
95
98
|
useEffect(() => {
|
|
@@ -103,43 +106,55 @@ export default function Page() {
|
|
|
103
106
|
return () => window.removeEventListener('keydown', onKey)
|
|
104
107
|
}, [])
|
|
105
108
|
|
|
109
|
+
// Derive the conversation view from the reconstructed assistant UIMessages
|
|
110
|
+
// (ai-sdk `UIMessageStream`): text parts → messages, tool parts → tool cards.
|
|
106
111
|
const items = useMemo<ConversationItem[]>(() => {
|
|
107
112
|
const ts = new Date().toISOString()
|
|
108
|
-
const agentItems: ConversationItem[] =
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
const agentItems: ConversationItem[] = []
|
|
114
|
+
for (const message of messages) {
|
|
115
|
+
if (message.role !== 'assistant') continue
|
|
116
|
+
message.parts.forEach((part, i) => {
|
|
117
|
+
const id = `${message.id}-${i}`
|
|
118
|
+
if (part.type === 'text') {
|
|
119
|
+
agentItems.push({
|
|
120
|
+
kind: 'message',
|
|
121
|
+
id,
|
|
122
|
+
role: 'assistant',
|
|
123
|
+
content: part.text,
|
|
124
|
+
timestamp: ts,
|
|
125
|
+
})
|
|
126
|
+
} else if (part.type === 'dynamic-tool') {
|
|
127
|
+
agentItems.push({
|
|
115
128
|
kind: 'tool',
|
|
116
129
|
id,
|
|
117
|
-
tool:
|
|
130
|
+
tool: part.toolName,
|
|
118
131
|
target:
|
|
119
|
-
typeof
|
|
120
|
-
? Object.entries(
|
|
132
|
+
part.input && typeof part.input === 'object'
|
|
133
|
+
? Object.entries(part.input as Record<string, unknown>)
|
|
121
134
|
.map(([k, v]) => `${k}=${JSON.stringify(v)}`)
|
|
122
135
|
.join(' ')
|
|
123
136
|
: undefined,
|
|
124
|
-
status:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
id,
|
|
131
|
-
tool: event.name,
|
|
132
|
-
status: 'success',
|
|
137
|
+
status:
|
|
138
|
+
part.state === 'output-available'
|
|
139
|
+
? 'success'
|
|
140
|
+
: part.state === 'output-error'
|
|
141
|
+
? 'error'
|
|
142
|
+
: 'running',
|
|
133
143
|
output:
|
|
134
|
-
|
|
144
|
+
part.state === 'output-available'
|
|
145
|
+
? typeof part.output === 'string'
|
|
146
|
+
? part.output
|
|
147
|
+
: JSON.stringify(part.output, null, 2)
|
|
148
|
+
: part.state === 'output-error'
|
|
149
|
+
? part.errorText
|
|
150
|
+
: undefined,
|
|
135
151
|
timestamp: ts,
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
}
|
|
141
156
|
return [...userMessages, ...agentItems]
|
|
142
|
-
}, [userMessages,
|
|
157
|
+
}, [userMessages, messages])
|
|
143
158
|
|
|
144
159
|
function handleSubmit(value: string) {
|
|
145
160
|
const trimmed = value.trim()
|
|
@@ -186,7 +201,7 @@ export default function Page() {
|
|
|
186
201
|
eyebrow="Theo Agent"
|
|
187
202
|
icon={Sparkles}
|
|
188
203
|
title="What should we build today?"
|
|
189
|
-
description="Ask anything. This scaffold ships
|
|
204
|
+
description="Ask anything. This scaffold ships an agent at agents/chat.ts — edit it to pick your model or add tools."
|
|
190
205
|
action={<QuickActionChips actions={QUICK_ACTIONS} onSelect={handleQuickAction} />}
|
|
191
206
|
/>
|
|
192
207
|
) : (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: theokit-agents
|
|
3
|
-
description: TheoKit agent/LLM integration —
|
|
3
|
+
description: TheoKit agent/LLM integration — agents/*.ts convention (defineAgent), @Agent decorator (advanced/DI), defineAgentTool, useAgent client hook
|
|
4
4
|
user-invocable: false
|
|
5
5
|
paths:
|
|
6
6
|
- "**/*agent*"
|
|
@@ -13,37 +13,38 @@ paths:
|
|
|
13
13
|
|
|
14
14
|
# TheoKit Agents & Tools
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Server Surface — agents/*.ts (zero-config convention)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
| Surface | Server | Events | Client | When to use |
|
|
21
|
-
|---------|--------|--------|--------|-------------|
|
|
22
|
-
| **Manual** (recommended for most apps) | `defineAgentEndpoint({ handler: async function* })` | `AgentEvent` (Message, ToolCall, Result, Error) | `useAgentStream()` / `consumeAgentStream()` | Full control over the LLM loop — you write the generator |
|
|
23
|
-
| **Decorator** (`@theokit/agents`) | `@Agent` class → auto-generated route | `AgentStreamEvent` (TextDelta, ToolCall, Done...) | (same client hooks work) | Declarative — framework manages LLM loop via `@MainLoop` |
|
|
24
|
-
|
|
25
|
-
### Surface 1: Manual (defineAgentEndpoint)
|
|
18
|
+
Create an `agents/<name>.ts` file at the project root. It is automatically served at
|
|
19
|
+
`POST /api/agents/<name>` (dev + build) with no manual route wiring.
|
|
26
20
|
|
|
27
21
|
```typescript
|
|
28
|
-
//
|
|
29
|
-
import {
|
|
30
|
-
import
|
|
31
|
-
|
|
32
|
-
export
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const result = await callLLM(body.message)
|
|
37
|
-
yield { type: 'message', content: result }
|
|
38
|
-
},
|
|
22
|
+
// agents/chat.ts
|
|
23
|
+
import { defineAgent } from '@theokit/agents'
|
|
24
|
+
import { z } from 'zod'
|
|
25
|
+
|
|
26
|
+
export default defineAgent({
|
|
27
|
+
input: z.object({ message: z.string() }),
|
|
28
|
+
model: 'openai/gpt-4o-mini',
|
|
29
|
+
system: 'You are a helpful assistant.',
|
|
39
30
|
})
|
|
40
31
|
```
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
The endpoint streams the ai-sdk `UIMessageStream` that `useAgent` (client hook) consumes.
|
|
34
|
+
`@theokit/sdk` runs the agent; conversation turns auto-persist per session — the SDK owns storage.
|
|
35
|
+
|
|
36
|
+
**Provider resolution:** `OPENROUTER_API_KEY` (preferred — routes to many models) OR
|
|
37
|
+
`ANTHROPIC_API_KEY` / `OPENAI_API_KEY`. Set one in `.env`.
|
|
38
|
+
|
|
39
|
+
## Advanced Surface — @Agent Decorator (DI / class-based)
|
|
40
|
+
|
|
41
|
+
When you need dependency injection or class-based composition, use the `@Agent` class
|
|
42
|
+
decorator from `@theokit/agents`. The class name determines the route:
|
|
43
|
+
`AssistantAgent` → `POST /api/agents/assistant`.
|
|
43
44
|
|
|
44
45
|
```typescript
|
|
45
46
|
// server/agents/assistant.agent.ts
|
|
46
|
-
import { Agent, MainLoop
|
|
47
|
+
import { Agent, MainLoop } from '@theokit/agents'
|
|
47
48
|
|
|
48
49
|
@Agent({
|
|
49
50
|
model: 'openai/gpt-4o-mini',
|
|
@@ -57,9 +58,33 @@ export class AssistantAgent {
|
|
|
57
58
|
}
|
|
58
59
|
```
|
|
59
60
|
|
|
60
|
-
|
|
61
|
+
## Tools — defineAgentTool
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
Declare typed tools with `defineAgentTool` (from `theokit/server`) and pass them to
|
|
64
|
+
`defineAgent`'s `tools` array.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// agents/chat.ts
|
|
68
|
+
import { defineAgent } from '@theokit/agents'
|
|
69
|
+
import { defineAgentTool } from 'theokit/server'
|
|
70
|
+
import { z } from 'zod'
|
|
71
|
+
|
|
72
|
+
const currentTimeTool = defineAgentTool({
|
|
73
|
+
name: 'current_time',
|
|
74
|
+
description: 'Return the current ISO timestamp',
|
|
75
|
+
input: z.object({}),
|
|
76
|
+
execute: async () => ({ time: new Date().toISOString() }),
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
export default defineAgent({
|
|
80
|
+
input: z.object({ message: z.string() }),
|
|
81
|
+
model: 'openai/gpt-4o-mini',
|
|
82
|
+
system: 'You are a helpful assistant.',
|
|
83
|
+
tools: [currentTimeTool],
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### @Tool Decorator (advanced / class-based)
|
|
63
88
|
|
|
64
89
|
```typescript
|
|
65
90
|
import { Toolbox, Tool } from '@theokit/agents'
|
|
@@ -78,23 +103,26 @@ export class TaskTools {
|
|
|
78
103
|
}
|
|
79
104
|
```
|
|
80
105
|
|
|
81
|
-
## Client —
|
|
82
|
-
|
|
83
|
-
Works with BOTH surfaces. Transport: `fetch` POST + `ReadableStream` (SSE).
|
|
106
|
+
## Client — useAgent (React hook)
|
|
84
107
|
|
|
85
108
|
```typescript
|
|
86
|
-
import {
|
|
109
|
+
import { useAgent } from 'theokit/client'
|
|
87
110
|
|
|
88
111
|
function ChatUI() {
|
|
89
|
-
const {
|
|
112
|
+
const { messages, status, send, reset } = useAgent<{ message: string }>('/api/agents/chat')
|
|
90
113
|
|
|
91
114
|
return (
|
|
92
115
|
<div>
|
|
93
116
|
{status === 'streaming' && <p>Thinking...</p>}
|
|
94
|
-
{
|
|
95
|
-
<div key={
|
|
96
|
-
{
|
|
97
|
-
|
|
117
|
+
{messages.map(message => (
|
|
118
|
+
<div key={message.id}>
|
|
119
|
+
{message.parts.map((part, i) => (
|
|
120
|
+
part.type === 'text'
|
|
121
|
+
? <p key={i}>{part.text}</p>
|
|
122
|
+
: part.type === 'dynamic-tool'
|
|
123
|
+
? <p key={i}>Using tool: {part.toolName}</p>
|
|
124
|
+
: null
|
|
125
|
+
))}
|
|
98
126
|
</div>
|
|
99
127
|
))}
|
|
100
128
|
<button onClick={() => send({ message: 'Hello' })}>Send</button>
|
|
@@ -103,15 +131,23 @@ function ChatUI() {
|
|
|
103
131
|
}
|
|
104
132
|
```
|
|
105
133
|
|
|
106
|
-
|
|
134
|
+
`messages` is `UIMessage[]` (ai-sdk). Render `message.parts` — text parts
|
|
135
|
+
(`part.type === 'text'`, `part.text`) and tool parts (`part.type === 'dynamic-tool'`,
|
|
136
|
+
`part.toolName`, `part.state`, `part.output`). Do NOT switch on an `events`/`event.type`
|
|
137
|
+
pattern — the wire is `UIMessageStream`, not SSE events.
|
|
138
|
+
|
|
139
|
+
### Non-React: consumeUIMessageStream
|
|
107
140
|
|
|
108
141
|
```typescript
|
|
109
|
-
import {
|
|
142
|
+
import { consumeUIMessageStream } from 'theokit/client'
|
|
110
143
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
144
|
+
const response = await fetch('/api/agents/chat', {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
body: JSON.stringify({ message: 'Hello' }),
|
|
147
|
+
})
|
|
148
|
+
consumeUIMessageStream(response, (message) => {
|
|
149
|
+
console.log(message.parts)
|
|
150
|
+
})
|
|
115
151
|
```
|
|
116
152
|
|
|
117
153
|
## SDK Ecosystem — "you are here" map
|
|
@@ -134,14 +170,12 @@ Before writing custom tools, check if they already exist:
|
|
|
134
170
|
- `@UseGuards()` works on agents (shared with HTTP pipeline)
|
|
135
171
|
- `@UseInterceptors()` and `@UseFilters()` on agents are metadata-only (emit warnings)
|
|
136
172
|
- Agent runtime is `@theokit/sdk` — NEVER call LLM APIs directly via fetch
|
|
137
|
-
- Pick ONE surface per endpoint — don't mix defineAgentEndpoint with @Agent for the same route
|
|
138
173
|
- Check `@theokit/sdk-tools` BEFORE writing custom tools — it may already exist
|
|
139
174
|
|
|
140
175
|
## Anti-patterns
|
|
141
176
|
|
|
142
|
-
- NEVER call OpenAI/Anthropic/OpenRouter APIs directly — use
|
|
177
|
+
- NEVER call OpenAI/Anthropic/OpenRouter APIs directly — use `defineAgent` or `@Agent`
|
|
143
178
|
- NEVER reimplement tool calling loop — the SDK handles it
|
|
144
179
|
- NEVER reimplement file/search/shell tools — use `@theokit/sdk-tools` (readFile, writeFile, search, etc.)
|
|
145
|
-
- NEVER store conversations manually —
|
|
180
|
+
- NEVER store conversations manually — SDK persistence is automatic (the SDK owns storage)
|
|
146
181
|
- NEVER infer tool capability from method name — always provide explicit `name` + `description`
|
|
147
|
-
- NEVER mix both surfaces for the same endpoint — pick manual OR decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: theokit-frontend
|
|
3
|
-
description: TheoKit frontend — file-based routing, layouts, theoFetch typed client,
|
|
3
|
+
description: TheoKit frontend — file-based routing, layouts, theoFetch typed client, useAgent, React patterns
|
|
4
4
|
user-invocable: false
|
|
5
5
|
paths:
|
|
6
6
|
- "app/**"
|
|
@@ -50,23 +50,24 @@ const created = await client.tasks.POST({ body: { title: 'New' } })
|
|
|
50
50
|
|
|
51
51
|
## Agent Streaming
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
Two client APIs from `theokit/client`:
|
|
54
54
|
|
|
55
|
-
###
|
|
55
|
+
### useAgent (React hook — most common)
|
|
56
56
|
|
|
57
57
|
```typescript
|
|
58
|
-
import {
|
|
58
|
+
import { useAgent } from 'theokit/client'
|
|
59
59
|
|
|
60
60
|
function ChatUI() {
|
|
61
|
-
const {
|
|
61
|
+
const { messages, status, send, reset } = useAgent<{ message: string }>('/api/agents/chat')
|
|
62
62
|
|
|
63
63
|
return (
|
|
64
64
|
<div>
|
|
65
65
|
{status === 'streaming' && <p>Thinking...</p>}
|
|
66
|
-
{
|
|
67
|
-
<div key={
|
|
68
|
-
{
|
|
69
|
-
|
|
66
|
+
{messages.map(message => (
|
|
67
|
+
<div key={message.id}>
|
|
68
|
+
{message.parts.map((part, i) =>
|
|
69
|
+
part.type === 'text' ? <p key={i}>{part.text}</p> : null
|
|
70
|
+
)}
|
|
70
71
|
</div>
|
|
71
72
|
))}
|
|
72
73
|
<button onClick={() => send({ message: 'Hello' })}>Send</button>
|
|
@@ -75,26 +76,23 @@ function ChatUI() {
|
|
|
75
76
|
}
|
|
76
77
|
```
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
`messages` is `UIMessage[]` (ai-sdk). Render `message.parts`: text parts
|
|
80
|
+
(`part.type === 'text'`, `part.text`) and tool parts (`part.type === 'dynamic-tool'`,
|
|
81
|
+
`part.toolName`, `part.state`, `part.output`). Do NOT switch on an `events`/`event.type`
|
|
82
|
+
pattern — the wire is `UIMessageStream`, not SSE events.
|
|
79
83
|
|
|
80
|
-
|
|
81
|
-
import { consumeAgentStream } from 'theokit/client'
|
|
82
|
-
|
|
83
|
-
const stream = consumeAgentStream('/api/agents/assistant', {
|
|
84
|
-
body: { message: 'Hello' },
|
|
85
|
-
})
|
|
86
|
-
for await (const event of stream) {
|
|
87
|
-
console.log(event.type, event.content)
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### parseSSEChunk (low-level SSE parser)
|
|
84
|
+
### consumeUIMessageStream (non-React)
|
|
92
85
|
|
|
93
86
|
```typescript
|
|
94
|
-
import {
|
|
87
|
+
import { consumeUIMessageStream } from 'theokit/client'
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
|
|
89
|
+
const response = await fetch('/api/agents/chat', {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
body: JSON.stringify({ message: 'Hello' }),
|
|
92
|
+
})
|
|
93
|
+
consumeUIMessageStream(response, (message) => {
|
|
94
|
+
console.log(message.parts)
|
|
95
|
+
})
|
|
98
96
|
```
|
|
99
97
|
|
|
100
98
|
## Path Aliases
|
|
@@ -111,4 +109,4 @@ Configured in `tsconfig.json` — works in both server and app code.
|
|
|
111
109
|
- NEVER use raw `fetch('/api/...')` — use `theoFetch` for type safety
|
|
112
110
|
- NEVER create pages outside `app/` — they won't be discovered by the router
|
|
113
111
|
- NEVER import server code directly in `app/` — use theoFetch or server actions
|
|
114
|
-
- NEVER use `useEffect` + `fetch` for data loading — use theoFetch or
|
|
112
|
+
- NEVER use `useEffect` + `fetch` for data loading — use theoFetch or `useAgent`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: theokit-ui
|
|
3
|
-
description: "@theokit/ui component library —
|
|
3
|
+
description: "@theokit/ui AI-native component library — AI-agent surfaces (ChatThread, ChatMessage, ChatComposer, ToolCallCard, AgentStream), theming, providers; generic primitives (CodeBlock, Sidebar, Button) come from @usetheo/ui"
|
|
4
4
|
user-invocable: false
|
|
5
5
|
paths:
|
|
6
6
|
- "app/**"
|
|
@@ -12,13 +12,13 @@ paths:
|
|
|
12
12
|
- "**/*Theme*"
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
# @theokit/ui — AI
|
|
15
|
+
# @theokit/ui — AI-native Component Library (AI-agent surfaces)
|
|
16
16
|
|
|
17
|
-
`@theokit/ui` is an optional peer dependency. If installed, it provides ready-made components for chat
|
|
17
|
+
`@theokit/ui` is an optional peer dependency. If installed, it provides ready-made AI components for chat + coding-agent surfaces (chat thread, agent events, tool calls, diff viewer, build logs), plus theming. **Never build custom equivalents** of components it provides. Generic primitives (Button, Input, Card, CodeBlock, PageShell, Sidebar, Avatar, Alert, etc.) live in `@usetheo/ui`, which `@theokit/ui` depends on — import those from `@usetheo/ui`.
|
|
18
18
|
|
|
19
19
|
## Package Identity
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
`@theokit/ui` (AI-native, currently `1.0.0`) provides the AI-agent-surface components. Its generic foundation was split into `@usetheo/ui` in the 2026-07-03 AI-exclusive pivot; `@theokit/ui` depends on `@usetheo/ui`, so installing `@theokit/ui` pulls the foundation transitively.
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
# Install from npm (preferred)
|
|
@@ -54,19 +54,19 @@ export default function Layout({ children }) {
|
|
|
54
54
|
### Full Chat Page (typical assembly)
|
|
55
55
|
|
|
56
56
|
```typescript
|
|
57
|
+
// AI-agent-surface components live in @theokit/ui
|
|
57
58
|
import {
|
|
58
|
-
PageShell,
|
|
59
|
-
Sidebar,
|
|
60
|
-
SessionListItem,
|
|
61
59
|
ChatThread,
|
|
62
60
|
ChatMessage,
|
|
63
61
|
ChatMessageContent,
|
|
64
62
|
ChatComposer,
|
|
65
63
|
} from '@theokit/ui'
|
|
66
|
-
|
|
64
|
+
// Generic layout primitives moved to @usetheo/ui (2026-07-03 pivot)
|
|
65
|
+
import { PageShell, Sidebar, SessionListItem } from '@usetheo/ui'
|
|
66
|
+
import { useAgent } from 'theokit/client'
|
|
67
67
|
|
|
68
68
|
function ChatPage() {
|
|
69
|
-
const {
|
|
69
|
+
const { messages, status, send } = useAgent('/api/agents/assistant')
|
|
70
70
|
|
|
71
71
|
return (
|
|
72
72
|
<PageShell sidebar={
|
|
@@ -77,9 +77,13 @@ function ChatPage() {
|
|
|
77
77
|
</Sidebar>
|
|
78
78
|
}>
|
|
79
79
|
<ChatThread>
|
|
80
|
-
{messages.map(
|
|
81
|
-
<ChatMessage key={
|
|
82
|
-
|
|
80
|
+
{messages.map(message => (
|
|
81
|
+
<ChatMessage key={message.id} role={message.role}>
|
|
82
|
+
{message.parts.map((part, i) =>
|
|
83
|
+
part.type === 'text'
|
|
84
|
+
? <ChatMessageContent key={i} markdown={part.text} />
|
|
85
|
+
: null
|
|
86
|
+
)}
|
|
83
87
|
</ChatMessage>
|
|
84
88
|
))}
|
|
85
89
|
</ChatThread>
|
|
@@ -93,29 +97,29 @@ function ChatPage() {
|
|
|
93
97
|
}
|
|
94
98
|
```
|
|
95
99
|
|
|
96
|
-
###
|
|
100
|
+
### AI-agent-surface components (from `@theokit/ui`)
|
|
97
101
|
|
|
98
102
|
| Component | Purpose | Key Props |
|
|
99
103
|
|-----------|---------|-----------|
|
|
100
104
|
| `ChatThread` | Scrollable message container | `children` (ChatMessage elements) |
|
|
101
105
|
| `ChatMessage` | Single message bubble | `role: 'user' \| 'assistant'`, `children` |
|
|
102
106
|
| `ChatMessageContent` | Markdown + code rendering | `markdown: string` (handles streaming partial) |
|
|
103
|
-
| `CodeBlock` | Syntax-highlighted code | `code: string`, `language?: string` (uses shiki, lazy-loaded) |
|
|
104
107
|
| `ChatComposer` | Message input + submit | `onSubmit: (text) => void`, `disabled?: boolean` |
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `SessionListItem` | Session entry in sidebar | `title: string`, `onClick`, `active?: boolean` |
|
|
108
|
+
| `ToolCallCard` | Display agent tool invocations | — |
|
|
109
|
+
| `AgentStream` | Lower-level stream renderer | — |
|
|
108
110
|
|
|
109
|
-
###
|
|
111
|
+
### Generic primitives (from `@usetheo/ui`)
|
|
110
112
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
|
114
|
-
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
113
|
+
Moved out of `@theokit/ui` in the 2026-07-03 AI-exclusive pivot. Import these from `@usetheo/ui`.
|
|
114
|
+
|
|
115
|
+
| Component | Purpose | Key Props |
|
|
116
|
+
|-----------|---------|-----------|
|
|
117
|
+
| `CodeBlock` | Syntax-highlighted code | `code: string`, `language?: string` (uses shiki, lazy-loaded) |
|
|
118
|
+
| `PageShell` | App layout with sidebar slot | `sidebar?: ReactNode`, `children` |
|
|
119
|
+
| `Sidebar` | Collapsible side panel | `children` |
|
|
120
|
+
| `Button`, `Input`, `Textarea` | Form primitives (themed) | — |
|
|
121
|
+
| `Avatar` | User/agent avatar | — |
|
|
122
|
+
| `Alert` | Status messages | — |
|
|
119
123
|
|
|
120
124
|
## Peer Dependencies (install only what you use)
|
|
121
125
|
|
|
@@ -154,8 +158,8 @@ const myTheme = defineTheme({
|
|
|
154
158
|
|
|
155
159
|
- NEVER build a custom chat message component — use `ChatMessage` + `ChatMessageContent`
|
|
156
160
|
- NEVER build a custom markdown renderer — `ChatMessageContent` handles it (including streaming partial fences)
|
|
157
|
-
- NEVER build a custom code highlighter — `CodeBlock` uses shiki (lazy-loaded)
|
|
158
|
-
-
|
|
161
|
+
- NEVER build a custom code highlighter — `CodeBlock` (from `@usetheo/ui`) uses shiki (lazy-loaded)
|
|
162
|
+
- Import AI-agent-surface components (ChatThread, ChatMessage, ToolCallCard, etc.) from `@theokit/ui`; import generic primitives (Button, Input, CodeBlock, PageShell, Sidebar, Avatar, Alert) from `@usetheo/ui` — both are live packages since the 2026-07-03 pivot (`@theokit/ui` depends on `@usetheo/ui`)
|
|
159
163
|
- NEVER use `npm link` or `file:../theo-ui` to install — causes dual-React (use tarball or npm registry)
|
|
160
164
|
- NEVER install ALL peer deps — only install the peers for components you actually use
|
|
161
165
|
- NEVER use components without wrapping in `TheoUIProvider` + `ThemeProvider` first
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"theokit": "^0.
|
|
19
|
-
"@theokit/
|
|
20
|
-
"@theokit/
|
|
18
|
+
"theokit": "^0.15.1",
|
|
19
|
+
"@theokit/agents": "^0.30.1",
|
|
20
|
+
"@theokit/sdk": "^2.13.0",
|
|
21
|
+
"@theokit/ui": "^1.0.0",
|
|
22
|
+
"@usetheo/ui": "^0.14.0",
|
|
21
23
|
"lucide-react": "^0.469.0",
|
|
22
24
|
"react": "^19.0.0",
|
|
23
25
|
"react-dom": "^19.0.0",
|
|
@@ -37,6 +39,10 @@
|
|
|
37
39
|
"vitest": "^3.0.0"
|
|
38
40
|
},
|
|
39
41
|
"pnpm": {
|
|
40
|
-
"onlyBuiltDependencies": [
|
|
42
|
+
"onlyBuiltDependencies": [
|
|
43
|
+
"esbuild",
|
|
44
|
+
"better-sqlite3",
|
|
45
|
+
"workerd"
|
|
46
|
+
]
|
|
41
47
|
}
|
|
42
48
|
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
import {
|
|
3
|
-
defineAgentEndpoint,
|
|
4
|
-
defineAgentTool,
|
|
5
|
-
streamAgentRun,
|
|
6
|
-
createConversationHistory,
|
|
7
|
-
type AgentEvent,
|
|
8
|
-
} from 'theokit/server'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Chat agent endpoint — persistent conversation via createConversationHistory.
|
|
12
|
-
*
|
|
13
|
-
* Each browser tab gets a stable conversation id cookie on first visit;
|
|
14
|
-
* subsequent requests resume the same agent. Conversation turns auto-persist
|
|
15
|
-
* in `<cwd>/.theokit/agents/<conversationId>/messages.jsonl` (SDK owns
|
|
16
|
-
* storage). Tools: current_time example. Memory facts: opt-in via
|
|
17
|
-
* options.memory (off by default).
|
|
18
|
-
*
|
|
19
|
-
* Provider: OPENROUTER_API_KEY (preferred — gateway to many models) OR
|
|
20
|
-
* ANTHROPIC_API_KEY (direct Anthropic).
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const currentTime = defineAgentTool({
|
|
24
|
-
name: 'current_time',
|
|
25
|
-
description: 'Get the current ISO timestamp on the server.',
|
|
26
|
-
inputSchema: z.object({}),
|
|
27
|
-
handler: () => new Date().toISOString(),
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
export const POST = defineAgentEndpoint({
|
|
31
|
-
async *handler({ body, request, cookieHeaders, signal }): AsyncGenerator<AgentEvent> {
|
|
32
|
-
const safeBody =
|
|
33
|
-
body !== null && typeof body === 'object' && !Array.isArray(body)
|
|
34
|
-
? (body as { message?: string })
|
|
35
|
-
: {}
|
|
36
|
-
const { message = '' } = safeBody
|
|
37
|
-
// Provider resolution centralizada (Strategy pattern) — theokit/server resolve
|
|
38
|
-
// apiKey + baseUrl + provider automático via OPENROUTER_API_KEY / OPENAI_API_KEY /
|
|
39
|
-
// ANTHROPIC_API_KEY presente no env. Wire protocol: OpenAI Chat Completions
|
|
40
|
-
// (universal — todos os providers implementam essa API). Consumer NÃO tem
|
|
41
|
-
// conditionals sobre provider — é responsabilidade do framework.
|
|
42
|
-
// Wrap full agent lifecycle in try/catch — provider errors (invalid KEY,
|
|
43
|
-
// 401, rate-limit, model-not-found, 5xx) MUST surface as AgentEvent
|
|
44
|
-
// 'error' so the client renders an actionable message instead of a
|
|
45
|
-
// silent SSE closure. Dogfood chaos Phase 12 validates this contract.
|
|
46
|
-
try {
|
|
47
|
-
const { agent } = await createConversationHistory({
|
|
48
|
-
request,
|
|
49
|
-
response: { headers: cookieHeaders },
|
|
50
|
-
options: {
|
|
51
|
-
// Model id is prefixed with the provider namespace. When using
|
|
52
|
-
// OPENROUTER_API_KEY (default), prefixes route to the correct
|
|
53
|
-
// upstream — `openai/`, `anthropic/`, `google/`, `meta-llama/`,
|
|
54
|
-
// `mistralai/`, `groq/`, etc. See https://openrouter.ai/models.
|
|
55
|
-
// Without the prefix the SDK falls back to a stub response.
|
|
56
|
-
model: { id: 'openai/gpt-4o-mini' },
|
|
57
|
-
tools: [currentTime],
|
|
58
|
-
},
|
|
59
|
-
})
|
|
60
|
-
const run = await agent.send(message, { signal })
|
|
61
|
-
yield* streamAgentRun(run)
|
|
62
|
-
// Intentionally NO agent.dispose() — the agent stays registered so the
|
|
63
|
-
// next request from the same conversation resumes it (continuity).
|
|
64
|
-
} catch (err) {
|
|
65
|
-
const msg = err instanceof Error ? err.message : String(err)
|
|
66
|
-
yield { type: 'error', message: `Agent error: ${msg}` }
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
})
|