Agent skill

Server Feature Dev

by scragnog in scragnog/HOT-Step-CPP

Guides adding or modifying a feature in the HOT-Step CPP Node/TypeScript server (Express route + service pattern, better-sqlite3 schema changes, engine calls via aceClient, logging, tsx watch dev…

MITAuto-check: notesBackend & APIs

Install Server Feature Dev

skills CLI
$ npx skills add scragnog/HOT-Step-CPP --skill server-feature-dev -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install scragnog/HOT-Step-CPP server-feature-dev --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/scragnog/HOT-Step-CPP.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/server-feature-dev .claude/skills/server-feature-dev && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
server-feature-dev
GitHub stars
170
Token cost
~6.5k tokens
SKILL.md length
2,872 words
Files
2
Skills in repo
18
Repo updated
First seen
Licence
MIT

At a glance

Guides adding or modifying a feature in the HOT-Step CPP Node/TypeScript server (Express route + service pattern, better-sqlite3 schema changes, engine calls via aceClient, logging, tsx watch dev…

  • Works in 11 steps: Type-check with npx tsc --noEmit during… → Node 20 to 24 LTS, 24 recommended.… → ESM everywhere — relative imports MUST… → …
  • Creating/editing files under server/src/
  • SKILL.md covers When to use this skill, Golden rules (hard constraints…, Dev loop and Procedure: add a new feature…, plus 8 more sections
  • Calls npx, npm and git

What it does

Server Feature Dev is an agent skill from scragnog/HOT-Step-CPP. Guides adding or modifying a feature in the HOT-Step CPP Node/TypeScript server (Express route + service pattern, better-sqlite3 schema changes, engine calls via aceClient, logging, tsx watch dev loop). Use when creating/editing files under server/src/, adding an /api/ endpoint, changing the SQLite schema, adding a config/env setting, wiring a server feature to the C++ engine, or debugging server-side failures (stalled/stuck generation jobs, engine HTTP timeouts, SQLite errors, engine crash-respawn loops).

Its SKILL.md is about 6.5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `reference.md`).

It sits in Backend & APIs, covering REST APIs, React components and Backend development. It works with SQLite, C++, TypeScript and npm. The repository describes itself as: Turn dials. Summon bangers! NOW WITH MORE C++! Local AI music generation powered by GGML. The licence is MIT.

When your agent uses it

  • Creating/editing files under server/src/
  • Adding an /api/ endpoint
  • Changing the SQLite schema
  • Adding a config/env setting

Example prompts

  • “Use the server-feature-dev skill to guide adding or modifying a feature in the HOT-Step CPP Node/TypeScript server (Express route + service pattern…”
  • “/server-feature-dev”

Requirements

  • Node.js

Workflow steps

11 steps, taken from the first numbered list in SKILL.md.

  1. Type-check with npx tsc --noEmit during dev; only npm run build before user testing. (Institutional rule, verbatim.) tsx watch runs…
  2. Node 20 to 24 LTS, 24 recommended. server/package.json:5-7 enforces >=20.0.0 <25.0.0. better-sqlite3 is a native module; postinstall runs…
  3. ESM everywhere — relative imports MUST end in .js even though sources are .ts. server/package.json:8 sets "type": "module". import {…
  4. Never block a request on long work. The engine is single-threaded; generations take minutes. Return a job id immediately and let the UI…
  5. Never call getDb() at module import time. initDb() runs at index.ts:60; a query executed during module load throws Database not…
  6. Declare static routes before /:id params. Express matches in order — GET /recent after GET /:id returns a 404 for id "recent". See the…
  7. Git: all work on master, stage explicit paths (never git add -A, never git add -f on gitignored paths — data/, logs/, docs/plans/…
  8. PowerShell syntax: ; not && for command chaining on this machine.
  9. Don't visually verify the UI with a browser agent — ask the human user; they provide screenshots. (Hitting API endpoints programmatically…
  10. If your change touches C++ (engine/src/), rebuild via dev-rebuild.bat at repo root — never engine/build.cmd directly (you cannot reliably…
  11. Never delete generated audio or test outputs, even ones you predict are bad — the human verifies results by ear. Leave all artifacts in…

What it can do on your machine

Read from SKILL.md and the folder at commit 24b12b5. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • npx
    • npm
    • git
    • tsx
    • cmake

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npx, npm and git, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Server Feature Dev loads about 6.5k tokens when it runs. Until then it costs about 133 tokens; SKILL.md has 2,872 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~133
When it runs · the whole SKILL.md, loaded when a task matches
~6.5k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check: notes

The automated check noted patterns worth knowing about, such as sudo or a known installer.

  • NoteMentions a .env fileSKILL.md:27
    a config value or a Settings-UI-exposed `.env` key (`server/src/config.ts`).
  • NoteMentions a .env fileSKILL.md:114
    ## Procedure: add a config / .env setting
  • NoteMentions a .env fileSKILL.md:116
    `server/data/hotstep.db`; `DATA_DIR` in `.env` resolves relative to `server/`, NOT the repo root — config.ts:192. A stal
  • NoteMentions a .env fileSKILL.md:122
    95) — **without this the value saves to `.env` but never hot-patches the live config**.
  • NoteMentions a .env fileSKILL.md:124
    , the code is POST; code wins) rewrites `.env` preserving comments and line endings, calls `reloadEnvConfig()`, and retu
  • NoteMentions a .env fileSKILL.md:164
    ver/src/config.ts` | All configuration; `.env` hot-reload (`EXPOSED_ENV_KEYS` / `RESTART_REQUIRED_KEYS` / `reloadEnvConf
  • NoteMentions a .env fileSKILL.md:176
    | `server/src/routes/settings.ts` | `.env` read/rewrite + hot-reload endpoint (`POST /api/settings/env`) |

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from scragnog/HOT-Step-CPP at commit 24b12b5, republished under its MIT licence (© scragnog). 2,872 words, ~6,480 tokens.

Download SKILL.mdSave it as .claude/skills/server-feature-dev/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
name
server-feature-dev
description
Guides adding or modifying a feature in the HOT-Step CPP Node/TypeScript server (Express route + service pattern, better-sqlite3 schema changes, engine calls via aceClient, logging, tsx watch dev loop). Use when creating/editing files under server/src/, adding an /api/* endpoint, changing the SQLite schema, adding a config/env setting, wiring a server feature to the C++ engine, or debugging server-side failures (stalled/stuck generation jobs, engine HTTP timeouts, SQLite errors, engine crash-respawn loops).

Node Server Feature Development

The Node server (server/src/) is the middle tier of HOT-Step CPP, a local AI music-generation app. It serves the React UI, owns the SQLite database, and orchestrates the C++ inference engine (ace-server.exe, spawned as a child process). This skill covers how to add or change a server feature the way this codebase does it.

Glossary (no prior context assumed):

  • Engine / ace-server — the C++ binary doing actual AI inference, HTTP API on port 8085. The Node server spawns it and talks to it over HTTP.
  • DiT — Diffusion Transformer, the engine's main music-synthesis model. LM — the language model that generates "audio codes" before synthesis. VAE — decodes latents to audio. Pipeline: LM → DiT → VAE.
  • AceRequest — the canonical JSON request shape the engine accepts, defined in server/src/services/aceClient.ts:38-151.
  • aceReq — the AceRequest instance built for a generation job (from UI params via translateParams).
  • Adapter — a LoRA/LoKr fine-tune applied to the DiT at generation time.
UI (fetch /api/*) → Express :3001 (dev: Vite on :3000 proxies /api to it) → routes/*.ts → services/*.ts
                                        ├→ getDb()  (better-sqlite3, synchronous)
                                        └→ aceClient (HTTP → ace-server :8085)

When to use this skill

  • Adding a new /api/* endpoint or a whole feature (route + service).
  • Modifying SQLite schema or queries (server/src/db/database.ts).
  • Adding a config value or a Settings-UI-exposed .env key (server/src/config.ts).
  • Making the server call the engine (new AceRequest field, new engine endpoint).
  • Debugging server-side failures (jobs stalling, engine timeouts, DB errors).

Golden rules (hard constraints — each prevents expensive damage)

  1. Type-check with npx tsc --noEmit during dev; only npm run build before user testing. (Institutional rule, verbatim.) tsx watch runs TypeScript directly, so a build is wasted work mid-dev — but tsx also tolerates errors that tsc rejects, so type-check before committing.
    powershell
    cd d:\Ace-Step-Latest\hot-step-cpp\server; npx tsc --noEmit
  2. Node 20 to 24 LTS, 24 recommended. server/package.json:5-7 enforces >=20.0.0 <25.0.0. better-sqlite3 is a native module; postinstall runs npm rebuild better-sqlite3 (package.json:14).
  3. ESM everywhere — relative imports MUST end in .js even though sources are .ts. server/package.json:8 sets "type": "module". import { config } from '../config.js' — omitting .js may run under tsx but fails tsc. There is no __dirname; the pattern is const __dirname = path.dirname(fileURLToPath(import.meta.url)) (config.ts:7, index.ts:46).
  4. Never block a request on long work. The engine is single-threaded; generations take minutes. Return a job id immediately and let the UI poll (see the worked example). The generation queue is deliberately serialized to one job at a time (generate.ts:1286-1292).
  5. Never call getDb() at module import time. initDb() runs at index.ts:60; a query executed during module load throws Database not initialized. Call initDb() first. (database.ts:17-22). Query only inside handlers/functions.
  6. Declare static routes before /:id params. Express matches in order — GET /recent after GET /:id returns a 404 for id "recent". See the explicit warning at songs.ts:47 and seeds.ts (/favorites, /random at lines 131/143 precede /:name at 156).
  7. Git: all work on master, stage explicit paths (never git add -A, never git add -f on gitignored paths — data/, logs/, docs/plans/, checkpoints/ are gitignored on purpose), commit locally often, push only with explicit user approval. Pushing any v* tag triggers a full multi-platform CI release build.
  8. PowerShell syntax: ; not && for command chaining on this machine.
  9. Don't visually verify the UI with a browser agent — ask the human user; they provide screenshots. (Hitting API endpoints programmatically is fine.)
  10. If your change touches C++ (engine/src/), rebuild via dev-rebuild.bat at repo root — never engine/build.cmd directly (you cannot reliably tell whether the app is running; the Node server auto-respawns ace-server on crash, index.ts:284-309 → infinite respawn + file-lock loop). Never cmake --clean-first (20+ min CUDA recompile); for stale .obj issues delete only engine/build/acestep-core.dir/ and engine/build/Release/acestep-core.lib.
  11. Never delete generated audio or test outputs, even ones you predict are bad — the human verifies results by ear. Leave all artifacts in place unless the user explicitly asks for cleanup.

Dev loop

  1. Start dev mode: d:\Ace-Step-Latest\hot-step-cpp\dev.bat — runs Vite (UI, :3000, HMR) plus server\restart-loop.cmd, which loops npx tsx watch src/index.ts (restart-loop.cmd:6). Develop against http://localhost:3000. LAUNCH.bat (Node :3001 serving prebuilt ui/dist/) is the end-user prod path — agents don't run it.
  2. Save any server/src/**/*.ts file → tsx watch restarts the whole Node process, which kills and respawns ace-server too (it's a child process). Expect a few seconds of engine downtime after every save; a generation in flight will die.
  3. The restart loop re-enters if a .restart-requested marker file exists at repo root — this is how the in-app "restart server" works without killing Vite.
  4. Type-check before committing: cd d:\Ace-Step-Latest\hot-step-cpp\server; npx tsc --noEmit.
  5. Logs land in logs/<YYYY-MM-DD_HH-MM-SS>/ at repo root: node_console.log (server), ace_engine.log (engine), generations/gen_<jobId>_<task>.log (per generation). Newest folder = current session.

Procedure: add a new feature (route + service)

  1. Create the route file server/src/routes/myFeature.ts. Convention: open with a comment block stating the mount point and a route table (best example: seeds.ts:1-20).
    ts
    // myFeature.ts — one-line purpose
    // Mounts at: /api/my-feature
    // Routes:
    //   GET  /api/my-feature        — list things
    import { Router } from 'express';
    import { config } from '../config.js';   // note the .js extension
    
    const router = Router();
    
    router.get('/', (req, res) => {
      try {
        res.json({ ok: true });
      } catch (err: any) {
        console.error('[MyFeature] list failed:', err.message);
        res.status(500).json({ error: err.message });
      }
    });
    
    export default router;
  2. Register it in server/src/index.ts — two edits: add the import to the block at index.ts:21-44 (import myFeatureRoutes from './routes/myFeature.js';) and mount it in the block at index.ts:72-95 (app.use('/api/my-feature', myFeatureRoutes);). URL segments are kebab-case (/api/model-manager, /api/cover-art, /api/stem-studio). One legacy exception: songBuilder.ts mounts at /api/builder.
  3. Put non-trivial logic in a service: server/src/services/myFeature.ts. When a feature grows, use a sub-folder — services/generation/ holds translateParams.ts, lmCache.ts, postProcessing.ts, sourceAudio.ts, etc., all extracted from the once-monolithic generate.ts.
  4. Auth-scope if the data is per-user: import { getUserId } from './auth.js'; then
    ts
    const userId = getUserId(req);
    if (!userId) { res.status(401).json({ error: 'Unauthorized' }); return; }
    (exact pattern: songs.ts:19-20). Auth is deliberately lightweight: an in-memory Map<token, userId> that resets on restart, with a single auto-created local user "Producer" (auth.ts:13-26). getUserId is auth.ts:98-102.
  5. Express 5 handler style: end early with res.status(...).json(...); return; — do not return res... from typed handlers. The SPA fallback uses Express-5 wildcard syntax app.get('/{*splat}', ...) (index.ts:136) — Express 4 '*' patterns do not work.
  6. Log with a bracketed feature tag — console.log('[MyFeature] ...'). Existing tags: [Server], [DB], [Config], [Generate], [Settings], [Logger], [ace-server]. This convention is what makes node_console.log greppable. To also surface lines in the UI's live log panel, import { pushLog } from './logs.js'; pushLog('text', 'server');.
  7. Type-check (npx tsc --noEmit), test via the running dev server, then commit explicit paths:
    powershell
    cd d:\Ace-Step-Latest\hot-step-cpp; git add server/src/routes/myFeature.ts server/src/services/myFeature.ts server/src/index.ts; git commit -m "feat(server): my feature"

Not every feature needs SQLite. routes/seeds.ts stores JSON files under server/data/seeds/ on purpose (ComfyUI-compatible, import/export-friendly) with a filename-sanitization regex at seeds.ts:44. Use seeds.ts as the template for small self-contained features; use generate.ts for engine-orchestrating ones.

Procedure: SQLite schema change

All DB work lives in server/src/db/database.ts. One connection, synchronous better-sqlite3 API, module singleton (initDb() once at startup, getDb() everywhere else, closeDb() on shutdown at index.ts:564). Pragmas at open: WAL, synchronous = NORMAL, foreign_keys = ON (database.ts:32-34).

  1. New table: add a CREATE TABLE IF NOT EXISTS block inside the big db.exec(...) at database.ts:37-209. Schema is re-run idempotently at every startup — there is no migration framework and no version table.
  2. New column on an existing table: add an entry to the checked-migration list (the songsMigrations array pattern, database.ts:213-259):
    ts
    {
      check: `SELECT COUNT(*) as c FROM pragma_table_info('songs') WHERE name='my_col'`,
      alter: `ALTER TABLE songs ADD COLUMN my_col TEXT DEFAULT ''`,
    },
    The loop runs ALTER only when the column is absent and logs [DB] Migration: .... Use this idiom for new columns. (A second, older idiom — try { db.exec("ALTER TABLE ...") } catch {} — exists for the lireek tables at database.ts:262-278; don't add to it.)
  3. Column conventions: TEXT primary keys are UUIDs (uuidv4()) for core tables; lireek (Lyric Studio) tables use INTEGER PRIMARY KEY AUTOINCREMENT; timestamps TEXT DEFAULT (datetime('now')); booleans are INTEGER 0/1, re-hydrated as !!row.is_public (songs.ts:38); complex values are JSON-in-TEXT parsed with JSON.parse(s.tags || '[]').
  4. Query style: inline getDb().prepare(sql).get/all/run(...params) with ? placeholders — no statement-caching layer. Dynamic filters append to the SQL string and a params array (songs.ts:24-32), including JSON queries like json_extract(generation_params, '$.source') = ?.
  5. Don't imitate the two-database pattern. db/lireekDb.ts is a query-helper module over the same connection; its initLireekDb/closeLireekDb are deprecated no-ops (lireekDb.ts:12-23). The old separate lireek.db was consolidated into server/data/hotstep.db (one-time ATTACH DATABASE migration at database.ts:293-386, a good reference if you ever need bulk data migration).

Procedure: add a config / .env setting

server/src/config.ts builds one exported config object (config.ts:96-257) grouping aceServer (engine exe autodetected from 4 candidate paths at config.ts:46-52; port default 8085), server (port 3001), data (getter dbPath → server/data/hotstep.db; DATA_DIR in .env resolves relative to server/, NOT the repo root — config.ts:192. A stale legacy data/ dir may exist at repo root with an outdated hotstep.db; ignore it), lireek (LLM keys/endpoints), vst, whisper, essentia. .env lives at repo root and is auto-bootstrapped from .env.example on first launch (config.ts:22-30).

To expose a new env setting in the Settings UI, you must touch three places in config.ts or it silently won't work:

  1. Add the key to the EXPOSED_ENV_KEYS whitelist (config.ts:265-286) — "nothing else leaks" to the UI.
  2. If it only takes effect at process/engine spawn time, also add it to RESTART_REQUIRED_KEYS (config.ts:289-294) so the UI shows "restart required".
  3. Add an apply('MY_KEY', setter, getter) line inside reloadEnvConfig() (config.ts:300-395) — without this the value saves to .env but never hot-patches the live config.

The Settings route (POST /api/settings/env, settings.ts:109-174 — note: the header comment says PUT, the code is POST; code wins) rewrites .env preserving comments and line endings, calls reloadEnvConfig(), and returns restartRequired.

Procedure: call the engine (aceClient)

server/src/services/aceClient.ts is the typed wrapper over ace-server's HTTP API (BASE = config.aceServer.url, i.e. http://127.0.0.1:8085 by default).

Critical constraint (aceClient.ts:6-8, verbatim comment): "ace-server uses single-threaded httplib. During heavy compute (DiT generation, adapter merge, VAE decode) it cannot respond to HTTP requests." Hence three timeout tiers via AbortSignal.timeout (aceClient.ts:17-19) — pick one deliberately for any new call:

TierValueFor
TIMEOUT_QUICK15 shealth, props, job submit
TIMEOUT_POLL30 sjob polling — fail fast, let the watchdog decide
TIMEOUT_RESULT300 sfetching large audio bodies
  • Async-job pattern: submitLm / submitSynth / submitUnderstand / warm POST and return a job id string; poll with pollJob(id) → { status: 'running'|'done'|'failed'|'cancelled', phase?, phase_step?, phase_total? } (phases like adapter_precompute, dit_inference — aceClient.ts:165-180); fetch the result with getJobResult(id) (GET /job?id=N&result=1); cancel with cancelJob(id). Sync endpoints (/spectral-lifter, /pp-vae-reencode) return processed WAV bytes directly.
  • A poll timeout is NOT a failure. The engine may simply be busy mid-DiT-step. Catch transient poll errors and retry; only treat explicit failed/cancelled statuses as terminal (see generate.ts:158-166).
  • New engine parameter? Add the field to the AceRequest interface first (aceClient.ts:38-151), then plumb it through services/generation/translateParams.ts (UI params → AceRequest). Known gotcha (institutional, from the LM-echo bug): server-only fields do not survive the engine's /lm round trip — synth requests are rebuilt from the original aceReq plus only the LM-generated fields (audio_codes, caption, lyrics, bpm, duration, keyscale, timesignature — generate.ts:237-246). Never assume a field you sent to /lm comes back.
  • File upload to the engine: multipart is hand-rolled with a manual boundary because the engine's parser expects a request JSON part plus named binary parts (audio, ref_audio, src_latents, ref_latents, seed_latents). The multipart path takes a single JSON object, not an array (aceClient.ts:374-378). Copy submitSynthMultipart (aceClient.ts:347-424).
  • Error convention: non-OK responses throw new Error('ace-server POST /x failed (status): body') (aceClient.ts:266-270); soft-fail helpers return null/false (getJobLatent, isReachable).
  • Readiness gate: routes that hit the engine should return 503 while the engine is booting. import { engineReady, engineBootStatus } from '../engineState.js'; — this module exists solely to break a circular import between index.ts and routes (engineState.ts:1-5). Pattern: generate.ts:1356-1363.
Show full SKILL.md (1,119 more words)Show less

Worked example (dissected): the generation feature

routes/generate.ts (~1600 lines) is the canonical "server orchestrates engine" feature. Copy its skeleton for anything long-running.

  1. In-memory job map — interface GenerationJob (id = uuid, userId, status pending|lm_running|synth_running|saving|succeeded|failed|cancelled, stage/progress for the UI, aceJobId, acePhase, result, error, params) in const jobs = new Map() (generate.ts:41-79). A TTL sweeper prunes terminal jobs older than 1 h every 10 min, with .unref() so the interval never blocks process exit (generate.ts:83-94).
  2. POST /api/generate (generate.ts:1355-1388): 503 if !engineReady; 401 if no user; build the job object from req.body; jobs.set(...); enqueueGeneration(job); respond { jobId, status } immediately.
  3. Serialized queue + retry (generate.ts:1286-1352): a plain closure array + generationRunning boolean — one generation at a time (the in-code comment explains why: subscribeLines() is a global log pub/sub with no job tagging, so concurrent jobs would cross-contaminate progress). One retry on transient failure with a fresh random seed; Cancelled/Unauthorized are non-retryable.
  4. Pipeline (runGeneration, generate.ts:173 onward): translateParams(job.params) → aceReq; write the resolved seed back into job.params so the DB stores the actual seed used (reproducibility, generate.ts:193-197); decide whether to skip the LM phase (explicit skipLm, audio_codes already present, or a cover-type task — generate.ts:207-210); startGenerationLog(job.id, taskType) + logGenerationParams(job.id, aceReq); check the LM cache (services/generation/lmCache.ts, keyed by seed+params); submit via aceClient.submitSynth or, when any source/reference audio or latents are present, submitSynthMultipart (generate.ts:814-827); stage timings via a small timed() helper.
  5. Watchdog polling (pollUntilDone, generate.ts:102-170): 500 ms interval; honors an AbortController for cancellation; stall detection — no stage/progress change for 120 s → cancel the engine job and throw (generate.ts:126-134); wall-clock timeout clamped to 5–120 min, default 45 (generate.ts:104-106); transient poll errors are logged and retried.
  6. Persist: on success, INSERT INTO songs (...) with a fresh uuid, generation_params = JSON.stringify(job.params) (generate.ts:1150-1161); audio files go under config.data.audioDir and are served statically at /audio/* (index.ts:98).
  7. Companion endpoints: GET /status/:id returns { jobId, status, stage, progress, result, error, ace_job_id, ace_phase, ace_phase_progress } (generate.ts:1391-1406); POST /cancel/:id sets status, cancels the engine job, fires the AbortController; plus /cancel-all, /queue, /reset-queue, and GET /stream/:id (SSE previews).

Per-generation logging (services/logger.ts): startGenerationLog(jobId, taskType) → logGeneration(jobId, 'INFO'|'DEBUG'|'WARNING'|'ERROR', msg) / logGenerationParams(jobId, obj) → finishGenerationLog(jobId, taskType) or failGenerationLog(jobId, error, taskType). Lines are buffered in memory and flushed to logs/<session>/generations/gen_<jobId>_<task>.log only on completion (logger.ts:95-178) — a crash before finish loses the buffer, but every line is also streamed live via pushLog.

Key files

PathRole
server/src/index.tsApp entry: route registration (imports :21-44, mounts :72-95), engine spawn/respawn (:158-316), graceful shutdown (:538-572)
server/src/config.tsAll configuration; .env hot-reload (EXPOSED_ENV_KEYS / RESTART_REQUIRED_KEYS / reloadEnvConfig)
server/src/db/database.tsSQLite singleton, schema (CREATE TABLE IF NOT EXISTS), column migrations
server/src/db/lireekDb.tsLyric-Studio query helpers over the same connection (init/close are deprecated no-ops)
server/src/services/aceClient.tsTyped HTTP client for the engine; AceRequest; timeout tiers; multipart
server/src/engineState.tsengineReady / engineBootStatus flags (circular-import breaker)
server/src/routes/generate.tsWorked example: job map, queue, watchdog, persistence
server/src/services/generation/translateParams.tsUI params → AceRequest translation
server/src/routes/seeds.tsTemplate for a small self-contained feature (filesystem storage, route-table header)
server/src/routes/songs.tsTemplate for auth-scoped SQLite CRUD (incl. the static-before-/:id warning)
server/src/routes/auth.tsToken-lite auth; getUserId(req) helper
server/src/services/logger.tsSession log dirs, logEngine, per-generation buffered logs
server/src/routes/logs.ts2000-line ring buffer + SSE at GET /api/logs; pushLog / subscribeLines
server/src/routes/settings.ts.env read/rewrite + hot-reload endpoint (POST /api/settings/env)
server/restart-loop.cmdDev-mode tsx watch wrapper with .restart-requested re-entry
server/package.jsonScripts (dev/typecheck), "type": "module", Node <25 engines pin

Failure signatures

SymptomCauseFix
Database not initialized. Call initDb() first.A module ran a DB query at import time, before index.ts:60Move queries inside handlers/functions
Engine requests time out during generationNormal — single-threaded httplib engine can't respond mid-computeUse the right timeout tier; retry transient poll errors; never treat a poll timeout as job failure
Generation stalled — no progress for NsThe 120 s watchdog fired and cancelled a wedged engine jobCheck gen_*.log + ace_engine.log in the newest logs/ session; the queue is unblocked automatically
[ace-server] Crashed 3 times within 30s — giving upMissing engine DLL / broken build; crash limiter halts respawn and sets engine not-ready (index.ts:296-301)Fix the engine build (via dev-rebuild.bat); check ace_engine.log
New env setting saves but never takes effectKey missing from the apply(...) list in reloadEnvConfig() or from EXPOSED_ENV_KEYSTouch all three places in config.ts (see procedure above)
Setting saved, hot-reload reports OK, engine ignores itKey is spawn-time (in RESTART_REQUIRED_KEYS)Restart the app so the engine child respawns with new args
GET /api/things/recent → 404 with id "recent"Static route declared after /:idReorder — static routes first (songs.ts:47)
better-sqlite3 native/ABI error after npm install or Node switchAddon built for a different Node ABI or skipped native rebuildUse Node 24; npm rebuild better-sqlite3 when the app is idle
tsc errors on imports that run fine under tsxMissing .js extension on a relative ESM importAdd .js to the import path
Spawned engine can't find DLLs despite a PATH editSpreading process.env on Windows creates a case-sensitive duplicate (Path vs PATH) that shadowsFind the real key case-insensitively before prepending — pattern at index.ts:221-244
Multipart submit to /synth fails with a parse errorSent a JSON array as the multipart request partEngine multipart expects a single object (aceClient.ts:374-378)

Institutional knowledge

  • VALIDATED (rule, verbatim): Type-check with npx tsc --noEmit during dev; only npm run build before user testing.
  • VALIDATED (code comment, aceClient.ts:6-8): the engine is single-threaded httplib and cannot answer HTTP during heavy compute — every timeout, watchdog, and retry in the server exists because of this.
  • VALIDATED (bug fixed, per project memory + generate.ts:234-246): LM echo sideband gotcha — server-only AceRequest fields don't survive the engine's /lm round trip. Rebuild synth requests from the original aceReq plus LM-generated fields only; never whitelist-copy from the LM response.
  • VALIDATED (in-code warning, index.ts:221-244): Windows process.env is a case-insensitive proxy but spreading it makes plain case-sensitive keys — mutating PATH blindly creates a shadowing duplicate of Path.
  • VALIDATED: the generation queue is intentionally single-file because subscribeLines() is global pub/sub with no job tagging (generate.ts:1286-1290); don't "fix" concurrency without solving log attribution first.
  • VALIDATED (config.ts:117-122): draft-LM speculative decoding is DISABLED — GGML per-call overhead negates the speedup. ACESTEPCPP_DRAFT_LM env re-enables it; auto-detect is commented out (config.ts:160-173).
  • Field present, feature reverted: adapter_section_isolation still exists in AceRequest (aceClient.ts:97) but the engine-side regional self-attn isolation was reverted for breaking musical continuity — treat the field as inert (unverified on the engine side — check engine code before relying on it).
  • VALIDATED: uncaughtException/unhandledRejection are logged but do not exit the process (index.ts:576-581), and there is no global Express error middleware — a throw in an async handler will not produce a clean JSON 500, so catch locally in every handler.

More depth (full aceClient method catalog, engine spawn args, settings hot-reload internals, DB idiom code, SSE log stream details): reference.md.

Deeper reading

  • CLAUDE.md (repo root) — orientation map, build/git rules.
  • engine/docs/ARCHITECTURE.md — engine internals, CLI, request JSON, generation modes (committed).
  • FEATURES.md — full feature catalogue (committed).
  • docs/dev/plugins-authoring.md — Lua plugin authoring; solvers/schedulers/guidance are plugins, not C++ (committed).
  • docs/plans/ — internal design/investigation docs. Gitignored, local-only — may be absent on a fresh clone.
  • server/src/data/assistant-knowledge.md — in-app assistant knowledge base (committed).

© scragnog, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file in .claude/skills/server-feature-dev of scragnog/HOT-Step-CPP.

  • SKILL.md
  • reference.md

Open the folder on GitHubat commit 24b12b5

Compare with similar skills

Server Feature Dev next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Server Feature Dev compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Server Feature Dev this skillscragnog/HOT-Step-CPP170—~6.5kAutomated safety check: NotesMIT
Developing Softwaretelagod/code-abyss244—~410Automated safety check: PassMIT
React Emailviclafouch/meme-studio1102 repos~3.6kAutomated safety check: PassMIT
Nodejs Expresscohen-liel/hivemind110—~856Automated safety check: PassApache-2.0
Implementing MCP ToolsPostHog/posthog-foss721—~3.7kAutomated safety check: PassMIT
MCP Server BuildershareAI-lab/learn-claude-code78k5 repos~1.2kAutomated safety check: PassMIT

Similar skills

  • Developing Software

    telagod/code-abyss

    Software development knowledge reference covering Python, Go, Rust, TypeScript, Java, C++, and Shell.

    244 GitHub stars~410 tokensUpdated 2 mo ago
    Backend & APIsAuto-check passed
  • React Email

    viclafouch/meme-studio

    A skill your agent uses when creating HTML email templates with React components - welcome emails, password resets, notifications, order confirmations, newsletters, or transactional emails.

    110 GitHub starsUsed in 2 repos~3.6k tokens
    Backend & APIsAuto-check passed
  • Nodejs Express

    cohen-liel/hivemind

    Node.js + Express backend patterns. An agent skill from cohen-liel/hivemind.

    110 GitHub stars~856 tokensUpdated 5 mo ago
    Backend & APIsAuto-check passed
  • Implementing MCP Tools

    PostHog/posthog-foss

    Official

    Guide for exposing PostHog product endpoints as MCP tools. An agent skill from PostHog/posthog-foss.

    721 GitHub stars~3.7k tokensUpdated today
    Backend & APIsAuto-check passed
  • MCP Server Builder

    shareAI-lab/learn-claude-code

    Walks through building MCP servers in Python or TypeScript that expose tools, resources and prompts to Claude, with templates, registration and testing.

    78k GitHub starsUsed in 5 repos~1.2k tokens
    Agent WorkflowsAuto-check passed
  • Nxv

    utensils/nxv

    Find any version of any Nix package across nixpkgs git history using the nxv CLI or HTTP API.

    136 GitHub stars~7.9k tokensUpdated 1 mo ago
    Backend & APIsAuto-check: notes

More from scragnog/HOT-Step-CPP

All 18 skills in this repo
  • Ear Test Scoresheet

    scragnog/HOT-Step-CPP

    The standard way to run a listening test in HOT-Step - a local HTML score sheet next to the renders where Rob plays each track, scores it 1-5 on named criteria, and the page charts the two score…

    170 GitHub stars~1.9k tokensUpdated yesterday
    Auto-check passed
  • Engine Performance

    scragnog/HOT-Step-CPP

    Explains where HOT-Step generation time goes (LM/DiT/VAE), how the TensorRT paths activate, how to benchmark from logs, and which knobs trade quality for speed.

    170 GitHub stars~4.9k tokensUpdated yesterday
    Auto-check passed
  • Mm3 Backend

    scragnog/HOT-Step-CPP

    Maps HOT-Step's native MiniMax-Music3 backend — engine port modules, endpoints, server/UI integration, parity/fixture infrastructure, and the hard-won trap list.

    170 GitHub stars~4.5k tokensUpdated yesterday
    Auto-check passed
  • Mm3 Lm Adapter Training

    scragnog/HOT-Step-CPP

    The validated recipe for training MiniMax-Music3 planner-LM style adapters (artist/album clones) with ace-train mm3-lm-train and the Training Studio.

    170 GitHub stars~4k tokensUpdated yesterday
    Auto-check passed
  • Release Process

    scragnog/HOT-Step-CPP

    Runbook for cutting and publishing a HOT-Step CPP release via a v git tag that triggers the multi-platform CI build and drafts a GitHub Release.

    170 GitHub stars~5.1k tokensUpdated yesterday
    Auto-check passed
  • Upstream Sync

    scragnog/HOT-Step-CPP

    Safely pulls upstream acestep.cpp changes into the HOT-Step engine fork without destroying its integration hooks.

    170 GitHub stars~5k tokensUpdated yesterday
    Auto-check passed

Questions about Server Feature Dev

What does Server Feature Dev do?

Guides adding or modifying a feature in the HOT-Step CPP Node/TypeScript server (Express route + service pattern, better-sqlite3 schema changes, engine calls via aceClient, logging, tsx watch dev…. Server Feature Dev is an agent skill from scragnog/HOT-Step-CPP. Guides adding or modifying a feature in the HOT-Step CPP Node/TypeScript server (Express route + service pattern, better-sqlite3 schema changes, engine calls via aceClient, logging, tsx watch dev loop).

When should I use Server Feature Dev?

Server Feature Dev fits situations like: creating/editing files under server/src/; adding an /api/ endpoint; changing the SQLite schema; adding a config/env setting.

How do I install Server Feature Dev in Claude Code?

Run `npx skills add scragnog/HOT-Step-CPP --skill server-feature-dev -a claude-code`. Or copy the skill folder (.claude/skills/server-feature-dev in scragnog/HOT-Step-CPP) into .claude/skills/server-feature-dev in your project. Claude Code loads it when a task matches its description.

How do I install Server Feature Dev in Codex?

Run `npx skills add scragnog/HOT-Step-CPP --skill server-feature-dev -a codex`. Or copy the skill folder (.claude/skills/server-feature-dev in scragnog/HOT-Step-CPP) into .agents/skills/server-feature-dev in your project. Codex loads it when a task matches its description.

Can I use Server Feature Dev in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add scragnog/HOT-Step-CPP --skill server-feature-dev -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/server-feature-dev, .gemini/skills/server-feature-dev, .github/skills/server-feature-dev and .opencode/skills/server-feature-dev in your project.

What does Server Feature Dev need to run?

Going by SKILL.md and its folder, Server Feature Dev needs the command-line tools its instructions call (npx, npm, git, tsx and cmake). Our summary lists: Node.js.

Does Server Feature Dev access the network?

SKILL.md contains no URLs. Its commands use npx, npm and git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Server Feature Dev safe to install?

Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.

What licence does Server Feature Dev use?

Server Feature Dev is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Server Feature Dev use?

About 6.5k tokens (SKILL.md is roughly 26k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Server Feature Dev?

Skills that share tags, products or a category with Server Feature Dev: Developing Software (telagod/code-abyss, 244 stars), React Email (viclafouch/meme-studio, 110 stars), Nodejs Express (cohen-liel/hivemind, 110 stars) and Implementing MCP Tools (PostHog/posthog-foss, 721 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Server Feature Dev?

scragnog (a GitHub user) maintains it in scragnog/HOT-Step-CPP, which has 170 GitHub stars. The repository holds 18 skills in this directory. The repository was last updated on October 5, 2026.

Source: scragnog/HOT-Step-CPP on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.