@wolfstar/create-http-framework 2.7.0 → 2.8.0-next-20261004142621
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -20
- package/dist/index.js +68 -11
- package/package.json +1 -1
- package/template/base/AGENTS.md.hbs +61 -0
- package/template/base/README.md.hbs +11 -1
- package/template/base/llms.txt.hbs +39 -0
package/README.md
CHANGED
|
@@ -48,26 +48,27 @@ The CLI will guide you through the following prompts:
|
|
|
48
48
|
|
|
49
49
|
## Options
|
|
50
50
|
|
|
51
|
-
| Flag | Alias | Description
|
|
52
|
-
| ------------------------------------ | ----- |
|
|
53
|
-
| `--overwrite` | | Overwrite the target directory if it already exists
|
|
54
|
-
| `--no-interactive` | | Skip all prompts and use defaults / flags
|
|
55
|
-
| `--interactive` | `-i` | Force interactive prompts even when an AI agent is detected
|
|
56
|
-
| `--package-manager <pm>` | | Choose npm, yarn, pnpm, or bun
|
|
57
|
-
| `--language <lang>` | | Choose TypeScript (`ts`) or JavaScript (`js`)
|
|
58
|
-
| `--build <tool>` | | Choose `tsc6`, `tsc7`, `tsdown`, `vite`, or `vite-nitro` for TypeScript
|
|
59
|
-
| `--lint <linter>` | | Choose `none`, `eslint`, or `oxlint`
|
|
60
|
-
| `--format <formatter>` | | Choose `none`, `prettier`, or `oxfmt`
|
|
61
|
-
| `--port <number>` | | Set the HTTP port (default: `3000`)
|
|
62
|
-
| `--i18n` / `--no-i18n` | | Enable or disable `@wolfstar/plugin-i18next` scaffolding
|
|
63
|
-
| `--subcommands` / `--no-subcommands` | | Enable or disable the example subcommand command
|
|
64
|
-
| `--testing` / `--no-testing` | | Enable or disable the Vitest + `@wolfstar/http-framework-test-utils` setup
|
|
65
|
-
| `--gateway` / `--no-gateway` | | Enable or disable `@wolfstar/plugin-gateway` scaffolding
|
|
66
|
-
| `--cache` / `--no-cache` | | Enable or disable `@wolfstar/plugin-cache` (turns `--gateway` on)
|
|
67
|
-
| `--redis` / `--no-redis` | | Cache in Redis instead of memory (turns `--cache` on)
|
|
68
|
-
| `--sharder` / `--no-sharder` | | Enable or disable `@wolfstar/plugin-sharder` (turns `--gateway` on)
|
|
69
|
-
| `--
|
|
70
|
-
| `--
|
|
51
|
+
| Flag | Alias | Description |
|
|
52
|
+
| ------------------------------------ | ----- | --------------------------------------------------------------------------- |
|
|
53
|
+
| `--overwrite` | | Overwrite the target directory if it already exists |
|
|
54
|
+
| `--no-interactive` | | Skip all prompts and use defaults / flags |
|
|
55
|
+
| `--interactive` | `-i` | Force interactive prompts even when an AI agent is detected |
|
|
56
|
+
| `--package-manager <pm>` | | Choose npm, yarn, pnpm, or bun |
|
|
57
|
+
| `--language <lang>` | | Choose TypeScript (`ts`) or JavaScript (`js`) |
|
|
58
|
+
| `--build <tool>` | | Choose `tsc6`, `tsc7`, `tsdown`, `vite`, or `vite-nitro` for TypeScript |
|
|
59
|
+
| `--lint <linter>` | | Choose `none`, `eslint`, or `oxlint` |
|
|
60
|
+
| `--format <formatter>` | | Choose `none`, `prettier`, or `oxfmt` |
|
|
61
|
+
| `--port <number>` | | Set the HTTP port (default: `3000`) |
|
|
62
|
+
| `--i18n` / `--no-i18n` | | Enable or disable `@wolfstar/plugin-i18next` scaffolding |
|
|
63
|
+
| `--subcommands` / `--no-subcommands` | | Enable or disable the example subcommand command |
|
|
64
|
+
| `--testing` / `--no-testing` | | Enable or disable the Vitest + `@wolfstar/http-framework-test-utils` setup |
|
|
65
|
+
| `--gateway` / `--no-gateway` | | Enable or disable `@wolfstar/plugin-gateway` scaffolding |
|
|
66
|
+
| `--cache` / `--no-cache` | | Enable or disable `@wolfstar/plugin-cache` (turns `--gateway` on) |
|
|
67
|
+
| `--redis` / `--no-redis` | | Cache in Redis instead of memory (turns `--cache` on) |
|
|
68
|
+
| `--sharder` / `--no-sharder` | | Enable or disable `@wolfstar/plugin-sharder` (turns `--gateway` on) |
|
|
69
|
+
| `--tunnel` / `--no-tunnel` | | Open a cloudflared quick tunnel in `stars dev` (`dev.tunnel` in the config) |
|
|
70
|
+
| `--install` / `--no-install` | | Enable or disable dependency installation |
|
|
71
|
+
| `--help` | `-h` | Print usage and exit |
|
|
71
72
|
|
|
72
73
|
## Non-interactive / AI agent mode
|
|
73
74
|
|
|
@@ -106,6 +107,9 @@ my-discord-bot/
|
|
|
106
107
|
├── vitest.setup.ts # Vitest setup file (--testing)
|
|
107
108
|
├── compose.yaml # Local Redis server (--redis)
|
|
108
109
|
├── README.md # Generated project README
|
|
110
|
+
├── AGENTS.md # The project's commands, layout and rules, for AI coding agents
|
|
111
|
+
├── llms.txt # The upstream documentation by topic, for AI coding agents
|
|
112
|
+
├── stars.config.ts # The `stars` CLI configuration (build, dev, tunnel)
|
|
109
113
|
├── .env # Environment variables (DISCORD_TOKEN, DISCORD_PUBLIC_KEY)
|
|
110
114
|
├── .gitignore
|
|
111
115
|
├── package.json
|
|
@@ -117,6 +121,9 @@ my-discord-bot/
|
|
|
117
121
|
- `src/commands/math.ts` is only generated when **Subcommands** is enabled.
|
|
118
122
|
- `src/lib/cache.ts` is only generated with **Cache**, `src/shard.ts` with **Sharder**, and `compose.yaml` with **Redis**.
|
|
119
123
|
- `tests/ping.test.ts`, `vitest.config.ts`, and `vitest.setup.ts` are only generated when **Testing** is enabled.
|
|
124
|
+
- `AGENTS.md` and `llms.txt` are always generated and only describe the features the project was created with. When one already exists and is not this generator's own unedited output (you wrote it, or edited it), a rerun leaves it as it is and says so.
|
|
125
|
+
- With a linter, the generated configuration (`.oxlintrc.json` or `eslint.config.mjs`) enables the rules of [`@wolfstar/eslint-plugin-http-framework`](../eslint-plugin-http-framework): decorator order, raw Discord fetches, dynamic translation keys and the other mistakes TypeScript cannot catch.
|
|
126
|
+
- `--tunnel` (or the **Dev tunnel** feature in the prompt) writes `dev: { tunnel: true }` to `stars.config`, so `stars dev` opens a cloudflared quick tunnel and Discord reaches the bot on your machine. Without it, press `t` in `stars dev` to open one on demand.
|
|
120
127
|
|
|
121
128
|
### Vite and Nitro
|
|
122
129
|
|
package/dist/index.js
CHANGED
|
@@ -145,6 +145,7 @@ async function fetchDependencyVersions(selections) {
|
|
|
145
145
|
names.add(selections.language === "ts" ? "typescript-eslint" : "@eslint/js");
|
|
146
146
|
}
|
|
147
147
|
if (selections.linter === "oxlint") names.add("oxlint");
|
|
148
|
+
if (selections.linter !== "none") names.add("@wolfstar/eslint-plugin-http-framework");
|
|
148
149
|
if (selections.formatter === "prettier") names.add("prettier");
|
|
149
150
|
if (selections.formatter === "oxfmt") names.add("oxfmt");
|
|
150
151
|
const list = [...names];
|
|
@@ -219,6 +220,17 @@ function runCommand(command, args, dir) {
|
|
|
219
220
|
//#endregion
|
|
220
221
|
//#region src/tools/projectFiles.ts
|
|
221
222
|
const caret = (version) => `^${version}`;
|
|
223
|
+
const FRAMEWORK_LINT_PLUGIN = "@wolfstar/eslint-plugin-http-framework";
|
|
224
|
+
/** The rules of {@link FRAMEWORK_LINT_PLUGIN}, listed for oxlint, whose JSON configuration cannot import them. */
|
|
225
|
+
const FRAMEWORK_LINT_RULES = [
|
|
226
|
+
"wolfstar/apply-options-decorator-order",
|
|
227
|
+
"wolfstar/require-subcommand-parent",
|
|
228
|
+
"wolfstar/no-raw-discord-fetch",
|
|
229
|
+
"wolfstar/no-dynamic-translation-key",
|
|
230
|
+
"wolfstar/prefer-apply-localized-builder",
|
|
231
|
+
"wolfstar/no-hoisted-plugin-register-import",
|
|
232
|
+
"wolfstar/no-deprecated-i18n-package"
|
|
233
|
+
];
|
|
222
234
|
function sortKeys(record) {
|
|
223
235
|
return Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
|
|
224
236
|
}
|
|
@@ -300,6 +312,7 @@ function buildDevDependencies(ctx) {
|
|
|
300
312
|
if (ctx.language === "ts") dev["typescript-eslint"] = caret(v["typescript-eslint"]);
|
|
301
313
|
else dev["@eslint/js"] = caret(v["@eslint/js"]);
|
|
302
314
|
} else if (ctx.linter === "oxlint") dev["oxlint"] = caret(v["oxlint"]);
|
|
315
|
+
if (ctx.linter !== "none") dev[FRAMEWORK_LINT_PLUGIN] = caret(v[FRAMEWORK_LINT_PLUGIN]);
|
|
303
316
|
if (ctx.formatter === "prettier") dev["prettier"] = caret(v["prettier"]);
|
|
304
317
|
else if (ctx.formatter === "oxfmt") dev["oxfmt"] = caret(v["oxfmt"]);
|
|
305
318
|
if (ctx.i18n) dev["@wolfstar/i18next-type-generator"] = caret(v["@wolfstar/i18next-type-generator"]);
|
|
@@ -389,12 +402,16 @@ function writeTsconfig(targetDir, ctx) {
|
|
|
389
402
|
*/
|
|
390
403
|
function writeStarsConfig(targetDir, ctx) {
|
|
391
404
|
const isJs = ctx.language === "js";
|
|
392
|
-
|
|
393
|
-
if (!isJs && isViteBuild(ctx.buildTool))
|
|
405
|
+
const parts = !isJs && (ctx.buildTool === "tsc6" || ctx.buildTool === "tsc7") ? ["build: { tool: 'tsc' }", "env: false"] : isJs ? ["env: false"] : [];
|
|
406
|
+
if (!isJs && isViteBuild(ctx.buildTool)) {
|
|
407
|
+
const nitro = isNitroBuild(ctx.buildTool) ? ", enableNitro: true, nitro: { preset: 'node-server' }" : "";
|
|
408
|
+
parts.push("build: { tool: 'vite' }", `experimental: { enableVite: true${nitro} }`);
|
|
409
|
+
}
|
|
410
|
+
if (ctx.tunnel) parts.push("dev: { tunnel: true }");
|
|
394
411
|
const content = [
|
|
395
412
|
"import { defineConfig } from '@wolfstar/http-framework/config';",
|
|
396
413
|
"",
|
|
397
|
-
`export default defineConfig(${
|
|
414
|
+
`export default defineConfig(${parts.length > 0 ? `{ ${parts.join(", ")} }` : "{}"});`,
|
|
398
415
|
""
|
|
399
416
|
].join("\n");
|
|
400
417
|
writeFile(join(targetDir, isJs ? "stars.config.js" : "stars.config.ts"), content);
|
|
@@ -403,27 +420,33 @@ function writeLinterConfig(targetDir, ctx) {
|
|
|
403
420
|
if (ctx.linter === "oxlint") writeFile(join(targetDir, ".oxlintrc.json"), json({
|
|
404
421
|
$schema: "./node_modules/oxlint/configuration_schema.json",
|
|
405
422
|
...ctx.language === "ts" ? { plugins: ["typescript"] } : {},
|
|
423
|
+
jsPlugins: [FRAMEWORK_LINT_PLUGIN],
|
|
406
424
|
categories: {
|
|
407
425
|
correctness: "error",
|
|
408
426
|
suspicious: "warn"
|
|
409
427
|
},
|
|
428
|
+
rules: Object.fromEntries(FRAMEWORK_LINT_RULES.map((rule) => [rule, "error"])),
|
|
410
429
|
ignorePatterns: ["dist/**", "node_modules/**"]
|
|
411
430
|
}));
|
|
412
431
|
else if (ctx.linter === "eslint") {
|
|
413
432
|
const content = ctx.language === "ts" ? [
|
|
433
|
+
`import wolfstar, { recommendedRules } from '${FRAMEWORK_LINT_PLUGIN}';`,
|
|
414
434
|
"import tseslint from 'typescript-eslint';",
|
|
415
435
|
"",
|
|
416
436
|
"export default tseslint.config(",
|
|
417
437
|
" { ignores: ['dist/**'] },",
|
|
418
|
-
" ...tseslint.configs.recommended",
|
|
438
|
+
" ...tseslint.configs.recommended,",
|
|
439
|
+
" { plugins: { wolfstar }, rules: recommendedRules }",
|
|
419
440
|
");",
|
|
420
441
|
""
|
|
421
442
|
].join("\n") : [
|
|
422
443
|
"import js from '@eslint/js';",
|
|
444
|
+
`import wolfstar, { recommendedRules } from '${FRAMEWORK_LINT_PLUGIN}';`,
|
|
423
445
|
"",
|
|
424
446
|
"export default [",
|
|
425
447
|
" { ignores: ['dist/**'] },",
|
|
426
|
-
" js.configs.recommended",
|
|
448
|
+
" js.configs.recommended,",
|
|
449
|
+
" { plugins: { wolfstar }, rules: recommendedRules }",
|
|
427
450
|
"];",
|
|
428
451
|
""
|
|
429
452
|
].join("\n");
|
|
@@ -497,6 +520,12 @@ function removeI18nDeclaration(outputDir) {
|
|
|
497
520
|
if (existsSync(typesDir) && readdirSync(typesDir).length === 0) rmSync(typesDir, { recursive: true });
|
|
498
521
|
}
|
|
499
522
|
/**
|
|
523
|
+
* The files that describe a project to coding agents. A project often has its own already, and unlike the sources
|
|
524
|
+
* they are prose a rerun cannot merge: an existing one is only replaced when this generator wrote it and nobody
|
|
525
|
+
* edited it since.
|
|
526
|
+
*/
|
|
527
|
+
const AGENT_DOCS = /* @__PURE__ */ new Set(["AGENTS.md", "llms.txt"]);
|
|
528
|
+
/**
|
|
500
529
|
* What the Handlebars sources see: the persisted {@link TemplateContext} plus values derived from it. Deriving at render
|
|
501
530
|
* time keeps the manifest small and lets manifests written before these fields existed still render.
|
|
502
531
|
*/
|
|
@@ -516,8 +545,10 @@ function toRenderContext(context) {
|
|
|
516
545
|
});
|
|
517
546
|
return {
|
|
518
547
|
...context,
|
|
548
|
+
typescript,
|
|
519
549
|
vite: typescript && isViteBuild(context.buildTool),
|
|
520
550
|
nitro: typescript && isNitroBuild(context.buildTool),
|
|
551
|
+
autoImports: typescript && context.buildTool === "tsdown",
|
|
521
552
|
registersEnv: Boolean(context.autoEnv) && typescript && (context.buildTool === "tsdown" || context.buildTool === "vite"),
|
|
522
553
|
commands
|
|
523
554
|
};
|
|
@@ -694,7 +725,7 @@ function removeStaleGeneratedFiles(outputDir, context) {
|
|
|
694
725
|
* - Non-`.hbs` files (e.g. static locale JSON under a feature's `src/locales/**`) are copied verbatim —
|
|
695
726
|
* no extension stripped, no Handlebars compilation.
|
|
696
727
|
*/
|
|
697
|
-
function processDir(root, outputDir, context) {
|
|
728
|
+
function processDir(root, outputDir, context, keep) {
|
|
698
729
|
for (const absoluteSource of walkDir(root)) {
|
|
699
730
|
const outputRelative = toOutputRelative(root, absoluteSource, context.language);
|
|
700
731
|
if (outputRelative === null) continue;
|
|
@@ -702,6 +733,7 @@ function processDir(root, outputDir, context) {
|
|
|
702
733
|
const isHandlebars = absoluteSource.endsWith(".hbs");
|
|
703
734
|
const outputPath = join(outputDir, outputRelative);
|
|
704
735
|
const content = isHandlebars ? Handlebars.compile(rawContent)(toRenderContext(context)) : rawContent;
|
|
736
|
+
if (keep?.(outputRelative, content)) continue;
|
|
705
737
|
writeFile(outputPath, content);
|
|
706
738
|
}
|
|
707
739
|
}
|
|
@@ -710,13 +742,25 @@ function processDir(root, outputDir, context) {
|
|
|
710
742
|
* {@link resolveFeatureDirs} on top, in order — feature files overwrite base files at the same
|
|
711
743
|
* output-relative path (e.g. `features/i18n/src/main.ts.hbs` overwrites `base/src/main.ts.hbs`).
|
|
712
744
|
*
|
|
745
|
+
* @param onKept Called with each of `AGENTS.md`/`llms.txt` that was left as it is, because it exists and is not this
|
|
746
|
+
* generator's own unedited output.
|
|
713
747
|
* @returns Output-relative paths that looked stale (belong to a disabled feature or the other
|
|
714
748
|
* language) but were left in place because they'd been hand-edited since the last run.
|
|
715
749
|
*/
|
|
716
|
-
async function processTemplate(outputDir, context) {
|
|
750
|
+
async function processTemplate(outputDir, context, onKept) {
|
|
751
|
+
const manifestContext = readManifest(outputDir);
|
|
717
752
|
const preserved = removeStaleGeneratedFiles(outputDir, context);
|
|
718
753
|
if (!context.i18n) removeI18nDeclaration(outputDir);
|
|
719
|
-
processDir(baseDir, outputDir, context)
|
|
754
|
+
processDir(baseDir, outputDir, context, (outputRelative, content) => {
|
|
755
|
+
if (!AGENT_DOCS.has(outputRelative)) return false;
|
|
756
|
+
const target = join(outputDir, outputRelative);
|
|
757
|
+
if (!existsSync(target)) return false;
|
|
758
|
+
const actual = readFileSync(target, "utf-8");
|
|
759
|
+
const source = join(baseDir, `${outputRelative}.hbs`);
|
|
760
|
+
const pristine = actual === content || manifestContext !== void 0 && renderSource(source, manifestContext) === actual;
|
|
761
|
+
if (!pristine) onKept?.(outputRelative);
|
|
762
|
+
return !pristine;
|
|
763
|
+
});
|
|
720
764
|
for (const feature of resolveFeatureDirs(context)) processDir(join(featuresDir, feature), outputDir, context);
|
|
721
765
|
writeManifest(outputDir, context);
|
|
722
766
|
return preserved;
|
|
@@ -752,6 +796,7 @@ Options:
|
|
|
752
796
|
--cache / --no-cache Toggle @wolfstar/plugin-cache, the gateway entity cache (default: off; enables --gateway)
|
|
753
797
|
--redis / --no-redis Store the cache in Redis instead of memory (default: off; enables --cache)
|
|
754
798
|
--sharder / --no-sharder Toggle @wolfstar/plugin-sharder, gateway shards across cluster workers (default: off; enables --gateway)
|
|
799
|
+
--tunnel / --no-tunnel Open a cloudflared quick tunnel in \`stars dev\`, so Discord reaches the bot (default: off)
|
|
755
800
|
--install / --no-install Toggle dependency installation (default: on)
|
|
756
801
|
--ignore Write into an existing, non-empty directory without clearing it
|
|
757
802
|
--help, -h Print this message and exit
|
|
@@ -810,6 +855,7 @@ async function main() {
|
|
|
810
855
|
const cliCache = argv["cache"];
|
|
811
856
|
const cliRedis = argv["redis"];
|
|
812
857
|
const cliSharder = argv["sharder"];
|
|
858
|
+
const cliTunnel = argv["tunnel"];
|
|
813
859
|
const cliInstall = argv["install"];
|
|
814
860
|
const { isAgent, agent } = await determineAgent();
|
|
815
861
|
const agentMode = isAgent && flagInteractive !== true;
|
|
@@ -1037,6 +1083,7 @@ async function main() {
|
|
|
1037
1083
|
let wantsCache;
|
|
1038
1084
|
let wantsRedis;
|
|
1039
1085
|
let wantsSharder;
|
|
1086
|
+
let wantsTunnel;
|
|
1040
1087
|
const nitro = language === "ts" && isNitroBuild(buildTool);
|
|
1041
1088
|
if (nonInteractive) {
|
|
1042
1089
|
wantsI18n = cliI18n ?? false;
|
|
@@ -1047,6 +1094,7 @@ async function main() {
|
|
|
1047
1094
|
wantsCache = cliCache ?? false;
|
|
1048
1095
|
wantsRedis = cliRedis ?? false;
|
|
1049
1096
|
wantsSharder = cliSharder ?? false;
|
|
1097
|
+
wantsTunnel = cliTunnel ?? false;
|
|
1050
1098
|
} else {
|
|
1051
1099
|
const featuresResult = await multiselect({
|
|
1052
1100
|
message: "Which optional features would you like to add?",
|
|
@@ -1078,9 +1126,13 @@ async function main() {
|
|
|
1078
1126
|
...nitro ? [] : [{
|
|
1079
1127
|
value: "sharder",
|
|
1080
1128
|
label: "Gateway sharding across cluster workers (@wolfstar/plugin-sharder)"
|
|
1081
|
-
}]
|
|
1129
|
+
}],
|
|
1130
|
+
{
|
|
1131
|
+
value: "tunnel",
|
|
1132
|
+
label: "Dev tunnel (a cloudflared quick tunnel, so Discord reaches the bot while developing)"
|
|
1133
|
+
}
|
|
1082
1134
|
],
|
|
1083
|
-
initialValues: [],
|
|
1135
|
+
initialValues: cliTunnel ? ["tunnel"] : [],
|
|
1084
1136
|
required: false
|
|
1085
1137
|
});
|
|
1086
1138
|
if (isCancel(featuresResult)) {
|
|
@@ -1095,6 +1147,7 @@ async function main() {
|
|
|
1095
1147
|
wantsGateway = features.has("gateway");
|
|
1096
1148
|
wantsCache = features.has("cache");
|
|
1097
1149
|
wantsSharder = features.has("sharder");
|
|
1150
|
+
wantsTunnel = features.has("tunnel");
|
|
1098
1151
|
wantsRedis = cliRedis ?? false;
|
|
1099
1152
|
if (wantsCache && cliRedis === void 0) {
|
|
1100
1153
|
const redisResult = await confirm({
|
|
@@ -1155,6 +1208,7 @@ async function main() {
|
|
|
1155
1208
|
});
|
|
1156
1209
|
s.stop("Versions fetched.");
|
|
1157
1210
|
s.start("Generating project files...");
|
|
1211
|
+
const keptDocs = [];
|
|
1158
1212
|
const preservedFiles = await processTemplate(targetDir, {
|
|
1159
1213
|
name: projectName,
|
|
1160
1214
|
port,
|
|
@@ -1168,8 +1222,9 @@ async function main() {
|
|
|
1168
1222
|
redis: wantsRedis,
|
|
1169
1223
|
sharder: wantsSharder,
|
|
1170
1224
|
buildTool,
|
|
1225
|
+
tunnel: wantsTunnel,
|
|
1171
1226
|
autoEnv: true
|
|
1172
|
-
});
|
|
1227
|
+
}, (path) => keptDocs.push(path));
|
|
1173
1228
|
writeProjectFiles(targetDir, {
|
|
1174
1229
|
name: projectName,
|
|
1175
1230
|
port,
|
|
@@ -1181,6 +1236,7 @@ async function main() {
|
|
|
1181
1236
|
cache: wantsCache,
|
|
1182
1237
|
redis: wantsRedis,
|
|
1183
1238
|
sharder: wantsSharder,
|
|
1239
|
+
tunnel: wantsTunnel,
|
|
1184
1240
|
packageManager,
|
|
1185
1241
|
language,
|
|
1186
1242
|
buildTool,
|
|
@@ -1189,6 +1245,7 @@ async function main() {
|
|
|
1189
1245
|
versions
|
|
1190
1246
|
});
|
|
1191
1247
|
s.stop("Project files generated.");
|
|
1248
|
+
for (const file of keptDocs) log.warn(`Kept your existing "${file}" as it is — delete it and run again to get the generated one.`);
|
|
1192
1249
|
for (const file of preservedFiles) log.warn(`Kept hand-edited "${file}" even though its feature is now disabled — remove it manually if it's no longer needed.`);
|
|
1193
1250
|
if (wantsI18n) try {
|
|
1194
1251
|
s.start("Generating i18n types...");
|
package/package.json
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI coding agents working in `{{name}}`, a Discord bot built with
|
|
4
|
+
[`@wolfstar/http-framework`](https://www.npmjs.com/package/@wolfstar/http-framework). It receives interactions over
|
|
5
|
+
HTTP{{#if gateway}} and gateway events over a WebSocket (`@wolfstar/plugin-gateway`){{/if}}.
|
|
6
|
+
|
|
7
|
+
## Commands
|
|
8
|
+
|
|
9
|
+
- `npm run dev` — build, run and restart the bot on changes (`stars dev`). Add `-- --no-tui` for plain line output,
|
|
10
|
+
which is what a non-interactive shell gets anyway.
|
|
11
|
+
{{#if typescript}}
|
|
12
|
+
- `npm run build` — build once (`stars build`).
|
|
13
|
+
{{/if}}
|
|
14
|
+
- `npm start` — run the built bot.
|
|
15
|
+
- `npx stars doctor` — check the runtime, the credentials and the generated files. Run it first when something is off.
|
|
16
|
+
- `npx stars info --json` — the resolved configuration.
|
|
17
|
+
- `npx stars commands diff` — what a deploy would change on Discord. `npx stars commands deploy --yes` deploys.
|
|
18
|
+
{{#if i18n}}
|
|
19
|
+
- `npm run generate:i18n` — regenerate the i18next types after editing `src/locales/`.
|
|
20
|
+
{{/if}}
|
|
21
|
+
{{#if testing}}
|
|
22
|
+
- `npm test` — run the tests (vitest).
|
|
23
|
+
{{/if}}
|
|
24
|
+
|
|
25
|
+
## Layout
|
|
26
|
+
|
|
27
|
+
- `src/main.*` — the entry: creates the client, loads the pieces, starts listening.
|
|
28
|
+
- `src/commands/` — one file per application command. A command registers its own builder
|
|
29
|
+
(`@RegisterCommand(...)` or `registerApplicationCommands`) and answers in `chatInputRun`.
|
|
30
|
+
- `src/interaction-handlers/` — buttons, select menus and modals, matched by custom id.
|
|
31
|
+
- `src/listeners/` — client event listeners.
|
|
32
|
+
- `src/lib/` — shared code{{#if autoImports}}, auto-imported by `stars` (see `.stars/imports.d.ts`){{else}}; import what you use, nothing is auto-imported{{/if}}.
|
|
33
|
+
{{#if i18n}}
|
|
34
|
+
- `src/locales/<locale>/` — translations. Keys are typed: never build a key from a variable.
|
|
35
|
+
{{/if}}
|
|
36
|
+
- `stars.config.*` — the only build and dev configuration. There is no separate bundler config.
|
|
37
|
+
- `.stars/` — generated by `stars prepare`. Never edit it, and do not commit it.
|
|
38
|
+
|
|
39
|
+
{{#if vite}}
|
|
40
|
+
The project is bundled into one file, so there is no `commands` directory at runtime: import every new piece in
|
|
41
|
+
`src/main.*` and load it with `container.stores.loadPiece`.
|
|
42
|
+
|
|
43
|
+
{{/if}}
|
|
44
|
+
## Rules
|
|
45
|
+
|
|
46
|
+
- Secrets live in `.env` (`DISCORD_TOKEN`, `DISCORD_PUBLIC_KEY`, `DISCORD_CLIENT_ID`). Never print or commit them.
|
|
47
|
+
- Call Discord through `container.rest`, not `fetch`: it handles rate limits and authentication.
|
|
48
|
+
- An interaction must be answered within 3 seconds: `reply()`, or `defer()` first and `edit()` later. `edit()` before
|
|
49
|
+
either one fails.
|
|
50
|
+
{{#if tunnel}}
|
|
51
|
+
- `stars dev` opens a public cloudflared tunnel to the bot (`dev.tunnel` in `stars.config`). Its URL changes on every
|
|
52
|
+
run; it is the application's interactions endpoint only while the session lasts.
|
|
53
|
+
{{/if}}
|
|
54
|
+
- Changing a command's builder (name, description, options) changes what Discord has to be told. `stars dev` asks
|
|
55
|
+
before it redeploys; outside it, use `stars commands deploy`.
|
|
56
|
+
- Do not start a long-running `npm run dev` to check a change: build{{#if testing}} and run the tests{{/if}} instead.
|
|
57
|
+
|
|
58
|
+
## Reference
|
|
59
|
+
|
|
60
|
+
- Framework and CLI documentation: <https://github.com/wolfstar-project/stars-components>
|
|
61
|
+
- `llms.txt` in this directory lists the documentation by topic.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A Discord bot built with [`@wolfstar/http-framework`](https://www.npmjs.com/package/@wolfstar/http-framework), an HTTP-interactions framework for Discord bots.
|
|
4
4
|
|
|
5
|
+
`AGENTS.md` and `llms.txt` describe the project to AI coding agents: its commands, its layout and the rules that are easy to get wrong.
|
|
6
|
+
|
|
5
7
|
## Setup
|
|
6
8
|
|
|
7
9
|
1. Install dependencies (skip this if the generator already installed them for you).
|
|
@@ -40,14 +42,22 @@ The project is bundled with [Vite](https://vite.dev) into a single file, so the
|
|
|
40
42
|
|
|
41
43
|
The `dev` and `build` scripts are powered by the [`stars`](https://www.npmjs.com/package/@wolfstar/cli) CLI and read the `stars.config` file at the project root.
|
|
42
44
|
|
|
43
|
-
- `npm run dev` — build, run and restart the bot on changes, with an interactive terminal
|
|
45
|
+
- `npm run dev` — build, run and restart the bot on changes, with an interactive terminal dashboard: status, log channels and levels you can filter, and a prompt before changed commands are redeployed (`npm run dev -- --no-tui` for plain logs).
|
|
44
46
|
- `npm run build` — {{#if nitro}}build the deployable `.output/` directory{{else}}compile the project{{/if}} (TypeScript projects only).
|
|
45
47
|
- `npm start` — run the bot.
|
|
46
48
|
- `npx stars info` — print the resolved configuration.
|
|
49
|
+
- `npx stars doctor` — check the runtime, the credentials, the interactions endpoint and the generated files.
|
|
50
|
+
- `npx stars commands diff` / `npx stars commands deploy` — compare the commands the bot defines with the ones Discord has, and deploy them.
|
|
47
51
|
{{#if testing}}
|
|
48
52
|
- `npm test` — run the test suite with vitest.
|
|
49
53
|
{{/if}}
|
|
50
54
|
|
|
55
|
+
{{#if tunnel}}
|
|
56
|
+
## Tunnel
|
|
57
|
+
|
|
58
|
+
`stars dev` opens a [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) quick tunnel (`dev.tunnel` in `stars.config`), so Discord can reach the bot on your machine. Its URL changes on every run: paste it as the **Interactions Endpoint URL** of the application, or set `dev: { tunnel: { updateEndpoint: true } }` to let `stars dev` do it. Press `t` in the dev UI to close or reopen it.
|
|
59
|
+
|
|
60
|
+
{{/if}}
|
|
51
61
|
## Commands
|
|
52
62
|
|
|
53
63
|
- `/ping` — replies with `Pong!`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
> A Discord bot built with @wolfstar/http-framework: HTTP interactions{{#if gateway}} and gateway events{{/if}}, built and run with the `stars` CLI.
|
|
4
|
+
|
|
5
|
+
Start with `AGENTS.md` for this project's commands, layout and rules. The links below are the upstream documentation.
|
|
6
|
+
|
|
7
|
+
## Framework
|
|
8
|
+
|
|
9
|
+
- [@wolfstar/http-framework](https://github.com/wolfstar-project/stars-components/tree/main/packages/http-framework#readme): the client, commands, interaction handlers, listeners, plugins
|
|
10
|
+
- [@wolfstar/decorators](https://github.com/wolfstar-project/stars-components/tree/main/packages/decorators#readme): `ApplyOptions`, permission and context preconditions
|
|
11
|
+
- [@wolfstar/env-utilities](https://github.com/wolfstar-project/stars-components/tree/main/packages/env-utilities#readme): typed environment variables
|
|
12
|
+
|
|
13
|
+
## CLI
|
|
14
|
+
|
|
15
|
+
- [@wolfstar/cli](https://github.com/wolfstar-project/stars-components/tree/main/packages/cli#readme): `stars dev`, `build`, `prepare`, `info`, `doctor`, `commands`, `codegen`, `completions`, and every `stars.config` option
|
|
16
|
+
{{#if i18n}}
|
|
17
|
+
|
|
18
|
+
## Internationalisation
|
|
19
|
+
|
|
20
|
+
- [@wolfstar/plugin-i18next](https://www.npmjs.com/package/@wolfstar/plugin-i18next): the i18next plugin
|
|
21
|
+
- [@wolfstar/i18next-type-generator](https://github.com/wolfstar-project/stars-components/tree/main/packages/i18next-type-generator#readme): typed translation keys
|
|
22
|
+
{{/if}}
|
|
23
|
+
{{#if gateway}}
|
|
24
|
+
|
|
25
|
+
## Gateway
|
|
26
|
+
|
|
27
|
+
- [@wolfstar/plugin-gateway](https://www.npmjs.com/package/@wolfstar/plugin-gateway): gateway events next to HTTP interactions
|
|
28
|
+
{{#if cache}}
|
|
29
|
+
- [@wolfstar/plugin-cache](https://www.npmjs.com/package/@wolfstar/plugin-cache): the gateway entity cache
|
|
30
|
+
{{/if}}
|
|
31
|
+
{{#if sharder}}
|
|
32
|
+
- [@wolfstar/plugin-sharder](https://www.npmjs.com/package/@wolfstar/plugin-sharder): shards across cluster workers
|
|
33
|
+
{{/if}}
|
|
34
|
+
{{/if}}
|
|
35
|
+
|
|
36
|
+
## Discord
|
|
37
|
+
|
|
38
|
+
- [Receiving and responding to interactions](https://discord.com/developers/docs/interactions/receiving-and-responding)
|
|
39
|
+
- [Application commands](https://discord.com/developers/docs/interactions/application-commands)
|