[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6HY9SCY27BW94W7ASATVRG5":3,"skill-same:01M6HY9SCY27BW94W7ASATVRG5":71},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":46,"repositoryHtmlUrl":47,"repositoryHomepageUrl":6,"ownerHtmlUrl":48,"ownerBlogUrl":49,"ownerDescription":6,"updatedAt":50,"install":51,"resources":64},"01M6HY9SCY27BW94W7ASATVRG5","vanilla-ts-http-route-handler",null,"How to correctly add route handlers in a vanilla TypeScript project that uses http.createServer with a manual switch\u002Fcase router — without introducing Express Router or Next.js patterns.","HKUDS\u002FAgentPilot","HKUDS","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F118165258?v=4","01M6HW8VRNZP64G0P4MVJW2KKQ","✨Data Intelligence Lab@HKU✨","01M6HW8VWZVHBRHYH3T330PFWV",7996,"MIT","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fvanilla-ts-http-route-handler\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"http","vanilla ts http route handler","route","how","js","router","switch","patterns","Next.js","next","project","express","how to","add","case","with","manual","route handlers","typescript","2026-11-10 10:12:30",false,"---\nname: vanilla-ts-http-route-handler\ndescription: How to correctly add route handlers in a vanilla TypeScript project that uses http.createServer with a manual switch\u002Fcase router — without introducing Express Router or Next.js patterns.\n---\n\n# Adding Route Handlers to a Vanilla TypeScript HTTP Server\n\nUse this skill whenever you need to add a new route to a TypeScript project\nthat uses Node's built-in `http` module and a `switch\u002Fcase` pathname router\n**without Express or Next.js**.\n\n---\n\n## Step 0 — Confirm the routing style\n\nBefore writing any code, inspect `server\u002Findex.ts` (or whichever file starts\nthe HTTP server).  Look for:\n\n```ts\nhttp.createServer((req, res) => {\n  const { pathname } = new URL(req.url!, `http:\u002F\u002F${req.headers.host}`);\n  switch (pathname) {\n    case '\u002Fapi\u002Ffoo': ...\n  }\n});\n```\n\nIf you see this pattern, follow the steps below.  \n**Do NOT use `express.Router`, `next\u002Fserver`, or any framework-specific\nhandler signature** — even if other files in `server\u002Froutes\u002F` happen to\nuse those patterns.\n\n---\n\n## Step 1 — Create the route file\n\nCreate `server\u002Froutes\u002F\u003Cfeature>.ts`.  \nUse **only** the native Node types: `http.IncomingMessage` and\n`http.ServerResponse`.\n\n```ts\n\u002F\u002F server\u002Froutes\u002Fhealth.ts\nimport http from 'http';\n\nexport async function handleHealth(\n  req: http.IncomingMessage,\n  res: http.ServerResponse\n): Promise\u003Cvoid> {\n  \u002F\u002F Parse body for POST\u002FPUT if needed\n  const body = await readBody(req);   \u002F\u002F helper shown in Step 3\n\n  res.writeHead(200, { 'Content-Type': 'application\u002Fjson' });\n  res.end(JSON.stringify({ status: 'ok' }));\n}\n```\n\nRules for the route file:\n- Export the handler as a **named async function** (`handle\u003CFeature>`).\n- Accept exactly `(req: http.IncomingMessage, res: http.ServerResponse)`.\n- Return `Promise\u003Cvoid>`.\n- Do **not** call `next()` or return a `Response` object.\n- Handle errors internally and write an appropriate HTTP status + JSON body.\n\n---\n\n## Step 2 — Import the handler in `server\u002Findex.ts`\n\nAdd a named import at the top of the file alongside any existing imports:\n\n```ts\nimport { handleHealth } from '.\u002Froutes\u002Fhealth';\n```\n\n---\n\n## Step 3 — Add a `case` entry to the switch block\n\nLocate the existing `switch (pathname)` block and add a new `case`:\n\n```ts\nswitch (pathname) {\n  case '\u002Fapi\u002Fhealth':\n    await handleHealth(req, res);\n    break;\n\n  \u002F\u002F … existing cases …\n\n  default:\n    res.writeHead(404, { 'Content-Type': 'application\u002Fjson' });\n    res.end(JSON.stringify({ error: 'Not found' }));\n}\n```\n\n- Match the exact pathname string used by the client.\n- Always `break` (or `return`) after calling the handler.\n- Keep the `default` case last.\n\n---\n\n## Step 4 — Optional: shared body-reading helper\n\nIf multiple routes need to parse a JSON request body, add a small utility\nrather than duplicating the logic:\n\n```ts\n\u002F\u002F server\u002Futils\u002FreadBody.ts\nimport http from 'http';\n\nexport function readBody(req: http.IncomingMessage): Promise\u003Cunknown> {\n  return new Promise((resolve, reject) => {\n    let data = '';\n    req.on('data', (chunk) => (data += chunk));\n    req.on('end', () => {\n      try {\n        resolve(data ? JSON.parse(data) : {});\n      } catch {\n        reject(new Error('Invalid JSON'));\n      }\n    });\n    req.on('error', reject);\n  });\n}\n```\n\nImport and use it inside any route handler that needs it.\n\n---\n\n## Checklist\n\n- [ ] `server\u002Froutes\u002F\u003Cfeature>.ts` exists and exports a named async handler.\n- [ ] Handler signature is `(req: http.IncomingMessage, res: http.ServerResponse): Promise\u003Cvoid>`.\n- [ ] Handler always calls `res.writeHead(...)` and `res.end(...)` on every code path.\n- [ ] Handler is imported in `server\u002Findex.ts`.\n- [ ] A matching `case` entry is present in the `switch (pathname)` block.\n- [ ] No Express \u002F Next.js \u002F Koa types or patterns were introduced.\n\n---\n\n## Anti-patterns to avoid\n\n| ❌ Wrong | ✅ Correct |\n|---|---|\n| `import { Router } from 'express'` | `import http from 'http'` |\n| `export default function handler(req: NextApiRequest, ...)` | `export async function handleFoo(req: http.IncomingMessage, ...)` |\n| `router.get('\u002Ffoo', ...)` | `case '\u002Fapi\u002Ffoo': await handleFoo(req, res); break;` |\n| Returning a value from the handler | Calling `res.end(...)` and returning `void` |","# Adding Route Handlers to a Vanilla TypeScript HTTP Server\n\nUse this skill whenever you need to add a new route to a TypeScript project\nthat uses Node's built-in `http` module and a `switch\u002Fcase` pathname router\n**without Express or Next.js**.\n\n---\n\n## Step 0 — Confirm the routing style\n\nBefore writing any code, inspect `server\u002Findex.ts` (or whichever file starts\nthe HTTP server).  Look for:\n\n```ts\nhttp.createServer((req, res) => {\n  const { pathname } = new URL(req.url!, `http:\u002F\u002F${req.headers.host}`);\n  switch (pathname) {\n    case '\u002Fapi\u002Ffoo': ...\n  }\n});\n```\n\nIf you see this pattern, follow the steps below.  \n**Do NOT use `express.Router`, `next\u002Fserver`, or any framework-specific\nhandler signature** — even if other files in `server\u002Froutes\u002F` happen to\nuse those patterns.\n\n---\n\n## Step 1 — Create the route file\n\nCreate `server\u002Froutes\u002F\u003Cfeature>.ts`.  \nUse **only** the native Node types: `http.IncomingMessage` and\n`http.ServerResponse`.\n\n```ts\n\u002F\u002F server\u002Froutes\u002Fhealth.ts\nimport http from 'http';\n\nexport async function handleHealth(\n  req: http.IncomingMessage,\n  res: http.ServerResponse\n): Promise\u003Cvoid> {\n  \u002F\u002F Parse body for POST\u002FPUT if needed\n  const body = await readBody(req);   \u002F\u002F helper shown in Step 3\n\n  res.writeHead(200, { 'Content-Type': 'application\u002Fjson' });\n  res.end(JSON.stringify({ status: 'ok' }));\n}\n```\n\nRules for the route file:\n- Export the handler as a **named async function** (`handle\u003CFeature>`).\n- Accept exactly `(req: http.IncomingMessage, res: http.ServerResponse)`.\n- Return `Promise\u003Cvoid>`.\n- Do **not** call `next()` or return a `Response` object.\n- Handle errors internally and write an appropriate HTTP status + JSON body.\n\n---\n\n## Step 2 — Import the handler in `server\u002Findex.ts`\n\nAdd a named import at the top of the file alongside any existing imports:\n\n```ts\nimport { handleHealth } from '.\u002Froutes\u002Fhealth';\n```\n\n---\n\n## Step 3 — Add a `case` entry to the switch block\n\nLocate the existing `switch (pathname)` block and add a new `case`:\n\n```ts\nswitch (pathname) {\n  case '\u002Fapi\u002Fhealth':\n    await handleHealth(req, res);\n    break;\n\n  \u002F\u002F … existing cases …\n\n  default:\n    res.writeHead(404, { 'Content-Type': 'application\u002Fjson' });\n    res.end(JSON.stringify({ error: 'Not found' }));\n}\n```\n\n- Match the exact pathname string used by the client.\n- Always `break` (or `return`) after calling the handler.\n- Keep the `default` case last.\n\n---\n\n## Step 4 — Optional: shared body-reading helper\n\nIf multiple routes need to parse a JSON request body, add a small utility\nrather than duplicating the logic:\n\n```ts\n\u002F\u002F server\u002Futils\u002FreadBody.ts\nimport http from 'http';\n\nexport function readBody(req: http.IncomingMessage): Promise\u003Cunknown> {\n  return new Promise((resolve, reject) => {\n    let data = '';\n    req.on('data', (chunk) => (data += chunk));\n    req.on('end', () => {\n      try {\n        resolve(data ? JSON.parse(data) : {});\n      } catch {\n        reject(new Error('Invalid JSON'));\n      }\n    });\n    req.on('error', reject);\n  });\n}\n```\n\nImport and use it inside any route handler that needs it.\n\n---\n\n## Checklist\n\n- [ ] `server\u002Froutes\u002F\u003Cfeature>.ts` exists and exports a named async handler.\n- [ ] Handler signature is `(req: http.IncomingMessage, res: http.ServerResponse): Promise\u003Cvoid>`.\n- [ ] Handler always calls `res.writeHead(...)` and `res.end(...)` on every code path.\n- [ ] Handler is imported in `server\u002Findex.ts`.\n- [ ] A matching `case` entry is present in the `switch (pathname)` block.\n- [ ] No Express \u002F Next.js \u002F Koa types or patterns were introduced.\n\n---\n\n## Anti-patterns to avoid\n\n| ❌ Wrong | ✅ Correct |\n|---|---|\n| `import { Router } from 'express'` | `import http from 'http'` |\n| `export default function handler(req: NextApiRequest, ...)` | `export async function handleFoo(req: http.IncomingMessage, ...)` |\n| `router.get('\u002Ffoo', ...)` | `case '\u002Fapi\u002Ffoo': await handleFoo(req, res); break;` |\n| Returning a value from the handler | Calling `res.end(...)` and returning `void` |",{"name":5,"description":7},"main","6b84b0689ca0d9e7b1e4061188ff6b60758e0f8a","openspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fvanilla-ts-http-route-handler",941,"https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot","https:\u002F\u002Fgithub.com\u002FHKUDS","https:\u002F\u002Fsites.google.com\u002Fview\u002Fchaoh","2026-11-12 22:26:39",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add HKUDS\u002FAgentPilot --skill vanilla-ts-http-route-handler",{"tool":57,"command":58},"bunx","bunx skills add HKUDS\u002FAgentPilot --skill vanilla-ts-http-route-handler",{"tool":60,"command":61},"pnpm","pnpm dlx skills add HKUDS\u002FAgentPilot --skill vanilla-ts-http-route-handler","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fvanilla-ts-http-route-handler\u002FSKILL.md",true,[65],{"path":66,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":69,"sourceHtmlUrl":70},".skill_id","OTHER","其他",43,"https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fvanilla-ts-http-route-handler\u002F.skill_id",[72,91,110,135,159,181],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":78,"publishedAt":38},"01M6HYB297NDABRERJRVG0K8R0","agentpilot","Set up AgentPilot and use its single entry to prepare missing inputs and run configured RSI experiments. Use when the user requests AgentPilot installation or work with AgentPilot.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fplugins\u002Fagentpilot\u002Fskills\u002Fagentpilot\u002FSKILL.md",[],[74,79,80,34,81,82,83,84,85,86,87,88,89,90],"prepare","requests","missing","installation","rsi","up","use","user","work","SET","run","experiments",{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":38},"01M6HYAYWXTTE1J7DJM0MFNQ4H","remember","Review auto-memory entries and propose promotions to OPENSPACE.md, OPENSPACE.local.md, or retained auto-memory topics; detect duplicates, stale facts, conflicts, and ambiguous entries before making changes.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fskills\u002Fremember\u002FSKILL.md",[],[93,98,99,100,101,102,103,104,105,106,107,108,109],"memory","review","auto","openspace","changes","facts","detect","entries","local","conflicts","stale","duplicates",{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":121,"publishedAt":38},"01M6HYAVMPM4SBDV60R2B5HPV8","skill-discovery","Search reusable OpenSpace skills locally, and browse cloud packages step by step when a cloud skill may help. Reusing proven skills saves tokens, improves reliability, and extends your capabilities beyond built-in tools.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fhost_skills\u002Fskill-discovery\u002FSKILL.md",[116],{"categoryNo":117,"categoryName":118,"taxonomyType":119,"taxonomyTypeDesc":120},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[122,123,124,101,125,126,127,128,129,130,131,132,133,134],"skill discovery","discovery","reusable","step by step","tokens","packages","help","cloud","reliability","capabilities","browse","search","Tools",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":142,"publishedAt":38},"01M6HYAR811B3RB0M50RBEDGN6","delegate-task","Delegate tasks to OpenSpace — a full-stack autonomous worker for coding, DevOps, web research, and desktop automation, backed by an extensive MCP tool and skill library. Skills auto-improve through use, reducing token consumption over time. A cloud community lets agents share and collectively evolve reusable skills.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Fopenspace\u002Fhost_skills\u002Fdelegate-task\u002FSKILL.md",[141],{"categoryNo":117,"categoryName":118,"taxonomyType":119,"taxonomyTypeDesc":120},[143,144,145,101,146,147,148,149,150,124,100,85,151,152,153,154,155,156,157,158],"delegate","task","delegate task","time","improve","desktop automation","worker","community","autonomous","tool","stack","for","share","tasks","automation","MCP",{"skillNo":160,"name":161,"nameZh":6,"description":162,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":163,"categories":164,"keywords":165,"publishedAt":38},"01M6HYAM9FSGV6E52AYENDQXYH","write-special-chars-ts-check","Workaround for write_file failures caused by special characters (apostrophes, backticks, template literals) using a Python heredoc script, plus a reliable TypeScript type-checking step that filters output to only the newly-written files to avoid being misled by pre-existing errors.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fwrite-special-chars-ts-check\u002FSKILL.md",[],[166,167,168,169,170,171,172,173,174,175,37,176,177,178,154,179,180],"write special chars ts check","write","check","type","files","filters","only","template","file","python","output","Existing","type-checking","errors","script",{"skillNo":182,"name":183,"nameZh":6,"description":184,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":185,"categories":186,"keywords":187,"publishedAt":38},"01M6HYAH05HGV7SVWSVR8PJRQD","write-special-chars-readback","Workaround for write_file failures caused by special characters (apostrophes, backticks, template literals) using a Python heredoc script, with mandatory post-write verification via read_file and cleanup of \u002Ftmp helper scripts.","https:\u002F\u002Fgithub.com\u002FHKUDS\u002FAgentPilot\u002Fblob\u002Fmain\u002Fopenspace\u002Flegacy\u002Fv1\u002Fshowcase\u002Fskills\u002Fwrite-special-chars-readback\u002FSKILL.md",[],[188,189,167,34,154,180,190,191,192,193,194,173,174,195,196,175],"write special chars readback","readback","helper","post","via","scripts","read","verification","cleanup"]