{"_id":"rosepack","_rev":"56-8664f50be3b6b8648b936e4c32173b28","name":"rosepack","dist-tags":{"latest":"0.5.1"},"versions":{"0.0.0":{"name":"rosepack","version":"0.0.0","_id":"rosepack@0.0.0","maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"dist":{"shasum":"498515f73a3d1c204b50a0038d320887a0adfa9f","tarball":"https://registry.npmjs.org/rosepack/-/rosepack-0.0.0.tgz","fileCount":3,"integrity":"sha512-1AbEuHLHU+dM342xhuH3HL4d4hqgysZTwo5wmMVcXGA19iSMvKk6kwet7Nl+shq81TCyPEZoTFwhC2Mu8lT17A==","signatures":[{"sig":"MEUCIGwAJpjC22qDwNImkn9msJ9eHPVSjlsvSPCe47g3qvZtAiEA7z+oUCxpEf4aqzFH0LY8X15Q3Tqln8EWT1fpvAD8jPI=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":867},"type":"module","module":"index.ts","shasum":"498515f73a3d1c204b50a0038d320887a0adfa9f","_npmUser":{"name":"taskylizard","email":"taskylizard@fmhy.net"},"_integrity":"sha512-1AbEuHLHU+dM342xhuH3HL4d4hqgysZTwo5wmMVcXGA19iSMvKk6kwet7Nl+shq81TCyPEZoTFwhC2Mu8lT17A==","_npmVersion":"10.8.3","directories":{},"_nodeVersion":"22.6.0","_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest"},"peerDependencies":{"typescript":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/rosepack_0.0.0_1730978564498_0.8808770060661619","host":"s3://npm-registry-packages"}},"0.1.0":{"name":"rosepack","version":"0.1.0","author":"taskylizard","license":"MIT","_id":"rosepack@0.1.0","maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"homepage":"https://github.com/taskylizard/rosepack#readme","bugs":{"url":"https://github.com/taskylizard/rosepack/issues"},"bin":{"rosepack":"./dist/cli.mjs"},"dist":{"shasum":"cd938ecea773d4d1f0fd29f608444e90d6ed6d2f","tarball":"https://registry.npmjs.org/rosepack/-/rosepack-0.1.0.tgz","fileCount":13,"integrity":"sha512-4cy2S93zIKvSlqPnUn9/P76gZStORrN1c2mA0JqE6BeI2VPOj9nVnFmG+31RzA5IN9/F+hDA7yu9Kd64kVxBjg==","signatures":[{"sig":"MEUCIQDliheQmc/qJyYebz2lqwc4tLssJFE70dAbNbb/N+i0jQIgUIMOiC2dIdcD3ohGMA1v/E33SZ7liDzI24mMv0Uk1nk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/rosepack@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":286126},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.mjs","./cli":"./dist/cli.mjs","./http":"./dist/http.mjs","./vite":"./dist/vite.mjs","./package.json":"./package.json"},"scripts":{"dev":"vp pack --watch","release":"vp exec node scripts/release.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a8444bee-1feb-48bd-b7ef-20e45c2a3ec1"}},"devEngines":{"packageManager":{"name":"pnpm","onFail":"download","version":"11.13.1"}},"repository":{"url":"git+https://github.com/taskylizard/rosepack.git","type":"git"},"description":"A strongly typed Oceanic interaction and command framework.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"obug":"2.1.4","gunshi":"0.37.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"npm:@voidzero-dev/vite-plus-core@0.2.4","vite-plus":"0.2.4","oceanic.js":"1.14.0","typescript":"7.0.2","@types/node":"26.1.1","@changesets/cli":"2.31.1","@typescript/native-preview":"7.0.0-dev.20260707.2"},"peerDependencies":{"vite":"8.1.3","oceanic.js":"1.14.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rosepack_0.1.0_1784915352526_0.21558617497232002","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"rosepack","version":"0.2.0","author":"taskylizard","license":"MIT","_id":"rosepack@0.2.0","maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"homepage":"https://github.com/taskylizard/rosepack#readme","bugs":{"url":"https://github.com/taskylizard/rosepack/issues"},"bin":{"rosepack":"./dist/cli.mjs"},"dist":{"shasum":"c23bcbccfc34ea4f7ed5ec15b53ea82961926f94","tarball":"https://registry.npmjs.org/rosepack/-/rosepack-0.2.0.tgz","fileCount":13,"integrity":"sha512-Iqpc51iZ2gbZgTt3MqPCBPpfmgx88c6brvnFbOysviiTyDvZGxRl9r1FixH9HIOddfI+cmrpkOWkedpvFJM9eg==","signatures":[{"sig":"MEQCIEGdm0/Y4vsWmqC+sTzTUtjdqupGEFRThvrrxtMjAz4jAiBxGdDfURxD+mWEZ1OKkv6EwdAikGrdggfx/YV9ST8PEQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/rosepack@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":308816},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.mjs","./cli":"./dist/cli.mjs","./http":"./dist/http.mjs","./vite":"./dist/vite.mjs","./package.json":"./package.json"},"scripts":{"dev":"vp pack --watch","release":"vp exec node scripts/release.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a8444bee-1feb-48bd-b7ef-20e45c2a3ec1"}},"devEngines":{"packageManager":{"name":"pnpm","onFail":"download","version":"11.13.1"}},"repository":{"url":"git+https://github.com/taskylizard/rosepack.git","type":"git"},"description":"A strongly typed Oceanic interaction and command framework.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"obug":"2.1.4","gunshi":"0.37.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"npm:@voidzero-dev/vite-plus-core@0.2.4","vite-plus":"0.2.4","oceanic.js":"1.14.0","typescript":"7.0.2","@types/node":"26.1.1","@changesets/cli":"2.31.1","@typescript/native-preview":"7.0.0-dev.20260707.2"},"peerDependencies":{"vite":"8.1.3","oceanic.js":"1.14.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rosepack_0.2.0_1784997769845_0.1544166799582012","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"rosepack","version":"0.3.0","author":"taskylizard","license":"MIT","_id":"rosepack@0.3.0","maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"homepage":"https://github.com/taskylizard/rosepack#readme","bugs":{"url":"https://github.com/taskylizard/rosepack/issues"},"bin":{"rosepack":"./dist/cli.mjs"},"dist":{"shasum":"f4a1238a6bbff84b93b9a7b0e59b1f5e7006c6ab","tarball":"https://registry.npmjs.org/rosepack/-/rosepack-0.3.0.tgz","fileCount":13,"integrity":"sha512-4pIAm6gUv9qg+kUCSRaFg1+qLfRf0FTR3NSwwzryn6k6lydziWAhtEWCVew3d3DHWwsImL4a+39w++AJ5lHRbQ==","signatures":[{"sig":"MEQCIEDAEgOLwDGp3KuCnPz7By9QdvHjUFFiok8SrTDxaX83AiAfVLuZQTJYXEcuxg9r93aPRd018/kRuVjyRsoBeig07w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/rosepack@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":318429},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.mjs","./cli":"./dist/cli.mjs","./http":"./dist/http.mjs","./vite":"./dist/vite.mjs","./package.json":"./package.json"},"scripts":{"dev":"vp pack --watch"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a8444bee-1feb-48bd-b7ef-20e45c2a3ec1"}},"devEngines":{"packageManager":{"name":"pnpm","onFail":"download","version":"11.13.1"}},"repository":{"url":"git+https://github.com/taskylizard/rosepack.git","type":"git"},"description":"A strongly typed Oceanic interaction and command framework.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","dependencies":{"obug":"2.1.4","gunshi":"0.37.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"npm:@voidzero-dev/vite-plus-core@0.2.4","bumpp":"12.0.0","vite-plus":"0.2.4","oceanic.js":"1.14.0","typescript":"7.0.2","@types/node":"26.1.1","@typescript/native-preview":"7.0.0-dev.20260707.2"},"peerDependencies":{"vite":"8.1.3","oceanic.js":"1.14.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rosepack_0.3.0_1785334085263_0.4721105488751651","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"rosepack","version":"0.4.0","author":"taskylizard","license":"MIT","_id":"rosepack@0.4.0","maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"homepage":"https://github.com/taskylizard/rosepack#readme","bugs":{"url":"https://github.com/taskylizard/rosepack/issues"},"bin":{"rosepack":"./dist/cli.mjs"},"dist":{"shasum":"673675b18f7c38189ded12db07b4df263f420a6d","tarball":"https://registry.npmjs.org/rosepack/-/rosepack-0.4.0.tgz","fileCount":13,"integrity":"sha512-xSpCrUnzZxcrpbP3c3AA3pdKVjQI+4cYX21xzT4hh7azo9Pv2KCn9CVZMnKtgg+8z8n8ma3x3aYCrg/j2j9q0g==","signatures":[{"sig":"MEQCIDsPqtgd4E/46l0MHE/xZheXyqkqkCp9FqCtwy5SFLLTAiAfAkT0i8gGKaHI/ZWm7eHI9x/ivwKL40tI/qeN6sEZZA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/rosepack@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":323154},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.mjs","./cli":"./dist/cli.mjs","./http":"./dist/http.mjs","./vite":"./dist/vite.mjs","./package.json":"./package.json"},"scripts":{"dev":"vp pack --watch"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a8444bee-1feb-48bd-b7ef-20e45c2a3ec1"}},"devEngines":{"packageManager":{"name":"pnpm","onFail":"download","version":"11.13.1"}},"repository":{"url":"git+https://github.com/taskylizard/rosepack.git","type":"git"},"description":"A strongly typed Oceanic interaction and command framework.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","dependencies":{"obug":"2.1.4","gunshi":"0.37.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"npm:@voidzero-dev/vite-plus-core@0.2.4","bumpp":"12.0.0","vite-plus":"0.2.4","oceanic.js":"1.14.0","typescript":"7.0.2","@types/node":"26.1.1","@typescript/native-preview":"7.0.0-dev.20260707.2"},"peerDependencies":{"vite":"8.1.3","oceanic.js":"1.14.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rosepack_0.4.0_1786905925747_0.5352220966862244","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"rosepack","version":"0.5.0","author":"taskylizard","license":"MIT","_id":"rosepack@0.5.0","maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"homepage":"https://github.com/taskylizard/rosepack#readme","bugs":{"url":"https://github.com/taskylizard/rosepack/issues"},"bin":{"rosepack":"./dist/cli.mjs"},"dist":{"shasum":"a9d9853b6b85f58d98039ac5f8eddc0f023aa933","tarball":"https://registry.npmjs.org/rosepack/-/rosepack-0.5.0.tgz","fileCount":21,"integrity":"sha512-IR/SLOmVxTZSl4XiPjpHjRyJvSbzr4s1Lsz5JCcD0+1k/V+Z8f2vcp3XbVbHdn4gSAYra0zv8mq1rlrTt9kHHA==","signatures":[{"sig":"MEUCIQDqGMRBPXYBehriN7iIW6x36TPPV2a23Gg5PmoURqFt0AIgG50eGbmE4lX/W3QeXMfbdTBhEk5LayP/w9e8iDhANnY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/rosepack@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":339751},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.mjs","./cli":"./dist/cli.mjs","./http":"./dist/http.mjs","./vite":"./dist/vite.mjs","./jsx-runtime":"./dist/jsx-runtime.mjs","./package.json":"./package.json","./jsx-dev-runtime":"./dist/jsx-dev-runtime.mjs"},"scripts":{"dev":"vp pack --watch"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a8444bee-1feb-48bd-b7ef-20e45c2a3ec1"}},"devEngines":{"packageManager":{"name":"pnpm","onFail":"download","version":"11.13.1"}},"repository":{"url":"git+https://github.com/taskylizard/rosepack.git","type":"git"},"description":"A strongly typed Oceanic interaction and command framework.","directories":{},"sideEffects":false,"_nodeVersion":"24.20.0","dependencies":{"obug":"2.1.4","gunshi":"0.37.0"},"publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"vite":"npm:@voidzero-dev/vite-plus-core@0.2.4","bumpp":"12.0.0","vite-plus":"0.2.4","oceanic.js":"1.14.0","typescript":"7.0.2","@types/node":"26.1.1","@typescript/native-preview":"7.0.0-dev.20260707.2"},"peerDependencies":{"vite":"8.1.3","oceanic.js":"1.14.0"},"peerDependenciesMeta":{"vite":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rosepack_0.5.0_1788431203065_0.4314249329306472","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"rosepack","version":"0.5.1","description":"A strongly typed Oceanic interaction and command framework.","homepage":"https://github.com/taskylizard/rosepack#readme","bugs":{"url":"https://github.com/taskylizard/rosepack/issues"},"license":"MIT","author":"taskylizard","repository":{"type":"git","url":"git+https://github.com/taskylizard/rosepack.git"},"bin":{"rosepack":"./dist/cli.mjs"},"type":"module","sideEffects":false,"exports":{".":"./dist/index.mjs","./cli":"./dist/cli.mjs","./http":"./dist/http.mjs","./jsx-dev-runtime":"./dist/jsx-dev-runtime.mjs","./jsx-runtime":"./dist/jsx-runtime.mjs","./vite":"./dist/vite.mjs","./package.json":"./package.json"},"publishConfig":{"access":"public","provenance":true},"dependencies":{"gunshi":"0.37.0","obug":"2.1.4"},"devDependencies":{"@types/node":"26.1.1","@typescript/native-preview":"7.0.0-dev.20260707.2","bumpp":"12.0.0","oceanic.js":"1.14.0","typescript":"7.0.2","vite":"npm:@voidzero-dev/vite-plus-core@0.2.4","vite-plus":"0.2.4"},"peerDependencies":{"oceanic.js":"1.14.0","vite":"8.1.3"},"peerDependenciesMeta":{"vite":{"optional":true}},"devEngines":{"packageManager":{"name":"pnpm","version":"11.13.1","onFail":"download"}},"engines":{"node":">=22"},"scripts":{"dev":"vp pack --watch"},"_nodeVersion":"24.20.0","_id":"rosepack@0.5.1","dist":{"integrity":"sha512-IJHd6PiJGRjiR8MqWjSceEjR+nVfuUUTdWw9dy2FyReXbhz+Hw3oz+wDYNFBBs+IEsi5KzISO4Y5Ttiqf3glKw==","shasum":"e99b60a029a1169c132545b25a3fdf1f4846ce10","tarball":"https://registry.npmjs.org/rosepack/-/rosepack-0.5.1.tgz","fileCount":21,"unpackedSize":340102,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/rosepack@0.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIA746vZim0lI9n8TidwvR+2Biijepo70KU7UZEtAdzI8AiBQB962oBYr39vQCH1oQQ5mhcMrGCNDem5W1U8Ieaop0Q=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a8444bee-1feb-48bd-b7ef-20e45c2a3ec1"}},"directories":{},"maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rosepack_0.5.1_1788438021279_0.2576766844360796"},"_hasShrinkwrap":false}},"time":{"created":"2024-11-07T11:22:44.497Z","modified":"2026-09-03T12:20:22.195Z","0.0.1-alpha":"2023-03-22T23:29:36.500Z","0.0.2-alpha":"2023-03-23T12:18:59.651Z","0.0.3-alpha":"2023-03-23T19:32:42.458Z","0.0.4-alpha":"2023-03-23T19:58:46.698Z","0.0.5-alpha":"2023-03-25T20:14:39.778Z","0.0.7-alpha":"2023-03-31T17:32:20.277Z","0.0.8-alpha":"2023-04-04T02:30:04.367Z","0.0.9-alpha":"2023-04-11T19:57:30.719Z","0.1.0-alpha":"2023-04-14T00:27:30.773Z","0.1.1-alpha":"2023-04-15T09:20:36.022Z","0.1.2-alpha":"2023-04-17T19:52:37.562Z","0.1.3-alpha":"2023-04-25T15:09:21.340Z","0.1.4-alpha":"2023-04-30T12:53:00.573Z","0.1.5-alpha":"2023-05-01T13:09:53.099Z","0.1.6-alpha":"2023-05-10T00:21:46.792Z","0.1.7-alpha":"2023-05-10T19:21:13.894Z","0.0.1-beta":"2023-05-11T21:22:21.922Z","0.0.2-beta":"2023-05-13T00:43:04.782Z","0.0.3-beta":"2023-05-13T17:06:27.059Z","0.0.1":"2023-05-15T16:38:36.097Z","0.0.2":"2023-05-28T12:37:18.538Z","1.0.0":"2023-06-04T17:53:09.780Z","1.0.1":"2023-06-27T18:34:54.833Z","1.0.2":"2023-06-29T01:42:27.679Z","1.0.3":"2023-07-06T21:28:42.379Z","1.0.4":"2023-11-12T19:01:31.293Z","1.0.5":"2023-11-12T20:01:53.767Z","1.0.6":"2023-11-18T12:08:37.435Z","1.0.7":"2023-12-09T12:48:39.784Z","1.0.8":"2023-12-11T18:43:23.942Z","1.0.9":"2023-12-11T18:46:59.262Z","1.0.10":"2023-12-13T17:24:12.585Z","1.0.11":"2023-12-17T09:43:35.811Z","1.0.12":"2023-12-22T02:49:00.742Z","1.0.13":"2023-12-29T17:39:24.820Z","1.0.14":"2024-01-07T13:54:29.485Z","1.0.15":"2024-01-07T14:12:36.472Z","1.0.16":"2024-01-10T04:47:38.809Z","1.0.17":"2024-01-13T13:39:43.552Z","1.0.18":"2024-01-22T18:01:57.390Z","1.0.19":"2024-03-08T23:10:13.030Z","1.0.20":"2024-07-17T21:32:25.982Z","1.0.21":"2024-07-17T21:33:23.902Z","0.0.0":"2024-11-07T11:22:44.689Z","0.1.0":"2026-07-24T17:49:12.670Z","0.2.0":"2026-07-25T16:42:50.010Z","0.3.0":"2026-07-29T14:08:05.403Z","0.4.0":"2026-08-16T18:45:25.890Z","0.5.0":"2026-09-03T10:26:43.218Z","0.5.1":"2026-09-03T12:20:21.431Z"},"bugs":{"url":"https://github.com/taskylizard/rosepack/issues"},"author":"taskylizard","license":"MIT","homepage":"https://github.com/taskylizard/rosepack#readme","repository":{"type":"git","url":"git+https://github.com/taskylizard/rosepack.git"},"description":"A strongly typed Oceanic interaction and command framework.","maintainers":[{"name":"taskylizard","email":"taskylizard@fmhy.net"}],"readme":"<img width=\"1500\" height=\"500\" alt=\"rosepack banner\" src=\"https://github.com/user-attachments/assets/fa76bdb5-1609-4b8b-857d-8315fe3c3cb4\" />\n\nrosepack is a highly typed interaction and prefix-command framework for [oceanic](https://oceanic.ws).\n\n## How to use\n\nYou can use rosepack in two ways:\n\n- Library mode: You do the setup for command registry construction, registration, process startup, and does not require a build tool aka Vite.\n- Framework mode: with the `rosepack/vite` Vite plugin, rosepack wraps Library mode and provides:\n  - Filesystem-based commands construction\n  - Compile-time validation\n  - Development guild synchronization\n  - Smart hot-module reloading\n  - Production bundling so you have everything needed, via Vite\n  - The registration cli, only changing what was changed\n\nWhat should you use? Well, it's about how much you want to do yourself.\n\n##### Use Framework mode if you:\n\n- are too new to have an opinion\n- need structured and a streamlined experience with best practices\n\n##### Use Library mode if you:\n\n- want to use rosepack as simply as possible\n- don't want a build-tool\n- can handle everything yourself\n\n## Examples\n\nThe repository has four runnable examples. Pick the smallest one that matches how your bot receives events:\n\n| Example                        | Use it for               | What it covers                                                                    |\n| ------------------------------ | ------------------------ | --------------------------------------------------------------------------------- |\n| [Library](examples/library)    | A bot without Vite       | Explicit command definitions, registries, Gateway events, and global registration |\n| [Framework](examples/rosepack) | A normal Gateway bot     | Filesystem discovery, generated types, HMR, and the registration CLI              |\n| [Starter](examples/starter)    | A feature tour           | Nested slash and prefix commands, hooks, parsers, and command invocation          |\n| [HTTP](examples/http)          | An interactions endpoint | Signed Hono requests without a Gateway connection                                 |\n\nFrom the repository root, install once and run the compact framework example:\n\n```sh\nvp install\ncd examples/rosepack\nvp exec rosepack prepare\nvp dev\n```\n\nCreate `.env` in the example directory before `vp dev`:\n\n```dotenv\nDISCORD_TOKEN=your-bot-token\nDISCORD_APPLICATION_ID=your-application-id\nDISCORD_DEV_GUILD_ID=your-development-guild-id\n```\n\nThe example registers commands into `DISCORD_DEV_GUILD_ID` while developing. The [library example](examples/library) needs only `DISCORD_TOKEN` and `DISCORD_APPLICATION_ID`; the [HTTP example](examples/http) also needs `DISCORD_PUBLIC_KEY` and a listening `PORT`.\n\n## Bind rosepack to your application\n\nStart with one file that binds your application context. Call `createRosepack` once, then import the builders from that bound instance.\n\nIn library mode, this can be `src/rosepack.ts`:\n\n```ts\nimport { createRosepack } from 'rosepack'\nimport type { NotesService } from './context.ts'\n\nexport interface AppContext {\n  notes: NotesService\n}\n\nexport const rosepack = createRosepack<AppContext>()\nexport const { button, component, messageMenu, modal, slash, userMenu } = rosepack\n\nexport const prefixCommands = rosepack.createPrefixCommands()\nexport const { prefix } = prefixCommands\n```\n\nThe generic only describes the object passed as `context.app`. It does not store a singleton application state inside rosepack. Your startup code creates `AppContext` and passes it to every dispatch call.\n\nFramework mode uses the same binding, but filename-based builders take the command name from the path:\n\n```ts\nexport const { button, component, messageMenu, modal, slash, slashFile, userMenu } = rosepack\nexport const { prefixFile: prefix } = prefixCommands\n```\n\nA library root command uses `slash` or `prefix` and supplies `name`. In framework mode, root and metadata files use `slashFile`, while nested slash-command files use the same recursive `slash` builder as library mode. The framework checks an explicit name during migration and requires it to match the filename.\n\n## Define a slash command\n\nAdd a flat command to `src/commands/ping.ts`:\n\n```ts\nimport { slash } from '../rosepack.ts'\n\nexport default slash({\n  name: 'ping',\n  description: 'check whether the bot is responding',\n  contexts: ['guild', 'botDm', 'privateChannel'],\n  installations: ['guild', 'user'],\n\n  async execute(context) {\n    await context.reply('pong')\n  }\n})\n```\n\nIn framework mode, the same file imports `slashFile` and drops `name`. `contexts` and `installations` are optional metadata; the handler still receives an Oceanic `CommandInteraction` through `context.interaction`.\n\nThe definition is just data plus an executor. Nothing talks to Discord until a registry is built and an event is dispatched.\n\n## Add typed options\n\nOptions are ordinary object properties, so TypeScript can infer the handler input from the definition:\n\n```ts\nexport default slash({\n  name: 'greet',\n  description: 'send a greeting',\n  options: {\n    style: {\n      choices: [\n        { name: 'Brief', value: 'brief' },\n        { name: 'Warm', value: 'warm' }\n      ],\n      description: 'how the greeting should sound',\n      kind: 'string',\n      required: true\n    }\n  },\n\n  async execute(context) {\n    const greeting = context.options.style === 'warm' ? 'good to see you' : 'hello'\n    await context.reply(greeting)\n  }\n})\n```\n\nHere `context.options.style` is the literal union `'brief' | 'warm'`, not an arbitrary string. Invalid option definitions, command names, and Discord limits on options or choices are checked when the registry is built. At dispatch time, rosepack parses the interaction and rejects a missing required option or a value outside its choices. Framework mode runs the definition checks during discovery and build.\n\n## Subcommands and groups\n\nA routed command uses the same `slash()` builder recursively for its root, groups, and executable leaves:\n\n```ts\nexport default slash({\n  name: 'notes',\n  description: 'manage personal notes',\n  subcommands: {\n    add: slash({\n      description: 'save a note',\n      options: {\n        content: {\n          description: 'the note to save',\n          kind: 'string',\n          maxLength: 1_000,\n          required: true\n        }\n      },\n\n      async execute(context) {\n        const count = context.app.notes.add(context.interaction.user.id, context.options.content)\n        await context.reply(`saved note ${count}`)\n      }\n    }),\n    list: slash({\n      description: 'list saved notes',\n\n      async execute(context) {\n        const notes = context.app.notes.list(context.interaction.user.id)\n        await context.reply(notes.length === 0 ? 'no saved notes' : notes.join('\\n'))\n      }\n    }),\n    admin: slash({\n      description: 'administrative note actions',\n      subcommands: {\n        clear: slash({\n          description: 'clear your notes',\n\n          async execute(context) {\n            context.app.notes.clear(context.interaction.user.id)\n            await context.reply('cleared')\n          }\n        })\n      }\n    })\n  }\n})\n```\n\nDiscord permits only `command → group → subcommand`. The root `notes` cannot also have an `execute` function or options, and `admin` cannot execute. Every nested node crosses its own `slash()` inference boundary, so its options and guards remain local and exact. Each level has Discord's 25-child limit. These constraints are represented in the builder types and checked again by `createRegistry`.\n\n## Build, register, and dispatch the registry\n\nOnce definitions exist, put them in an `InteractionRegistry`. The registry freezes the command tree, converts application commands to Discord payloads, and gives dispatch a single route:\n\n```ts\nconst registry = rosepack.createRegistry({\n  components: [deleteNote, chooseColor],\n  messageContextMenus,\n  modals,\n  slashCommands,\n  userContextMenus\n})\n\nclient.once('ready', async () => {\n  await registry.registerGlobal({\n    applicationID: client.application.id,\n    client\n  })\n})\n\nclient.on('interactionCreate', async (interaction) => {\n  await registry.dispatch({ app, interaction })\n})\n```\n\nThe snippet assumes `client` is an Oceanic `Client`, `app` is your `AppContext`, and the arrays contain the definitions from the next sections. `registerGlobal` bulk-replaces the application's global application-command payloads with this registry. Components and modals are routed at runtime and are not application-command registration payloads. For guild registration or incremental production registration, use the framework CLI or `registry.modules` instead.\n\nA root command can install `beforeExecute(context)` and `onError(context, error)` hooks. The same lifecycle hooks are available on context menus, components, and modals. Interaction handlers use `defer`, `reply`, `editResponse`, `followUp`, and `deleteResponse`; component handlers also have `deferUpdate`, `update`, and `editParent` for editing the message that contained the component.\n\nInteraction definitions can declare an ordered `guards` tuple. Guards run before `beforeExecute`, stop at the first denial, and send ephemeral denials by default. Slash guards inherit from root to group to leaf; context menus, components, and modals run their local guards.\n\n```ts\nconst { guard, slash } = createRosepack<AppContext>()\n\nexport default slash({\n  name: 'settings',\n  description: 'Manage server settings',\n  guards: [\n    guard.guild({ message: 'Use this command in a server.' }),\n    guard.userPermissions('MANAGE_GUILD', { message: 'Manage Server is required.' }),\n    guard.role(STAFF_ROLE_ID, { message: 'The Staff role is required.' }),\n    guard(({ app }) => (app.ready ? guard.allow() : guard.deny('Try again shortly.')))\n  ],\n  async execute(context) {\n    context.interaction.guildID // string\n  }\n})\n```\n\n## Add context menus and a modal\n\nContext menus use separate builders, which narrows `context.target` to an Oceanic `User` or `Message`:\n\n```ts\nexport const inspectUser = userMenu({\n  name: 'Inspect user',\n\n  async execute(context) {\n    await context.reply(`user: ${context.target.username}`)\n  }\n})\n\nexport const quoteMessage = messageMenu({\n  name: 'Quote message',\n\n  async execute(context) {\n    await context.reply(context.target.content || '(no text)')\n  }\n})\n```\n\nModal fields and route parameters are inferred from one definition:\n\n```ts\nexport const editNoteModal = modal({\n  customID: 'notes/edit/:noteID',\n  title: 'Edit note',\n  fields: {\n    content: {\n      kind: 'text',\n      label: 'Note',\n      maxLength: 1_000,\n      required: true,\n      style: 'paragraph'\n    }\n  },\n\n  async execute(context) {\n    const noteID = context.params.noteID\n    const content = context.values.content\n    await context.reply(`updated ${noteID}: ${content}`)\n  }\n})\n```\n\nOpen the modal from a slash or context-menu handler:\n\n```ts\nawait context.showModal(editNoteModal, {\n  params: { noteID: note.id },\n  values: { content: note.content }\n})\n```\n\n## Route buttons and select menus\n\nComponents use the same custom-ID route style as modals. `button` is a convenience builder;\n`component` accepts `button`, `stringSelect`, `userSelect`, `roleSelect`, `mentionableSelect`, or\n`channelSelect` and narrows the interaction and select values in the handler:\n\n```ts\nexport const deleteNote = button({\n  customID: 'notes/:noteID/delete',\n\n  async execute(context) {\n    await context.deferUpdate()\n    await context.editParent(`deleted ${context.params.noteID}`)\n  }\n})\n\nexport const chooseColor = component({\n  componentType: 'stringSelect',\n  customID: 'notes/:noteID/color',\n\n  async execute(context) {\n    const color = context.values[0]\n    await context.update(`set ${context.params.noteID} to ${color}`)\n  }\n})\n\nconst componentID = deleteNote.buildID({ params: { noteID: note.id } })\n```\n\nComponent route parameters are URL-encoded before they are put in Discord's 100-character custom\nID. A component registry dispatches buttons and select menus alongside commands and modals. In\nframework mode, export the generated `virtual:rosepack/components` tuple and pass it to\n`createCompiledRegistry`.\n\nRegister all three interaction kinds together:\n\n```ts\nconst registry = rosepack.createRegistry({\n  components: [deleteNote, chooseColor],\n  messageContextMenus: [quoteMessage],\n  modals: [editNoteModal],\n  slashCommands,\n  userContextMenus: [inspectUser]\n})\n```\n\nRoute parameters are URL-encoded into Discord's custom ID and decoded on submission. They are untrusted input, so authorize access to `noteID` before changing a note.\n\n## Render components with JSX\n\nrosepack ships a zero-dependency JSX runtime for Discord's Components V2. Set `jsx: 'react-jsx'` and `jsxImportSource: 'rosepack'` in your `tsconfig.json`, then compose components into a message:\n\n```tsx\nimport { ComponentMessage, Container, Section, Separator, TextDisplay, Thumbnail } from 'rosepack'\n\nconst tree = (\n  <Container accentColor={0x5865f2}>\n    <Section accessory={<Thumbnail url=\"https://example.com/avatar.png\" />}>\n      <TextDisplay>## Weekly digest</TextDisplay>\n      <TextDisplay>Here is what happened while you were away.</TextDisplay>\n    </Section>\n    <Separator spacing={1} />\n    <TextDisplay>-# rendered with rosepack/jsx-runtime</TextDisplay>\n  </Container>\n)\n\nawait context.reply(ComponentMessage({ children: [tree] }))\n```\n\nThe runtime lives at `rosepack/jsx-runtime` (and `rosepack/jsx-dev-runtime` for development transforms), and every component is also exported from the package root for direct calls without JSX syntax. JSX elements are typed as `JSX.Element`; direct calls keep precise return types, so `TextDisplay({ children: 'hi' })` is a `TextDisplayComponent`.\n\n## Add prefix commands\n\nPrefix commands use a positional schema string and a separate flag record:\n\n```ts\nconst ban = prefix({\n  name: 'ban',\n  aliases: ['b'],\n  description: 'ban a user',\n  options: '[user: User] [reason?: rest]',\n  flags: {\n    force: {\n      aliases: ['f'],\n      description: 'skip confirmation',\n      kind: 'boolean'\n    },\n    days: {\n      aliases: ['d'],\n      parser: 'integer'\n    }\n  },\n\n  async execute(context) {\n    const { user, reason } = context.options\n    const suffix = reason === undefined ? '' : ` for ${reason}`\n    await context.reply(\n      `Would ban ${user.username} (${user.id})${suffix}${context.flags.force ? ' (forced)' : ''}`\n    )\n  }\n})\n\nconst prefixRegistry = prefixCommands.createRegistry([ban], {\n  prefixes: ['!', '?']\n})\n\nclient.on('messageCreate', async (message) => {\n  await prefixRegistry.dispatch({ app, message })\n})\n```\n\n`[name: Parser]` is required, `[name?: Parser]` is optional, and a `rest` parser must be last. The built-in parsers are `string`, `integer`, `number`, `boolean`, `rest`, `User`, `Member`, `Role`, `Channel`, and `Mentionable`. The Oceanic-aware parsers accept IDs and the corresponding mentions.\n\nBoolean flags understand `--force`, `--no-force`, and aliases such as `-f`. Value flags accept `--days 7` and `--days=7`; add `multiple: true` for repeated values. A bare `--` ends flag parsing. The registry ignores bot and webhook messages by default and can resolve prefixes asynchronously per message.\n\nPrefix nodes can contain nested children, and the same `prefix` builder creates every node:\n\n```ts\nconst moderation = prefix({\n  name: 'moderation',\n  aliases: ['mod'],\n  description: 'moderation commands',\n  subcommands: [\n    prefix({\n      name: 'users',\n      description: 'user moderation',\n      subcommands: [ban]\n    })\n  ]\n})\n```\n\nFor a parser that belongs to your application, define it once and pass it to the prefix scope:\n\n```ts\nconst Duration = rosepack.prefixParser({\n  consumption: 'token',\n  parse({ fail, value }) {\n    const match = /^(\\d+)([smhd])$/u.exec(value)\n    if (match === null) return fail('use a duration such as 30s or 5m')\n    const units = { d: 86_400, h: 3_600, m: 60, s: 1 } as const\n    return Number(match[1]) * units[match[2] as keyof typeof units]\n  }\n})\n\nconst prefixCommands = rosepack.createPrefixCommands({\n  parsers: { Duration }\n})\n```\n\nA schema such as `[timeout: Duration]` now supplies a `number` to the handler. Parser failures become structured `PrefixCommandParseError` values, which you can handle with `onParseError`.\n\n## Move the bot to framework mode\n\nThe Vite plugin discovers files, assembles their command tree, validates it, and emits the generated modules used by your runtime entry. A minimal `vite.config.ts` looks like this:\n\n```ts\nimport { defineConfig } from 'vite-plus'\nimport { rosepack } from 'rosepack/vite'\n\nexport default defineConfig({\n  plugins: [\n    rosepack({\n      prefixCommands: {\n        directory: 'src/prefix-commands',\n        scope: 'src/rosepack.ts'\n      },\n      slashCommands: {\n        directory: 'src/slash-commands'\n      }\n    })\n  ],\n  build: {\n    outDir: 'dist'\n  }\n})\n```\n\nThe plugin also discovers `src/components`, `src/user-context-menus`, `src/message-context-menus`, and `src/modals` unless you set one of those options to `false`. The `scope` on `prefixCommands` points at the module exporting your configured `prefixCommands` object; it is needed when you add custom parsers.\n\nChange the bound builders in `src/rosepack.ts`:\n\n```ts\nexport const { button, component, messageMenu, modal, slash, slashFile, userMenu } = rosepack\nexport const prefixCommands = rosepack.createPrefixCommands()\nexport const { prefixFile: prefix } = prefixCommands\n```\n\nNow `src/slash-commands/ping.ts` can export the same handler without a `name` property, and `src/prefix-commands/echo.ts` can omit both `name` and the old manual command list.\n\n## Filesystem routes and generated types\n\nA framework command's path is its Discord route. The compact example has this shape:\n\n```text\nsrc/\n├── rosepack.ts\n├── slash-commands/\n│   ├── ping.ts\n│   ├── notes/\n│   │   ├── _command.ts\n│   │   ├── add.ts\n│   │   └── show.ts\n│   └── admin/\n│       ├── _command.ts\n│       └── server/\n│           ├── _group.ts\n│           └── inspect.ts\n├── prefix-commands/\n│   └── echo.ts\n├── components/\n│   └── notes-delete.ts\n├── user-context-menus/\n├── message-context-menus/\n└── modals/\n```\n\n`ping.ts` becomes `/ping`; `notes/add.ts` becomes `/notes add`; and `admin/server/inspect.ts` becomes `/admin server inspect`.\n\nFor slash routes:\n\n- A flat file exports `slashFile({ ... })`.\n- A directory's `_command.ts` exports `slashFile({ ... })` root metadata and cannot declare `subcommands`.\n- A direct child file exports `slash({ ... })`.\n- A `_group.ts` exports `slashGroup({ description })` from `rosepack`; its children export `slash()`.\n- The path must already be a valid lowercase Discord command name. rosepack does not rewrite it.\n\nPrefix directories use `_command.ts` for every directory node and can nest deeper than Discord slash commands, up to rosepack's 32-level prefix tree limit. Child files provide the tree, so a prefix `_command.ts` supplies metadata but not a `subcommands` array.\n\nThe plugin emits typed virtual modules:\n\n```ts\nimport messageContextMenus from 'virtual:rosepack/message-context-menus'\nimport components from 'virtual:rosepack/components'\nimport modals from 'virtual:rosepack/modals'\nimport prefixCommandList from 'virtual:rosepack/prefix-commands'\nimport slashCommands from 'virtual:rosepack/slash-commands'\nimport userContextMenus from 'virtual:rosepack/user-context-menus'\nimport { prefixCommands, rosepack } from './rosepack.ts'\n\nconst registry = rosepack.createCompiledRegistry({\n  components,\n  messageContextMenus,\n  modals,\n  slashCommands,\n  userContextMenus\n})\nconst prefixRegistry = prefixCommands.createCompiledRegistry(prefixCommandList, {\n  prefixes: '!'\n})\n```\n\nThese imports are generated at build time. Their declarations, the modal route catalog, and the generated environment shim live under `.rosepack/`; include `.rosepack/**/*.d.ts` in `tsconfig.json`:\n\n```json\n{\n  \"compilerOptions\": {\n    \"paths\": {\n      \"#rosepack/*\": [\"./.rosepack/*\"]\n    }\n  },\n  \"include\": [\"src\", \"tests\", \"vite.config.ts\", \".rosepack/**/*.d.ts\"]\n}\n```\n\n`src/rosepack-env.d.ts` can simply import `../.rosepack/env.d.ts`. Run `vp exec rosepack prepare` after cloning when the editor needs the generated declarations before `vp dev` or `vp build` has run.\n\nThe generated modal catalog also gives route-string autocomplete and parameter checking:\n\n```ts\nawait context.showModal('notes/edit/:noteID', {\n  params: { noteID: note.id }\n})\n```\n\n## Develop, build, and register\n\nThe example scripts make the development and production stages explicit:\n\n```sh\n# Generate .rosepack declarations without starting the bot\nvp exec rosepack prepare\n\n# Discover, validate, generate, and run the development host\nvp dev\n\n# Bundle the runtime and the portable registration CLI\nvp build\n\n# Preview the registration diff\nvp run register:dry\n\n# Apply the registration diff, then start the built bot\nvp run register\nvp run start\n```\n\n`vp dev` watches command files and the module scope, regenerates virtual modules, synchronizes a development guild when `DISCORD_APPLICATION_ID`, `DISCORD_DEV_GUILD_ID`, and `DISCORD_TOKEN` are present, and restarts the exported `startRosepackApp` host when application code changes.\n\n`vp build` writes `dist/index.mjs`, `dist/rosepack.mjs`, and `dist/commands.manifest.json`. The generated CLI compares the manifest with Discord and creates, updates, or deletes only commands previously owned by this app. That ownership is stored in `.rosepack/registration.json`, so an unrelated command is not deleted just because it is absent from this build.\n\nUse `--guild ID` for a guild registration. The CLI's `modules list` command prints the generated module catalog without contacting Discord:\n\n```sh\nvp exec node --env-file-if-exists=.env dist/rosepack.mjs modules list\nvp run register --guild 123456789012345678\n```\n\nThe generated entry accepts `modules list` and `register`. The `register` command accepts `--dry-run`, `--guild`, `--cache`, and repeated `--module` options. The next section shows how module selection works.\n\n## Guild modules\n\nModules let a guild enable or disable a complete set of application commands. Define the catalog once; the IDs are the values you persist and the labels are what you show in Discord:\n\n```ts\n// src/modules.ts\nimport { defineModules } from 'rosepack'\n\nexport const modules = defineModules({\n  economy: {\n    description: 'Coins, balances, and shops',\n    label: '🍣 Economy'\n  },\n  moderation: {\n    description: 'Server moderation tools',\n    label: '🔨 Moderation'\n  }\n})\n```\n\nBind that catalog to a persistence adapter. The adapter's `mutate` operation must be atomic across all bot processes and must return the complete state after the toggle:\n\n```ts\nimport { createRosepack } from 'rosepack'\nimport { modules } from './modules.ts'\nimport type { AppContext } from './context.ts'\n\nexport const rosepack = createRosepack<AppContext>().withModules({\n  catalog: modules,\n\n  async read({ app, applicationID, guildID }) {\n    return app.moduleStore.read({ applicationID, guildID })\n  },\n  async mutate({ app, applicationID, enabled, guildID, module }) {\n    return app.moduleStore.mutate({ applicationID, enabled, guildID, module })\n  },\n  async readOwnedCommandKeys({ app, applicationID, guildID }) {\n    return app.moduleStore.readOwnedCommandKeys({ applicationID, guildID })\n  },\n  async writeOwnedCommandKeys({ app, applicationID, guildID, keys }) {\n    await app.moduleStore.writeOwnedCommandKeys({ applicationID, guildID, keys })\n  }\n})\n```\n\nThe snippet assumes `AppContext.moduleStore` implements those four storage methods. `readOwnedCommandKeys` and `writeOwnedCommandKeys` let rosepack delete stale commands that it previously registered while leaving unrelated commands alone.\n\nThe next command snippets use library mode so the complete tree fits in one place. In framework mode, put the same metadata in the discovered files and let their paths supply the names.\n\nMark a root slash command or a context menu with a module reference. The whole root command, including its subcommands, is then guild-scoped:\n\n```ts\nexport default slash({\n  module: modules.economy,\n  name: 'shop',\n  description: 'buy something from the server shop',\n\n  async execute(context) {\n    await context.reply('shop')\n  }\n})\n```\n\nA modular command is omitted from `registry.payload` and global registration. When you enable or sync its guild, rosepack reconciles every modular slash command, user menu, and message menu in that guild.\n\nUse `moduleChoices` to keep a module-management command tied to the catalog's exact ID union:\n\n```ts\nimport { moduleChoices } from 'rosepack'\n\nconst moduleOption = {\n  choices: moduleChoices(modules),\n  description: 'which module to change',\n  kind: 'string',\n  required: true\n} as const\n\nexport default slash({\n  name: 'modules',\n  description: 'manage server modules',\n  contexts: ['guild'],\n  installations: ['guild'],\n  subcommands: {\n    enable: slash({\n      description: 'enable a module',\n      options: { module: moduleOption },\n\n      async execute(context) {\n        const result = await context.modules.enable(context.options.module)\n        await context.reply(\n          result.changed\n            ? `enabled ${result.module.label}`\n            : `${result.module.label} is already enabled`\n        )\n      }\n    }),\n    disable: slash({\n      description: 'disable a module',\n      options: { module: moduleOption },\n\n      async execute(context) {\n        const result = await context.modules.disable(context.options.module)\n        await context.reply(\n          result.changed\n            ? `disabled ${result.module.label}`\n            : `${result.module.label} is already disabled`\n        )\n      }\n    })\n  }\n})\n```\n\nThe `as const` on the option is needed here because the object is shared between two definitions; an inline object infers the same type without it. In either handler, `context.options.module` is `'economy' | 'moderation'`.\n\nThe interaction context exposes `enable`, `disable`, `list`, and `isEnabled`. Enabling or disabling persists the desired state, reconciles the guild, and returns `changed`, `enabled`, `module`, and registration results. A module context is guild-only.\n\nRepair remote state after a deploy:\n\n```ts\nawait registry.modules.sync({\n  app,\n  applicationID: client.application.id,\n  client,\n  guildID\n})\n\nawait registry.modules.syncAll({\n  app,\n  applicationID: client.application.id,\n  client,\n  guildIDs\n})\n```\n\n`syncAll` accepts an optional `concurrency` limit. A failed Discord reconciliation is wrapped in `ModuleSynchronizationError` with the application, guild, and desired module IDs. If an interaction arrives after a module is disabled, rosepack skips the handler and calls the adapter's optional `onDisabled` hook so you can send an explanatory response.\n\nIn framework mode, point the plugin at the catalog export as well as binding the runtime adapter:\n\n```ts\nrosepack({\n  modules: {\n    scope: 'src/modules.ts'\n  }\n})\n```\n\nThe plugin validates module references during discovery and puts the catalog in the build manifest. It does not replace your persistence adapter.\n\nThe production CLI can inspect or select catalog entries:\n\n```sh\nvp exec node --env-file-if-exists=.env dist/rosepack.mjs modules list\nvp run register --guild 123456789012345678 --module economy --module moderation\n```\n\n`--module` requires `--guild` and may be repeated. A global registration never includes modular commands; a guild registration with no module filter includes the full manifest, while a filtered registration includes only the selected module commands.\n\n## Receive interactions over HTTP\n\nGateway mode is not required for slash commands, context menus, and modals. `createHttpInteractionHandler` accepts a Fetch-compatible `Request` and verifies Discord's Ed25519 signature before it parses the body:\n\n```ts\nimport { Hono } from 'hono'\nimport { Client } from 'oceanic.js'\nimport { createHttpInteractionHandler } from 'rosepack/http'\n\nconst client = new Client({ auth: `Bot ${token}` })\nawait client.restMode(false)\n\nconst handleInteraction = createHttpInteractionHandler({\n  app,\n  client,\n  publicKey,\n  registry\n})\n\nconst api = new Hono()\napi.post('/interactions', (context) => handleInteraction(context.req.raw))\n```\n\nA Discord `PING` gets a `PONG` response immediately. Commands, context menus, and modal submissions are hydrated into Oceanic interactions and sent through `registry.dispatch`; an acknowledged request ends with HTTP 204. Set `onUnhandledInteraction` for autocomplete and component interactions, which rosepack deliberately leaves to your application.\n\nPrefix commands still need the Gateway's message events. The complete signed Hono setup is in the [HTTP example](examples/http).\n\n## Inspect, invoke, and validate commands\n\nA registry is also a read-only command index:\n\n```ts\nconst pingNode = registry.get('ping')\nconst notesLeaf = registry.resolve('/notes admin clear')\nconst allRoots = registry.tree\n```\n\n`get` accepts a root name or the original definition object. `resolve` accepts a slash-style string or an array of path segments. Each node exposes its `name`, `description`, `path`, `children`, `executable`, and original `definition`.\n\nFrom a slash handler, invoke another executable definition without reparsing Discord input:\n\n```ts\nawait context.invoke(pingCommand, {})\n```\n\nrosepack validates the target's options, preserves its hooks, and rejects recursive invocation. Prefix handlers have the corresponding `context.invoke(target, { options, flags })` API.\n\nFor tooling or tests, inspect a tree without constructing a registry:\n\n```ts\nimport { lintSlashCommandTree, slashCommandToDiscord } from 'rosepack'\n\nconst issues = lintSlashCommandTree(commands)\nif (issues.length > 0) {\n  console.error(issues)\n}\n\nconst payload = slashCommandToDiscord(commands[0]!)\n```\n\n`lintSlashCommandTree` returns stable issue codes, paths, and messages. `slashCommandToDiscord` validates one root and returns the Oceanic registration payload; neither function sends a request. The [starter example](examples/starter) puts these pieces together with a larger command tree.\n","readmeFilename":""}