{"id":49881449,"url":"https://github.com/lcanady/rhost-template","last_synced_at":"2026-05-15T14:38:24.838Z","repository":{"id":354428353,"uuid":"1220984193","full_name":"lcanady/rhost-template","owner":"lcanady","description":"RhostMUSH softcode project template — compiler, Docker test runner, mush.json package registry, security-hardened tooling","archived":false,"fork":false,"pushed_at":"2026-04-28T13:30:55.000Z","size":85,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-28T15:27:57.063Z","etag":null,"topics":["mush","mushcode","rhostmush","softcode","template"],"latest_commit_sha":null,"homepage":"https://github.com/kumakun/rhost-template","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lcanady.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-25T15:47:21.000Z","updated_at":"2026-04-28T13:33:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lcanady/rhost-template","commit_stats":null,"previous_names":["lcanady/rhost-template"],"tags_count":null,"template":true,"template_full_name":null,"purl":"pkg:github/lcanady/rhost-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lcanady%2Frhost-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lcanady%2Frhost-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lcanady%2Frhost-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lcanady%2Frhost-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lcanady","download_url":"https://codeload.github.com/lcanady/rhost-template/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lcanady%2Frhost-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33070213,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T11:35:32.926Z","status":"ssl_error","status_checked_at":"2026-05-15T11:35:31.362Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mush","mushcode","rhostmush","softcode","template"],"created_at":"2026-05-15T14:38:22.710Z","updated_at":"2026-05-15T14:38:24.829Z","avatar_url":"https://github.com/lcanady.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rhost-template\n\nA batteries-included project template for building RhostMUSH softcode systems.\nProvides a compiler, Docker-based test runner, and CLAUDE.md conventions so every\nnew RhostMUSH project starts with the same structure.\n\n---\n\n## Requirements\n\n- Node.js 20+\n- npm\n- Docker (for `npm test`)\n- [Claude Code](https://claude.ai/code) with [mush-skills](https://github.com/lcanady/mush-skills) installed\n\n---\n\n## Quick Start\n\n```bash\n# 1. Install Claude Code skills (once per machine)\ngit clone https://github.com/lcanady/mush-skills ~/.claude/skills/mush-skills-repo\ncd ~/.claude/skills/mush-skills-repo \u0026\u0026 ./install.sh\n# Restart Claude Code\n\n# 2. Use this template on GitHub → \"Use this template\" button\n#    or copy locally:\ncp -r rhost-template my-new-game\ncd my-new-game\n\n# 3. Install Node dependencies\nnpm install\n\n# 4. Configure environment\ncp .env.example .env\n# Edit .env with your server details (only needed for manual installs)\n\n# 5. Build the installer\nnpm run build\n# → writes dist/installer.txt\n\n# 6. Run tests (requires Docker)\nnpm test\n```\n\n---\n\n## Project Structure\n\n```\nmy-project/\n├── src/                    # Softcode source (.mush files)\n│   └── system/             # Core system files\n│       ├── config.mush     # Object creation — ALWAYS runs first\n│       └── udfs-core.mush  # Core UDFs\n├── showcases/              # Showcase JSON files (one per feature demo)\n│   └── example.json        # Copy this to add a new showcase section\n├── tests/                  # TypeScript test suites (@rhost/testkit)\n│   ├── run.ts              # Master test runner (Docker + wave execution)\n│   ├── helpers.ts          # Shared helpers (createThing, createRoom, etc.)\n│   └── example.test.ts     # Starter suite — copy to add new tests\n├── tools/                  # Build tooling\n│   ├── build.ts            # Compiles .mush → dist/installer.txt\n│   ├── showcase.ts         # Interactive showcase runner\n│   ├── header.js           # Installer header (think/ansi lines)\n│   └── post.js             # Installer footer\n├── dist/                   # Compiled output (gitignored)\n│   └── installer.txt       # Paste this into RhostMUSH as Wizard\n├── db/                     # SQLite databases if needed (gitignored)\n├── scripts/                # Shell helpers\n├── resources/              # Reference PDFs/rulebooks (gitignored)\n├── .env                    # Local credentials (never commit)\n├── .env.example            # Committed env template\n├── CLAUDE.md               # Claude Code instructions for this project\n├── mush.json               # Softcode package manifest\n├── package.json\n└── tsconfig.json\n```\n\n---\n\n## Package Registry\n\nThis template is registry-ready. `mush.json` is the softcode equivalent of\n`package.json` — it declares your package's identity, namespace, and dependencies\non other softcode packages.\n\n### mush.json\n\n```json\n{\n  \"name\": \"@mushpkg/my-project\",\n  \"version\": \"1.0.0\",\n  \"main\": \"dist/installer.txt\",\n  \"namespace\": \"cg\",\n  \"server\": \"rhostmush\",\n  \"dependencies\": {\n    \"@mushpkg/bbs\": \"^1.3.0\",\n    \"@mushpkg/jobs\": \"github:lcanady/mush-jobs@v2.0.0\"\n  },\n  \"registry\": \"https://registry.mushpkg.dev\"\n}\n```\n\n### Installing dependencies\n\n```bash\nnpm run mush:install\n```\n\nFetches each entry in `mush.json → dependencies` into `deps/` and writes\n`mush-lock.json` with pinned versions and SHA-256 checksums. Commit `mush-lock.json`;\n`deps/` is gitignored (re-fetch after a fresh clone).\n\nSupported version specifiers:\n\n| Format | Example |\n|---|---|\n| Semver (registry) | `\"^1.3.0\"` |\n| GitHub release | `\"github:owner/repo@v2.0.0\"` |\n| Local path | `\"file:../local-pkg\"` |\n\n---\n\n## Build\n\n```bash\nnpm run mush:install   # fetch deps first (if any)\nnpm run build\n```\n\nCompiles all `.mush` files listed in `mush.json → installers[].manifest` into a single\n`dist/installer.txt`. Multiple named installers are supported — use `--only \u003cname\u003e` to\nbuild one:\n\n```bash\nnpm run build -- --only main\nnpm run build -- --list        # print installer names\n```\n\nDependency installers are automatically prepended before your own source files.\n\nThe compiler:\n- Collapses indented continuation lines into single-line commands\n- Strips blank lines\n- Passes `@@` comments and `think` lines through as-is\n- Executes `.js` / `.ts` helper scripts and appends their stdout\n\n### Adding a new source file\n\n1. Create `src/\u003csystem\u003e/my-feature.mush`\n2. Add `'\u003csystem\u003e/my-feature.mush'` (or `'\u003csystem\u003e/'` for all files in the dir) to\n   the `manifest` array of the appropriate installer in `mush.json`\n3. Run `npm run build`\n\n---\n\n## Showcase\n\nThe showcase runner lets you demo your softcode interactively against a live server —\nno test fixtures, no assertions, just real output. Useful for demos, debugging visual\noutput, and end-to-end walkthroughs.\n\n```bash\nnpm run showcase                     # interactive menu\nnpm run showcase -- --list           # list available sections (no connection)\nnpm run showcase -- my-section       # run one section, then menu\nnpm run showcase -- --spin --deploy  # spin Docker, deploy, then menu\n```\n\n### How it works\n\nEach showcase section is a JSON file in `showcases/`. Register files in `mush.json`:\n\n```json\n{\n  \"showcases\": [\n    { \"file\": \"showcases/hello-world.json\" }\n  ]\n}\n```\n\n### Showcase file format\n\n```json\n{\n  \"key\":   \"hello-world\",\n  \"label\": \"Hello world — basic UDF output\",\n  \"vars\": {\n    \"sys\": \"search(name=My System \u003ctmpl.sys\u003e)\"\n  },\n  \"steps\": [\n    { \"sub\": \"Basic eval\" },\n    { \"eval\": \"u({{sys}}/F.HELLO,{{player}})\", \"label\": \"+hello\" },\n    { \"sub\": \"Command with stored result\" },\n    { \"eval\": \"num(me)\", \"store\": \"wiz_dbref\" },\n    { \"command\": \"think {{wiz_dbref}}\", \"label\": \"wiz dbref\" },\n    { \"sub\": \"Reset player state\" },\n    { \"reset\": true }\n  ]\n}\n```\n\n### Step types\n\n| Field | Purpose |\n|---|---|\n| `{ \"sub\": \"text\" }` | Print a sub-heading |\n| `{ \"cmd\": \"text\" }` | Print an informational line |\n| `{ \"eval\": \"expr\", \"label\": \"...\" }` | Run `wiz.eval(expr)` and show result |\n| `{ \"eval\": \"expr\", \"store\": \"key\" }` | Eval and store result in vars (silent) |\n| `{ \"command\": \"cmd\", \"label\": \"...\" }` | Run `wiz.command(cmd)` and show output |\n| `{ \"reset\": true }` | Wipe all `_*` attrs on the showcase player |\n| `{ \"set_stats\": \"INT:6 REF:7 ...\" }` | Set `_CG_STAT_*` attrs (chargen systems) |\n\n### Template tokens\n\nAll `eval`, `command`, and `label` strings support `{{token}}` interpolation:\n\n| Token | Value |\n|---|---|\n| `{{player}}` | ShowcasePlayer dbref |\n| `{{key}}` | Any key declared in the file's `vars` block |\n| `{{key}}` | Any key stored by a prior `store` step |\n\n`vars` values are MUSH expressions evaluated at startup (e.g. `search(name=...)`).\nThe result's dbref is extracted automatically; the full result is kept if no dbref\nis found (useful for non-dbref values).\n\n### CLI options\n\n| Flag | Purpose |\n|---|---|\n| `--spin` | Start a fresh Docker container |\n| `--deploy` | Deploy the first installer before running |\n| `--no-deploy` | Skip deploy even with `--spin` |\n| `--installer NAME` | Deploy a specific named installer |\n| `--host HOST` | MUSH host (default: `$RHOST_HOST` or `localhost`) |\n| `--port PORT` | MUSH port (default: `$RHOST_PORT` or `4201`) |\n| `--pass PASS` | Wizard password (default: `$RHOST_PASS` or `changeme`) |\n| `--list` | Print sections and exit, no connection needed |\n\n---\n\n## Testing\n\n```bash\nnpm test\n```\n\nThe test runner (`tests/run.ts`):\n\n1. Pulls `lcanady/rhostmush:latest` and starts a throwaway container\n2. Logs in as Wizard, deploys `dist/installer.txt` in batches of 20 lines\n3. Creates one MUSH account per parallel test slot (WIZARD + ROYALTY + SIDEFX)\n4. Gives each account a private room (prevents connect/disconnect message bleed)\n5. Runs test **waves** in order; suites within a wave run in parallel\n6. Stops the container on pass or fail\n\n### Test waves\n\nEdit `WAVES` in `tests/run.ts` to control grouping and parallelism:\n\n```typescript\nconst WAVES: string[][] = [\n    // Wave 1 — fast unit tests (run in parallel)\n    ['feature-a.test.ts', 'feature-b.test.ts'],\n    // Wave 2 — integration tests (run after Wave 1 completes)\n    ['workflow.test.ts'],\n    // Wave 3 — heavy/queue-sensitive (run alone)\n    ['e2e.test.ts'],\n];\n```\n\n### Writing a test suite\n\nCopy `tests/example.test.ts` and adapt:\n\n```typescript\nimport { RhostRunner } from '@rhost/testkit';\nimport { createThing } from './helpers';\n\nconst runner = new RhostRunner();\nlet sysObj: string;\n\nrunner.describe('My Feature', ({ it, beforeAll }) =\u003e {\n\n    beforeAll(async ({ client }) =\u003e {\n        // Always look up by name — never hardcode dbrefs\n        sysObj = (await client.eval('search(name=My System \u003csys\u003e)')).trim();\n    });\n\n    it('F.MY.FUNC returns expected value', async ({ expect }) =\u003e {\n        await expect(`u(${sysObj}/F.MY.FUNC,arg)`).toBe('expected');\n    });\n\n});\n\nrunner.run({\n    host:     process.env.RHOST_HOST || 'localhost',\n    port:     parseInt(process.env.RHOST_PORT || '4201', 10),\n    username: process.env.RHOST_USER || 'Wizard',\n    password: process.env.RHOST_PASS || '',\n}).then(r =\u003e process.exit(r.failed \u003e 0 ? 1 : 0))\n  .catch(err =\u003e { console.error(err); process.exit(1); });\n```\n\n---\n\n## Softcode Conventions\n\n### Object naming and namespaces\n\nSet a `namespace` in `mush.json`. Objects use `\u003cns.tag\u003e` format:\n\n```mush\n@@ mush.json: { \"namespace\": \"cg\" }\n@create Chargen System \u003ccg.sys\u003e\n@create Chargen Data   \u003ccg.data\u003e\n```\n\nLook up in tests with: `search(name=Chargen System \u003ccg.sys\u003e)`\n\nThe `\u003cns.tag\u003e` convention prevents name collisions between packages on the same server.\n\n### Attribute naming\n\n| Pattern | Purpose |\n|---|---|\n| `F.VERB.NOUN` | User-defined functions |\n| `CMD.VERB` | Command handlers (`$+cmd`) |\n| `_COR_*` | Hidden internal state (wiz-only via `_` prefix) |\n| `_\u003cSYS\u003e_*` | System-specific hidden state |\n\n### `_` prefix = wiz-only hidden\n\nIn RhostMUSH, a `_` prefix makes an attribute invisible to non-wizard players.\nUse it for all internal state that players should not read or set directly.\n\n### Comments\n\n- `@@` at line start = MUSH comment (safe anywhere)\n- `/*` is only a comment when it appears at `^\\s*/\\*` (start of line)\n- Never use `/*` mid-line or inside attribute values\n\n---\n\n## Manual Installation (no Docker)\n\n```bash\nnpm run build\n# Paste contents of dist/installer.txt into your RhostMUSH session as Wizard\n# Then: @shutdown/reboot\n```\n\n---\n\n## Environment Variables\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `RHOST_HOST` | `localhost` | Server hostname |\n| `RHOST_PORT` | `4201` | Server port |\n| `RHOST_USER` | `Wizard` | Login name |\n| `RHOST_PASS` | `changeme` | Login password |\n| `RHOST_IMAGE` | `lcanady/rhostmush:latest` | Docker image for tests |\n\nCopy `.env.example` → `.env` and fill in values. Never commit `.env`.\n\n---\n\n## Claude Code Skills\n\n| Skill | Use for |\n|---|---|\n| `/mush-build` | Writing new softcode |\n| `/mush-test` | Writing `@rhost/testkit` tests |\n| `/mush-lint` | Static analysis before build |\n| `/mush-review` | Code review (logic, patterns) |\n| `/mush-security` | Security audit |\n| `/mush-coverage` | Find untested attributes |\n| `/mush-simulate` | Trace execution without a server |\n| `/mush-efficiency` | Optimize speed and attribute count |\n| `/mush-explain` | Explain what softcode does |\n| `/mush-docs` | Generate in-game help text |\n| `/mush-deps` | Map attribute dependencies |\n| `/mush-readme` | Regenerate this README |\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flcanady%2Frhost-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flcanady%2Frhost-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flcanady%2Frhost-template/lists"}