@rebasepro/cli 0.25.0-canary.ge06257e → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/bin/rebase.js +9 -5
  2. package/dist/{agent-setup-C83DXnlx.js → agent-setup-B5AKHOgt.js} +2 -2
  3. package/dist/{agent-setup-C83DXnlx.js.map → agent-setup-B5AKHOgt.js.map} +1 -1
  4. package/dist/{api-keys-Bc4FJKYp.js → api-keys-Tm7zvAPe.js} +27 -20
  5. package/dist/api-keys-Tm7zvAPe.js.map +1 -0
  6. package/dist/{apps-wG-ZTryA.js → apps-BsU_ILbP.js} +4 -4
  7. package/dist/{apps-wG-ZTryA.js.map → apps-BsU_ILbP.js.map} +1 -1
  8. package/dist/{args-CcBphFdj.js → args-BX0YD14a.js} +45 -16
  9. package/dist/args-BX0YD14a.js.map +1 -0
  10. package/dist/{atlas-dev-database-DeK3Sa4h.js → atlas-dev-database-OWtqnG7W.js} +2 -2
  11. package/dist/{atlas-dev-database-DeK3Sa4h.js.map → atlas-dev-database-OWtqnG7W.js.map} +1 -1
  12. package/dist/{auth-BL7bNQVu.js → auth-Vl2hFKXP.js} +77 -10
  13. package/dist/auth-Vl2hFKXP.js.map +1 -0
  14. package/dist/{branch-pointer-DTQNLFtb.js → branch-pointer-9DnwkFJJ.js} +1 -1
  15. package/dist/{build-h_7Re-67.js → build-BBgT8EJZ.js} +5 -5
  16. package/dist/{build-h_7Re-67.js.map → build-BBgT8EJZ.js.map} +1 -1
  17. package/dist/{bundle-CqURVN-K.js → bundle-DtCCCr4w.js} +4 -19
  18. package/dist/bundle-DtCCCr4w.js.map +1 -0
  19. package/dist/{cloud-DVV_5GPG.js → cloud-BpH0_bmV.js} +7 -7
  20. package/dist/{cloud-DVV_5GPG.js.map → cloud-BpH0_bmV.js.map} +1 -1
  21. package/dist/commands/api-keys.d.ts +4 -1
  22. package/dist/commands/auth.d.ts +3 -2
  23. package/dist/connection-banner-DbLJzWf4.js +128 -0
  24. package/dist/connection-banner-DbLJzWf4.js.map +1 -0
  25. package/dist/{console-Dw3HLdUs.js → console-CEGm0XNc.js} +4 -4
  26. package/dist/{console-Dw3HLdUs.js.map → console-CEGm0XNc.js.map} +1 -1
  27. package/dist/{context-BM-oOMNX.js → context-CyflUrKg.js} +2 -2
  28. package/dist/{context-BM-oOMNX.js.map → context-CyflUrKg.js.map} +1 -1
  29. package/dist/{daemon-entry-C6pxdily.js → daemon-entry-CbLRrgzy.js} +199 -3
  30. package/dist/daemon-entry-CbLRrgzy.js.map +1 -0
  31. package/dist/{db-Dkn9XhT-.js → db-CRNpA5mH.js} +171 -60
  32. package/dist/db-CRNpA5mH.js.map +1 -0
  33. package/dist/{derive-Dl0z2OWi.js → derive-CsoFGAnK.js} +28 -13
  34. package/dist/{derive-Dl0z2OWi.js.map → derive-CsoFGAnK.js.map} +1 -1
  35. package/dist/{dev-DAcZR4Pp.js → dev-C0pbMmhS.js} +6 -6
  36. package/dist/{dev-DAcZR4Pp.js.map → dev-C0pbMmhS.js.map} +1 -1
  37. package/dist/dev-db/connection-banner.d.ts +47 -0
  38. package/dist/dev-db/notification-proxy.d.ts +82 -1
  39. package/dist/{dev-preflight-CorMg7Ub.js → dev-preflight-unHpiYO1.js} +26 -3
  40. package/dist/dev-preflight-unHpiYO1.js.map +1 -0
  41. package/dist/{doctor-Ds_lzc9p.js → doctor-CATRdWm6.js} +4 -4
  42. package/dist/{doctor-Ds_lzc9p.js.map → doctor-CATRdWm6.js.map} +1 -1
  43. package/dist/{eject-CJHkfnat.js → eject-DuasTr-F.js} +2 -2
  44. package/dist/{eject-CJHkfnat.js.map → eject-DuasTr-F.js.map} +1 -1
  45. package/dist/{fold-static-BC9CYicw.js → fold-static-hDHtDfTq.js} +2 -2
  46. package/dist/{fold-static-BC9CYicw.js.map → fold-static-hDHtDfTq.js.map} +1 -1
  47. package/dist/{generate_sdk-kfkZmG-d.js → generate_sdk-Cvg5jjJF.js} +3 -3
  48. package/dist/{generate_sdk-kfkZmG-d.js.map → generate_sdk-Cvg5jjJF.js.map} +1 -1
  49. package/dist/index.es.js +34 -34
  50. package/dist/{init-CjoJoThg.js → init-BWAwHPtz.js} +8 -7
  51. package/dist/init-BWAwHPtz.js.map +1 -0
  52. package/dist/last-dev-database-c_dKz1S3.js +2 -0
  53. package/dist/{normalize-imports-BHxL2d05.js → normalize-imports-BoBVh5tU.js} +3 -3
  54. package/dist/{normalize-imports-BHxL2d05.js.map → normalize-imports-BoBVh5tU.js.map} +1 -1
  55. package/dist/precompress.d.ts +0 -7
  56. package/dist/{prepare-C1iaiTC4.js → prepare-DdnU70z8.js} +4 -4
  57. package/dist/prepare-DdnU70z8.js.map +1 -0
  58. package/dist/{pull-COpNJFzY.js → pull-C3PXduzl.js} +13 -40
  59. package/dist/pull-C3PXduzl.js.map +1 -0
  60. package/dist/resolve-CNQjsatH.js +2 -0
  61. package/dist/resources/derive.d.ts +9 -0
  62. package/dist/{resources-CLm6g0br.js → resources-BdNRDEAy.js} +3 -3
  63. package/dist/{resources-CLm6g0br.js.map → resources-BdNRDEAy.js.map} +1 -1
  64. package/dist/{schema-BKdh6UoE.js → schema-B0-p6T1O.js} +4 -4
  65. package/dist/{schema-BKdh6UoE.js.map → schema-B0-p6T1O.js.map} +1 -1
  66. package/dist/{skills-DEhYiPeO.js → skills-DfnTeyRz.js} +3 -3
  67. package/dist/{skills-DEhYiPeO.js.map → skills-DfnTeyRz.js.map} +1 -1
  68. package/dist/{start-BsPJedPQ.js → start-BmEZkS0n.js} +4 -4
  69. package/dist/{start-BsPJedPQ.js.map → start-BmEZkS0n.js.map} +1 -1
  70. package/dist/{status-D_B3tlDy.js → status-BJ26yZOo.js} +9 -9
  71. package/dist/{status-D_B3tlDy.js.map → status-BJ26yZOo.js.map} +1 -1
  72. package/dist/{storage-CtB8polS.js → storage-DKPUBTWG.js} +3 -3
  73. package/dist/{storage-CtB8polS.js.map → storage-DKPUBTWG.js.map} +1 -1
  74. package/dist/{telemetry-BYmPU-Ji.js → telemetry-m9czTNGR.js} +2 -2
  75. package/dist/{telemetry-BYmPU-Ji.js.map → telemetry-m9czTNGR.js.map} +1 -1
  76. package/dist/{upgrade-DbuKX9lv.js → upgrade-DjihWuZL.js} +5 -4
  77. package/dist/{upgrade-DbuKX9lv.js.map → upgrade-DjihWuZL.js.map} +1 -1
  78. package/dist/utils/args.d.ts +13 -0
  79. package/dist/utils/redact.d.ts +10 -0
  80. package/dist/utils/refusal.d.ts +18 -0
  81. package/package.json +7 -7
  82. package/templates/overlays/baas/README.md +2 -1
  83. package/templates/template/.env.example +6 -0
  84. package/templates/template/frontend/vite.config.ts +7 -73
  85. package/dist/api-keys-Bc4FJKYp.js.map +0 -1
  86. package/dist/args-CcBphFdj.js.map +0 -1
  87. package/dist/auth-BL7bNQVu.js.map +0 -1
  88. package/dist/bundle-CqURVN-K.js.map +0 -1
  89. package/dist/daemon-entry-C6pxdily.js.map +0 -1
  90. package/dist/db-Dkn9XhT-.js.map +0 -1
  91. package/dist/dev-preflight-CorMg7Ub.js.map +0 -1
  92. package/dist/init-CjoJoThg.js.map +0 -1
  93. package/dist/last-dev-database-BEBPfAMY.js +0 -2
  94. package/dist/prepare-C1iaiTC4.js.map +0 -1
  95. package/dist/pull-COpNJFzY.js.map +0 -1
  96. package/dist/resolve-b_p9bmdH.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"skills-DEhYiPeO.js","names":[],"sources":["../src/commands/skills.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { createHash } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport inquirer from \"inquirer\";\nimport { createRequire } from \"module\";\nimport os from \"os\";\nimport { findProjectRoot } from \"../utils/project\";\nimport { parseCommandArgs, UsageError, wantsHelp } from \"../utils/args\";\nimport { unknownCommand } from \"../utils/unknown-command\";\n\nconst require = createRequire(import.meta.url);\n\n/**\n * Supported agent environments and their target directories.\n *\n * Every agent `rebase init` writes a pointer file for has an entry here. That\n * was not true: `rebase init` writes `CLAUDE.md`, `AGENTS.md`, `.cursorrules`,\n * `.windsurfrules` and `.github/copilot-instructions.md`, and the installer\n * covered four of the five — so a Codex or Copilot user was told, by a file in\n * their own repository, to run an installer that had nothing to offer them.\n *\n * `flatLayout` marks the agents whose rules directory is loaded *whole*, into\n * every request. Writing 21 skills there put ~84,000 characters of always-on\n * context in front of every question a person asked Cursor — more than the task\n * itself, on every turn, most of it about parts of Rebase they were not\n * touching. Those agents get one short index rule instead, and the skill bodies\n * go in per-skill subdirectories the index names, which the agent reads when it\n * needs one. `indexFile` is the name of that rule.\n *\n * A subdirectory layout has no such problem: the rule file already sits beside\n * its own assets, so `references/x.md` resolves as written and nothing is\n * loaded until it is opened.\n *\n * `homeMarkers` are the directories under `~` that say the assistant is\n * installed on this machine. They answer a different question from\n * `detectDir`: a fresh scaffold has no `.claude/` of its own, so `rebase init`\n * asks the machine which assistants the developer has and pre-selects those.\n *\n * `mcp` is where the assistant reads a *project-level* MCP server list, and in\n * which shape — see `agent-setup.ts`. `projectDir` is what `REBASE_PROJECT_DIR`\n * is set to: `\".\"` where the server is spawned in the project, the editor's own\n * workspace variable where it may not be. `null` means there is no project\n * file to write: Windsurf reads only a user-level config, and one entry there\n * cannot name every project on the machine.\n */\nexport const AGENTS = {\n cursor: {\n label: \"Cursor\",\n detectDir: \".cursor\",\n homeMarkers: [\".cursor\"],\n targetDir: \".cursor/rules\",\n flatLayout: true,\n indexFile: \"rebase.mdc\",\n mcp: {\n file: \".cursor/mcp.json\",\n format: \"mcpServers\",\n projectDir: \"${workspaceFolder}\"\n },\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n claude: {\n label: \"Claude Code\",\n detectDir: \".claude\",\n homeMarkers: [\".claude\"],\n targetDir: \".claude/skills\",\n flatLayout: false,\n // The scaffold already ships this file; writing it is then a no-op.\n mcp: {\n file: \".mcp.json\",\n format: \"mcpServers\",\n projectDir: \".\"\n },\n /** Claude Code uses the standard SKILL.md format in subdirectories. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n windsurf: {\n label: \"Windsurf\",\n detectDir: \".windsurf\",\n homeMarkers: [\".codeium/windsurf\", \".windsurf\"],\n targetDir: \".windsurf/rules\",\n flatLayout: true,\n indexFile: \"rebase.md\",\n mcp: null,\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n gemini: {\n label: \"Gemini CLI / Antigravity\",\n detectDir: \".agents\",\n homeMarkers: [\".gemini\"],\n targetDir: \".agents/skills\",\n flatLayout: false,\n mcp: {\n file: \".gemini/settings.json\",\n format: \"mcpServers\",\n projectDir: \".\"\n },\n /** Gemini uses the standard SKILL.md format in subdirectories. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n codex: {\n label: \"Codex CLI\",\n detectDir: \".codex\",\n homeMarkers: [\".codex\"],\n // Codex reads repository skills from `.agents/skills` — every directory\n // from the working directory up to the repository root — and nothing\n // under `.codex/`. This entry used to write `.codex/skills`, so `--agent\n // codex` reported 21 skills installed into a directory Codex never\n // opens. It shares the directory with Gemini; the installer writes it\n // once.\n targetDir: \".agents/skills\",\n flatLayout: false,\n // Project-scoped, and read only once the developer trusts the project.\n mcp: {\n file: \".codex/config.toml\",\n format: \"codexToml\",\n projectDir: \".\"\n },\n /** Codex uses the standard SKILL.md format in subdirectories. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n kiro: {\n label: \"Kiro\",\n detectDir: \".kiro\",\n homeMarkers: [\".kiro\"],\n targetDir: \".kiro/steering\",\n flatLayout: true,\n indexFile: \"rebase.md\",\n mcp: {\n file: \".kiro/settings/mcp.json\",\n format: \"mcpServers\",\n projectDir: \".\"\n },\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n copilot: {\n label: \"GitHub Copilot\",\n // Deliberately undetectable. `.github/` is the one directory in this\n // table that is not evidence of an assistant: `rebase init` writes\n // `.github/copilot-instructions.md` into every scaffold, and half the\n // repositories on the internet have a `.github/` for workflows and\n // issue templates. Listing it made bare `rebase skills install` resolve\n // to `[copilot]` in every project — so a Claude Code user who ran the\n // command `rebase init` printed got a success message and nothing in\n // `.claude/skills`. Copilot is `--agent copilot`.\n detectDir: null,\n // Same reasoning on the machine: Copilot lives inside VS Code or a\n // JetBrains IDE, and neither's directory says the extension is there.\n homeMarkers: [],\n targetDir: \".github/instructions\",\n flatLayout: true,\n indexFile: \"rebase.instructions.md\",\n mcp: {\n file: \".vscode/mcp.json\",\n format: \"vscode\",\n projectDir: \"${workspaceFolder}\"\n },\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n }\n} as const;\n\nexport type AgentKey = keyof typeof AGENTS;\n\n/** Narrows a string to an agent name, by the table's own keys. */\nexport function isAgentKey(value: string): value is AgentKey {\n return Object.hasOwn(AGENTS, value);\n}\n\n/** Every agent, in table order. */\nexport const AGENT_KEYS: AgentKey[] = Object.keys(AGENTS).filter(isAgentKey);\n\n/**\n * Resolve the path to the skills directory from @rebasepro/agent-skills.\n * Works in both workspace (symlink) and published (real files) layouts.\n */\nfunction getSkillsSourceDir(): string {\n const pkgJsonPath = require.resolve(\"@rebasepro/agent-skills/package.json\");\n const pkgRoot = path.dirname(pkgJsonPath);\n const skillsDir = path.join(pkgRoot, \"skills\");\n\n if (!fs.existsSync(skillsDir)) {\n throw new Error(\n `Skills directory not found at ${skillsDir}. ` +\n \"Make sure @rebasepro/agent-skills is installed.\"\n );\n }\n\n return skillsDir;\n}\n\nexport interface LoadedSkill {\n name: string;\n /** Absolute path of the skill's source directory. */\n dir: string;\n content: string;\n /** Every file the skill ships besides SKILL.md, relative to `dir`. */\n assets: string[];\n}\n\n/**\n * Everything a skill ships alongside its SKILL.md — the `references/` tree the\n * Agent Skills format uses for progressive disclosure.\n *\n * These used to be dropped on install, because the installer read exactly\n * `<skill>/SKILL.md` and nothing else. That left `rebase-design-language`\n * telling the agent three separate times to read `references/view-patterns.md`\n * — 379 lines of view skeletons — in a project where the file had never\n * landed, and the instruction it carries is \"extend an existing pattern; do not\n * invent a layout\".\n */\nfunction loadSkillAssets(skillDir: string): string[] {\n const found: string[] = [];\n\n const walk = (dir: string, prefix: string) => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n // Dotfiles are bookkeeping — `.gitkeep` is what holds the empty\n // `references/` directories in git — and install nothing.\n if (entry.name.startsWith(\".\")) continue;\n const rel = prefix ? path.join(prefix, entry.name) : entry.name;\n if (entry.isDirectory()) walk(path.join(dir, entry.name), rel);\n else if (rel !== \"SKILL.md\") found.push(rel);\n }\n };\n\n walk(skillDir, \"\");\n return found.sort();\n}\n\n/** Read all skill directories and return their names, content and assets. */\nexport function loadSkills(skillsDir: string): LoadedSkill[] {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true });\n const skills: LoadedSkill[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillDir = path.join(skillsDir, entry.name);\n const skillMdPath = path.join(skillDir, \"SKILL.md\");\n if (!fs.existsSync(skillMdPath)) continue;\n skills.push({\n name: entry.name,\n dir: skillDir,\n content: fs.readFileSync(skillMdPath, \"utf-8\"),\n assets: loadSkillAssets(skillDir)\n });\n }\n\n return skills;\n}\n\n/**\n * The skills this CLI ships, from its own `@rebasepro/agent-skills`.\n *\n * Throws rather than exiting, so `rebase init` can report a missing bundle as a\n * warning and still finish the scaffold.\n */\nexport function loadBundledSkills(): LoadedSkill[] {\n const skillsDir = getSkillsSourceDir();\n const skills = loadSkills(skillsDir);\n if (skills.length === 0) throw new Error(`No skills found in ${skillsDir}`);\n return skills;\n}\n\n/**\n * The one always-on rule a flat-layout agent gets, in place of every skill.\n *\n * Cursor and Windsurf load their whole rules directory into each request. With\n * a rule file per skill that was ~84,000 characters of Rebase reference in\n * front of every question, whether or not the question was about Rebase — and\n * an instruction an assistant skims is an instruction it does not follow.\n *\n * So the always-on part is this: a table of what exists and where to read it.\n * `alwaysApply: true` because knowing the skills are there costs a few hundred\n * characters and is the whole point; the bodies stay on disk until the agent\n * opens one.\n */\nexport function renderSkillIndex(skills: LoadedSkill[], agentKey: AgentKey): string {\n const agent = AGENTS[agentKey];\n const bodyPath = (name: string) => agent.transformFile(name, \"\").fileName.split(path.sep).join(\"/\");\n const rows = skills\n .map((skill) => `| \\`${skill.name}\\` | ${describe(skill)} | \\`${bodyPath(skill.name)}\\` |`)\n .join(\"\\n\");\n\n return [\n \"---\",\n \"description: Rebase — the skills index. Read the file named for the task before writing Rebase code.\",\n \"alwaysApply: true\",\n \"---\",\n \"\",\n \"# Rebase skills\",\n \"\",\n `This project uses [Rebase](https://rebase.pro). ${skills.length} reference skills are`,\n \"installed beside this file. **Read the one that covers the task before writing\",\n \"code** — they are the difference between a collection that compiles and one that\",\n \"does not.\",\n \"\",\n \"| Skill | Covers | Read |\",\n \"|---|---|---|\",\n rows,\n \"\",\n \"Start with `rebase-basics` if you are not sure: it opens with the recipes for\",\n \"adding a collection, a function and an RLS rule, and names the commands.\",\n \"\"\n ].join(\"\\n\");\n}\n\n/**\n * The body of a YAML block scalar, given the offset of its `description:` line.\n *\n * Everything indented more than the key belongs to the value; the first line\n * that is not blank and not indented ends it.\n */\nfunction blockScalarBody(frontMatter: string, keyOffset: number): string {\n const lines = frontMatter.slice(keyOffset).split(\"\\n\").slice(1);\n const body: string[] = [];\n for (const line of lines) {\n if (line.trim() === \"\") { body.push(\"\"); continue; }\n if (!/^[ \\t]/.test(line)) break;\n body.push(line.trim());\n }\n return body.join(\" \");\n}\n\n/**\n * A skill's one-line summary, from its own front matter.\n *\n * Read rather than written here so a new skill needs no edit in this file, and\n * truncated because the index is only worth having while it stays short.\n */\nfunction describe(skill: LoadedSkill): string {\n const frontMatter = skill.content.split(\"---\")[1] ?? \"\";\n const described = /^description:[ \\t]*(.*)$/m.exec(frontMatter);\n // `description: |` (or `>`) is a YAML block scalar: the value is the\n // indented lines that follow, and capturing the line itself captured the\n // `|`. That rendered an empty \"Covers\" cell for `rebase-design-language`,\n // the one skill whose description says agents MUST read it.\n const raw = /^[|>][-+]?$/.test((described?.[1] ?? \"\").trim())\n ? blockScalarBody(frontMatter, described?.index ?? 0)\n : (described?.[1] ?? \"\");\n const text = raw.trim().replace(/\\s+/g, \" \").replace(/\\|/g, \"\\\\|\");\n const firstSentence = /^.*?\\.(?:\\s|$)/.exec(text)?.[0]?.trim() ?? text;\n const fallback = skill.name.replace(/^rebase-/, \"\").replace(/-/g, \" \");\n if (!firstSentence) return fallback;\n if (firstSentence.length <= 140) return firstSentence;\n // Cut at a word boundary. A description sliced mid-word (\"using the collect\")\n // reads as a corrupted file rather than as a summary.\n const cut = firstSentence.slice(0, 140);\n return `${cut.slice(0, cut.lastIndexOf(\" \")).replace(/[,;:]$/, \"\")}…`;\n}\n\n/**\n * Detect which agent environments already exist in the project.\n *\n * A `detectDir` has to be evidence *the user* created, not something a scaffold\n * ships. An agent whose only marker fails that test declares `detectDir: null`\n * and is reachable by `--agent` alone; guessing wrong here is worse than not\n * guessing, because the wrong guess installs 21 files and reports success.\n */\nexport function detectAgents(projectDir: string): AgentKey[] {\n return AGENT_KEYS.filter((key) => {\n const { detectDir } = AGENTS[key];\n return detectDir !== null && fs.existsSync(path.join(projectDir, detectDir));\n });\n}\n\n/** What `detectInstalledAgents` found for one assistant. */\nexport interface InstalledAgent {\n key: AgentKey;\n label: string;\n /** The home directory checked, as `~/…` — the one found, else the first. */\n marker: string | null;\n found: boolean;\n}\n\n/**\n * Which assistants are installed on this machine, from their home directories.\n *\n * The question `rebase init` needs answered, and one `detectAgents` cannot: a\n * project that does not exist yet has no `.claude/` in it. What the machine has\n * is only a default — the developer confirms it — so a false positive costs an\n * unticked box, not 21 files nobody asked for.\n */\nexport function detectInstalledAgents(home: string = os.homedir()): InstalledAgent[] {\n return AGENT_KEYS.map((key) => {\n const agent = AGENTS[key];\n const markers: readonly string[] = agent.homeMarkers;\n const hit = markers.find((m) => fs.existsSync(path.join(home, m)));\n const shown = hit ?? markers[0];\n return {\n key,\n label: agent.label,\n marker: shown === undefined ? null : `~/${shown}`,\n found: hit !== undefined\n };\n });\n}\n\n/** Install skills for a specific agent into the project directory. */\n/**\n * The file in each target directory that records what the installer wrote:\n * relative path → sha256 of the bytes written. A dotfile, so no agent loads it\n * as a rule.\n */\nexport const SKILLS_MANIFEST = \".rebase-skills.json\";\n\ninterface SkillsManifest {\n version: 1;\n files: Record<string, string>;\n}\n\nfunction sha256(bytes: Buffer | string): string {\n return createHash(\"sha256\").update(bytes).digest(\"hex\");\n}\n\nfunction readManifest(targetBase: string): SkillsManifest | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(path.join(targetBase, SKILLS_MANIFEST), \"utf-8\"));\n if (parsed && typeof parsed === \"object\" && \"files\" in parsed) {\n const files = (parsed as { files: unknown }).files;\n if (files && typeof files === \"object\") {\n const entries = Object.entries(files).filter((e): e is [string, string] => typeof e[1] === \"string\");\n return { version: 1, files: Object.fromEntries(entries) };\n }\n }\n } catch {\n // No manifest, or one that is not ours: the first install, or one made\n // before manifests existed.\n }\n return null;\n}\n\n/** What one agent's install did. */\nexport interface AgentInstallResult {\n skills: number;\n assets: number;\n /** Files left as they are because they were edited since the last install (relative to the target dir). */\n kept: string[];\n /** Files of skills the bundle no longer ships, removed because nobody edited them. */\n removed: string[];\n}\n\nexport function installForAgent(\n agentKey: AgentKey,\n skills: LoadedSkill[],\n projectDir: string,\n options: { force?: boolean } = {}\n): AgentInstallResult {\n const agent = AGENTS[agentKey];\n const targetBase = path.join(projectDir, agent.targetDir);\n\n // Ensure the target directory exists\n fs.mkdirSync(targetBase, { recursive: true });\n\n // A re-run used to overwrite every file unconditionally and never remove\n // one: a note a team appended to an installed skill and committed — as\n // the command tells them to — vanished on the next `skills install`, and a\n // skill a release retired stayed installed beside its replacement. The\n // manifest records what was written, so an edit is told apart from an\n // older bundle's copy. A file with no record (an install from before\n // manifests) is overwritten, as before.\n const previous = readManifest(targetBase);\n const recorded: Record<string, string> = {};\n const kept: string[] = [];\n\n const place = (relPath: string, bytes: Buffer | string) => {\n const target = path.join(targetBase, relPath);\n const wanted = sha256(bytes);\n const last = previous?.files[relPath];\n if (fs.existsSync(target)) {\n const current = sha256(fs.readFileSync(target));\n if (current === wanted) {\n recorded[relPath] = wanted;\n return;\n }\n if (last !== undefined && current !== last && !options.force) {\n // Edited since it was installed: left alone, and still recorded\n // as what was installed, so the next run says so again.\n kept.push(relPath);\n recorded[relPath] = last;\n return;\n }\n }\n fs.mkdirSync(path.dirname(target), { recursive: true });\n fs.writeFileSync(target, bytes);\n recorded[relPath] = wanted;\n };\n\n let count = 0;\n let assetCount = 0;\n for (const skill of skills) {\n // Every layout now writes the body into a per-skill subdirectory, so a\n // link the skill spells `references/x.md` resolves as written and no\n // rewriting is needed. What differs is whether that file is also the\n // agent's *rule* file (subdirectory layouts) or something the index\n // points at (flat ones).\n const { fileName, content } = agent.transformFile(skill.name, skill.content);\n place(fileName, content);\n count++;\n\n // A skill's assets always land in a directory named after the skill,\n // whatever the rule file's layout, so two skills' `references/` trees\n // cannot collide in a flat target directory.\n for (const asset of skill.assets) {\n place(path.join(skill.name, asset), fs.readFileSync(path.join(skill.dir, asset)));\n assetCount++;\n }\n }\n\n if (agent.flatLayout && agent.indexFile) {\n place(agent.indexFile, renderSkillIndex(skills, agentKey));\n }\n\n // Retired files: removed when they are exactly what was installed, kept\n // (and reported) when someone edited them.\n const removed: string[] = [];\n for (const [relPath, last] of Object.entries(previous?.files ?? {})) {\n if (relPath in recorded) continue;\n const target = path.join(targetBase, relPath);\n if (!fs.existsSync(target)) continue;\n if (sha256(fs.readFileSync(target)) !== last && !options.force) {\n kept.push(relPath);\n continue;\n }\n fs.rmSync(target);\n removed.push(relPath);\n // Empty directories the removal leaves behind, up to the target dir.\n for (let dir = path.dirname(target); dir.startsWith(targetBase + path.sep); dir = path.dirname(dir)) {\n if (fs.readdirSync(dir).length > 0) break;\n fs.rmdirSync(dir);\n }\n }\n\n fs.writeFileSync(\n path.join(targetBase, SKILLS_MANIFEST),\n JSON.stringify({ version: 1, files: recorded } satisfies SkillsManifest, null, 2) + \"\\n\",\n \"utf-8\"\n );\n\n return { skills: count, assets: assetCount, kept, removed };\n}\n\n/** What `installSkills` did for one agent. */\nexport interface SkillsInstallResult extends AgentInstallResult {\n agent: AgentKey;\n /** An earlier agent in the same run that already wrote this directory. */\n sharedWith?: AgentKey;\n}\n\n/**\n * Install for several agents, writing each target directory once.\n *\n * Gemini and Codex both read `.agents/skills`; installing twice would write the\n * same 21 files twice and report two installs of one directory.\n */\nexport function installSkills(\n agents: readonly AgentKey[],\n skills: LoadedSkill[],\n projectDir: string,\n options: { force?: boolean } = {}\n): SkillsInstallResult[] {\n const written = new Map<string, SkillsInstallResult>();\n return agents.map((agent) => {\n const earlier = written.get(AGENTS[agent].targetDir);\n if (earlier) return { ...earlier, agent, sharedWith: earlier.agent };\n const result = { agent, ...installForAgent(agent, skills, projectDir, options) };\n written.set(AGENTS[agent].targetDir, result);\n return result;\n });\n}\n\n/** Everything the switch below dispatches, for the did-you-mean. */\nexport const SKILLS_SUBCOMMANDS = [\"install\"] as const;\n\nexport async function skillsCommand(subcommand: string | undefined, rawArgs: string[]) {\n // `--help` cannot reach `skillsInstall`. `cli.ts` only rewrites the\n // subcommand to `\"--help\"` when none was named, so `rebase skills install\n // --help` used to run the install: it detected the agents and overwrote\n // `.claude/skills/*/SKILL.md`, `.cursor/rules/*.mdc` and the rest. A flag\n // that prints text must not write files.\n if (wantsHelp(rawArgs)) {\n printSkillsHelp();\n return;\n }\n\n switch (subcommand) {\n case \"install\":\n await skillsInstall(rawArgs);\n break;\n case \"--help\":\n case undefined:\n printSkillsHelp();\n break;\n default:\n unknownCommand(subcommand, SKILLS_SUBCOMMANDS, \"skills\");\n }\n}\n\n/**\n * Agents named explicitly on the command line, e.g. `--agent claude --agent cursor`\n * (also accepts a comma-separated list, and `all`). Returns null when none were\n * given.\n */\nexport function parseAgentFlags(rawArgs: string[]): AgentKey[] | null {\n return parseSkillsInstallLine(rawArgs).agents;\n}\n\n/** `rebase skills install`'s whole line: the agents it names, `--mcp` and `--force`. */\nexport function parseSkillsInstallLine(rawArgs: string[]): { agents: AgentKey[] | null; mcp: boolean; force: boolean } {\n // Strict, and before anything is written. This used to scan `rawArgs` for\n // `--agent` and ignore every other token, so `rebase skills install\n // --frobnicate --agent claude` exited 0 after writing 21 files — the CLI's\n // other families reject an unknown flag, and an installer is the last place\n // to guess what the person meant.\n const { flags } = parseCommandArgs({\n spec: { \"--agent\": [String], \"-a\": \"--agent\", \"--mcp\": Boolean, \"--force\": Boolean },\n rawArgs,\n commandWords: 2,\n command: \"skills install\",\n maxPositionals: 0\n });\n\n return {\n agents: resolveAgentNames(flags[\"--agent\"] ?? []),\n mcp: flags[\"--mcp\"] === true,\n force: flags[\"--force\"] === true\n };\n}\n\n/**\n * The agents a list of `--agent` values names, or null when it names none.\n *\n * Shared by `rebase skills install` and `rebase init`, so both accept the same\n * spellings. Throws a UsageError on a name that is not an agent.\n */\nexport function resolveAgentNames(values: readonly string[]): AgentKey[] | null {\n const requested = values\n .flatMap(value => value.split(\",\").map(v => v.trim()))\n .filter(Boolean);\n if (requested.length === 0) return null;\n\n // `all` exists for the non-interactive case. Detection cannot help there: a\n // scaffolded project ships `.cursorrules`, `CLAUDE.md`, `.windsurfrules`,\n // `AGENTS.md` and `.github/copilot-instructions.md` all at once, so every\n // pointer file is present and none of them says which assistant the\n // developer actually uses. Guessing from them would install several agents'\n // skills unasked; refusing leaves a scripted setup with no way through.\n // Naming `all` is the developer saying it out loud.\n if (requested.includes(\"all\")) return [...AGENT_KEYS];\n\n const unknown = requested.filter(a => !isAgentKey(a));\n if (unknown.length > 0) {\n throw new UsageError(`Unknown agent(s): ${unknown.join(\", \")}. Available: ${AGENT_KEYS.join(\", \")}, all`);\n }\n // Deduplicated, in the order given: `--agent claude,claude` is one install.\n return [...new Set(requested.filter(isAgentKey))];\n}\n\nasync function skillsInstall(rawArgs: string[] = []) {\n // Parse the line before anything else touches the disk: a bad flag must\n // cost nothing, and everything below this writes or resolves paths.\n const { agents: namedAgents, mcp: withMcp, force } = parseSkillsInstallLine(rawArgs);\n\n // The project root, not the cwd. Agent skills belong beside the repository's\n // other agent configuration — `.claude/`, `.cursor/` — which is both what\n // `detectAgents` looks for and where an assistant reads them from. Resolving\n // against wherever the command happened to be typed put them in\n // `backend/.claude/skills` when run from `backend/`, detected no agent\n // there, and left the skills somewhere nothing would look. Outside a project\n // the cwd is still the honest answer: this command is useful in a repository\n // that is not a Rebase one.\n const projectDir = findProjectRoot() ?? process.cwd();\n\n // 1. Load skills from @rebasepro/agent-skills\n let skills: LoadedSkill[];\n try {\n skills = loadBundledSkills();\n } catch (err) {\n console.error(`${chalk.red.bold(\"ERROR\")} ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n // 2. Explicit --agent wins; otherwise detect existing agent environments\n let agents = namedAgents ?? detectAgents(projectDir);\n\n // 3. If none detected, ask the user\n if (agents.length === 0) {\n // A scaffolded project ships `.cursorrules`, `CLAUDE.md`, `AGENTS.md`\n // and `.github/copilot-instructions.md`, but none of the *directories*\n // detectAgents looks for, so a fresh project always lands here — which\n // is correct: those files say what `rebase init` writes, not what the\n // developer uses. On a non-TTY that used to abort with a raw\n // ExitPromptError.\n if (!process.stdin.isTTY) {\n console.error(chalk.red(\"Cannot prompt: this is a non-interactive terminal (no TTY).\"));\n console.error(chalk.yellow(` Name the agents explicitly, e.g. rebase skills install --agent ${Object.keys(AGENTS)[0]}`));\n console.error(chalk.yellow(\" Or install for every supported agent: rebase skills install --agent all\"));\n console.error(chalk.gray(` Available: ${Object.keys(AGENTS).join(\", \")}`));\n process.exit(1);\n }\n\n // The project says nothing, but the machine may: pre-tick the agents\n // whose home directory exists, as `rebase init` does.\n const installed = detectInstalledAgents();\n const choices = installed.map(({ key, label, found }) => ({\n name: label,\n value: key,\n checked: found\n }));\n\n const { selectedAgents } = await inquirer.prompt([{\n type: \"checkbox\",\n name: \"selectedAgents\",\n message: \"No AI agent configuration in this project. Which agents do you use?\",\n choices,\n validate: (input: readonly unknown[]) => {\n if (input.length === 0) return \"Please select at least one agent.\";\n return true;\n }\n }]);\n\n agents = AGENT_KEYS.filter(key => selectedAgents.includes(key));\n }\n\n // 4. Install skills for each agent\n console.log(\"\");\n console.log(chalk.gray(` Found ${chalk.white(skills.length)} Rebase skills`));\n console.log(\"\");\n\n let anyKept = false;\n for (const result of installSkills(agents, skills, projectDir, { force })) {\n const agent = AGENTS[result.agent];\n // Relative to where the command was typed, now that the destination is\n // the project root rather than the cwd — otherwise `.claude/skills`\n // names a directory that is not the one it wrote to.\n const shown = path.relative(process.cwd(), path.join(projectDir, agent.targetDir)) || agent.targetDir;\n if (result.sharedWith) {\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — reads the same ${chalk.gray(shown)} as ${AGENTS[result.sharedWith].label}`);\n continue;\n }\n const withAssets = result.assets > 0 ? ` (+ ${result.assets} reference file${result.assets === 1 ? \"\" : \"s\"})` : \"\";\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — ${result.skills} skills installed${withAssets} to ${chalk.gray(shown)}`);\n for (const file of result.removed) {\n console.log(chalk.gray(` − removed ${path.join(shown, file)} (no longer in the bundle)`));\n }\n for (const file of result.kept) {\n anyKept = true;\n console.log(chalk.yellow(` ! kept ${path.join(shown, file)}: edited since it was installed`));\n }\n }\n if (anyKept) {\n console.log(chalk.yellow(\" Edited files were left as they are. To replace them with the bundle's copy:\"));\n console.log(` ${chalk.cyan(\"rebase skills install --force\")}`);\n }\n\n // The MCP server, as `rebase init --agent` registers it. Imported here\n // rather than at the top: agent-setup imports this module.\n const {\n MCP_SERVER_NAME, ensureMcpDependency, mcpDependencyNote, mcpServerRegistered, writeMcpConfig\n } = await import(\"./agent-setup\");\n const { detectPackageManager } = await import(\"../utils/package-manager\");\n const shownFile = (file: string) => path.relative(process.cwd(), path.join(projectDir, file)) || file;\n if (withMcp) {\n console.log(\"\");\n const pm = detectPackageManager(projectDir);\n let registered = false;\n for (const agentKey of agents) {\n const agent = AGENTS[agentKey];\n const result = writeMcpConfig(agentKey, projectDir, pm);\n if (result && result.status !== \"unreadable\") registered = true;\n if (!result) {\n console.log(chalk.gray(` · ${agent.label} has no project-level MCP config — add @rebasepro/mcp in its MCP settings.`));\n } else if (result.status === \"added\") {\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — MCP server in ${chalk.gray(shownFile(result.file))}`);\n } else if (result.status === \"present\") {\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — MCP server already in ${chalk.gray(shownFile(result.file))}`);\n } else {\n console.log(chalk.yellow(` ! ${agent.label}: ${shownFile(result.file)} is not plain JSON, so it was left alone — add the \"${MCP_SERVER_NAME}\" server by hand.`));\n }\n }\n // The configs run the project's own copy of the server.\n const note = registered ? mcpDependencyNote(ensureMcpDependency(projectDir), pm) : undefined;\n if (note) console.log(chalk.yellow(` ${note}`));\n }\n\n console.log(\"\");\n console.log(chalk.gray(\" Skills are project-local. Commit them to share with your team.\"));\n // The skills ship inside the CLI's own pinned `@rebasepro/agent-skills`,\n // so a re-run reinstalls that version; newer ones come with a newer CLI.\n console.log(chalk.gray(\" Re-run this command to restore them. Upgrade @rebasepro/cli for newer skills.\"));\n if (!withMcp) {\n // Only the agents whose config does not have the server yet: on a\n // project `init --agent` set up, a refresh says nothing about it.\n const missing = agents.filter(agentKey => mcpServerRegistered(agentKey, projectDir) === false);\n if (missing.length > 0) {\n console.log(chalk.gray(\" To register the Rebase MCP server for them too, as `rebase init --agent` does:\"));\n console.log(` ${chalk.cyan(`rebase skills install --agent ${missing.join(\",\")} --mcp`)}`);\n }\n }\n console.log(\"\");\n}\n\nfunction printSkillsHelp() {\n console.log(`\n${chalk.bold(\"rebase skills\")} — Manage AI agent skills\n\n${chalk.green.bold(\"Usage\")}\n rebase skills ${chalk.blue(\"<subcommand>\")}\n\n${chalk.green.bold(\"Subcommands\")}\n ${chalk.blue.bold(\"install\")} Install Rebase agent skills for your AI coding assistant\n Supports: ${Object.values(AGENTS).map(a => a.label).join(\", \")}\n\n${chalk.green.bold(\"Options\")}\n ${chalk.blue(\"--force\")} Replace installed skill files you edited, and remove edited files of\n skills the bundle no longer ships. Without it, edits are kept.\n ${chalk.blue(\"--mcp\")} Also register the Rebase MCP server in each agent's project config\n (.mcp.json, .cursor/mcp.json, …), as ${chalk.cyan(\"rebase init --agent\")} does.\n Servers already in the file are kept.\n ${chalk.blue(\"--agent, -a\")} Agent(s) to install for, skipping detection and the prompt.\n Repeat the flag or pass a comma-separated list, or ${chalk.bold(\"all\")}.\n Required without a TTY: detection looks for an agent's own\n directory (${Object.values(AGENTS).filter(a => a.detectDir).map(a => a.detectDir).join(\", \")}),\n and a fresh scaffold has none. GitHub Copilot is never detected\n — ${chalk.gray(\".github/\")} is not evidence anyone uses it — so name it.\n Available: ${Object.keys(AGENTS).join(\", \")}, all\n\n${chalk.green.bold(\"Examples\")}\n ${chalk.cyan(\"rebase skills install\")}\n ${chalk.cyan(\"rebase skills install --agent claude\")}\n ${chalk.cyan(\"rebase skills install --agent claude,cursor\")}\n ${chalk.cyan(\"rebase skills install --agent cursor --mcp\")} ${chalk.gray(\"# skills + the MCP server\")}\n ${chalk.cyan(\"rebase skills install --agent all\")} ${chalk.gray(\"# scripted / CI\")}\n`);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"skills-DfnTeyRz.js","names":[],"sources":["../src/commands/skills.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { createHash } from \"crypto\";\nimport fs from \"fs\";\nimport path from \"path\";\nimport inquirer from \"inquirer\";\nimport { createRequire } from \"module\";\nimport os from \"os\";\nimport { findProjectRoot } from \"../utils/project\";\nimport { parseCommandArgs, UsageError, wantsHelp } from \"../utils/args\";\nimport { unknownCommand } from \"../utils/unknown-command\";\n\nconst require = createRequire(import.meta.url);\n\n/**\n * Supported agent environments and their target directories.\n *\n * Every agent `rebase init` writes a pointer file for has an entry here. That\n * was not true: `rebase init` writes `CLAUDE.md`, `AGENTS.md`, `.cursorrules`,\n * `.windsurfrules` and `.github/copilot-instructions.md`, and the installer\n * covered four of the five — so a Codex or Copilot user was told, by a file in\n * their own repository, to run an installer that had nothing to offer them.\n *\n * `flatLayout` marks the agents whose rules directory is loaded *whole*, into\n * every request. Writing 21 skills there put ~84,000 characters of always-on\n * context in front of every question a person asked Cursor — more than the task\n * itself, on every turn, most of it about parts of Rebase they were not\n * touching. Those agents get one short index rule instead, and the skill bodies\n * go in per-skill subdirectories the index names, which the agent reads when it\n * needs one. `indexFile` is the name of that rule.\n *\n * A subdirectory layout has no such problem: the rule file already sits beside\n * its own assets, so `references/x.md` resolves as written and nothing is\n * loaded until it is opened.\n *\n * `homeMarkers` are the directories under `~` that say the assistant is\n * installed on this machine. They answer a different question from\n * `detectDir`: a fresh scaffold has no `.claude/` of its own, so `rebase init`\n * asks the machine which assistants the developer has and pre-selects those.\n *\n * `mcp` is where the assistant reads a *project-level* MCP server list, and in\n * which shape — see `agent-setup.ts`. `projectDir` is what `REBASE_PROJECT_DIR`\n * is set to: `\".\"` where the server is spawned in the project, the editor's own\n * workspace variable where it may not be. `null` means there is no project\n * file to write: Windsurf reads only a user-level config, and one entry there\n * cannot name every project on the machine.\n */\nexport const AGENTS = {\n cursor: {\n label: \"Cursor\",\n detectDir: \".cursor\",\n homeMarkers: [\".cursor\"],\n targetDir: \".cursor/rules\",\n flatLayout: true,\n indexFile: \"rebase.mdc\",\n mcp: {\n file: \".cursor/mcp.json\",\n format: \"mcpServers\",\n projectDir: \"${workspaceFolder}\"\n },\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n claude: {\n label: \"Claude Code\",\n detectDir: \".claude\",\n homeMarkers: [\".claude\"],\n targetDir: \".claude/skills\",\n flatLayout: false,\n // The scaffold already ships this file; writing it is then a no-op.\n mcp: {\n file: \".mcp.json\",\n format: \"mcpServers\",\n projectDir: \".\"\n },\n /** Claude Code uses the standard SKILL.md format in subdirectories. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n windsurf: {\n label: \"Windsurf\",\n detectDir: \".windsurf\",\n homeMarkers: [\".codeium/windsurf\", \".windsurf\"],\n targetDir: \".windsurf/rules\",\n flatLayout: true,\n indexFile: \"rebase.md\",\n mcp: null,\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n gemini: {\n label: \"Gemini CLI / Antigravity\",\n detectDir: \".agents\",\n homeMarkers: [\".gemini\"],\n targetDir: \".agents/skills\",\n flatLayout: false,\n mcp: {\n file: \".gemini/settings.json\",\n format: \"mcpServers\",\n projectDir: \".\"\n },\n /** Gemini uses the standard SKILL.md format in subdirectories. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n codex: {\n label: \"Codex CLI\",\n detectDir: \".codex\",\n homeMarkers: [\".codex\"],\n // Codex reads repository skills from `.agents/skills` — every directory\n // from the working directory up to the repository root — and nothing\n // under `.codex/`. This entry used to write `.codex/skills`, so `--agent\n // codex` reported 21 skills installed into a directory Codex never\n // opens. It shares the directory with Gemini; the installer writes it\n // once.\n targetDir: \".agents/skills\",\n flatLayout: false,\n // Project-scoped, and read only once the developer trusts the project.\n mcp: {\n file: \".codex/config.toml\",\n format: \"codexToml\",\n projectDir: \".\"\n },\n /** Codex uses the standard SKILL.md format in subdirectories. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n kiro: {\n label: \"Kiro\",\n detectDir: \".kiro\",\n homeMarkers: [\".kiro\"],\n targetDir: \".kiro/steering\",\n flatLayout: true,\n indexFile: \"rebase.md\",\n mcp: {\n file: \".kiro/settings/mcp.json\",\n format: \"mcpServers\",\n projectDir: \".\"\n },\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n },\n copilot: {\n label: \"GitHub Copilot\",\n // Deliberately undetectable. `.github/` is the one directory in this\n // table that is not evidence of an assistant: `rebase init` writes\n // `.github/copilot-instructions.md` into every scaffold, and half the\n // repositories on the internet have a `.github/` for workflows and\n // issue templates. Listing it made bare `rebase skills install` resolve\n // to `[copilot]` in every project — so a Claude Code user who ran the\n // command `rebase init` printed got a success message and nothing in\n // `.claude/skills`. Copilot is `--agent copilot`.\n detectDir: null,\n // Same reasoning on the machine: Copilot lives inside VS Code or a\n // JetBrains IDE, and neither's directory says the extension is there.\n homeMarkers: [],\n targetDir: \".github/instructions\",\n flatLayout: true,\n indexFile: \"rebase.instructions.md\",\n mcp: {\n file: \".vscode/mcp.json\",\n format: \"vscode\",\n projectDir: \"${workspaceFolder}\"\n },\n /** The body is not a rule file here — `indexFile` is. */\n transformFile: (skillName: string, content: string) => ({\n fileName: path.join(skillName, \"SKILL.md\"),\n content\n })\n }\n} as const;\n\nexport type AgentKey = keyof typeof AGENTS;\n\n/** Narrows a string to an agent name, by the table's own keys. */\nexport function isAgentKey(value: string): value is AgentKey {\n return Object.hasOwn(AGENTS, value);\n}\n\n/** Every agent, in table order. */\nexport const AGENT_KEYS: AgentKey[] = Object.keys(AGENTS).filter(isAgentKey);\n\n/**\n * Resolve the path to the skills directory from @rebasepro/agent-skills.\n * Works in both workspace (symlink) and published (real files) layouts.\n */\nfunction getSkillsSourceDir(): string {\n const pkgJsonPath = require.resolve(\"@rebasepro/agent-skills/package.json\");\n const pkgRoot = path.dirname(pkgJsonPath);\n const skillsDir = path.join(pkgRoot, \"skills\");\n\n if (!fs.existsSync(skillsDir)) {\n throw new Error(\n `Skills directory not found at ${skillsDir}. ` +\n \"Make sure @rebasepro/agent-skills is installed.\"\n );\n }\n\n return skillsDir;\n}\n\nexport interface LoadedSkill {\n name: string;\n /** Absolute path of the skill's source directory. */\n dir: string;\n content: string;\n /** Every file the skill ships besides SKILL.md, relative to `dir`. */\n assets: string[];\n}\n\n/**\n * Everything a skill ships alongside its SKILL.md — the `references/` tree the\n * Agent Skills format uses for progressive disclosure.\n *\n * These used to be dropped on install, because the installer read exactly\n * `<skill>/SKILL.md` and nothing else. That left `rebase-design-language`\n * telling the agent three separate times to read `references/view-patterns.md`\n * — 379 lines of view skeletons — in a project where the file had never\n * landed, and the instruction it carries is \"extend an existing pattern; do not\n * invent a layout\".\n */\nfunction loadSkillAssets(skillDir: string): string[] {\n const found: string[] = [];\n\n const walk = (dir: string, prefix: string) => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n // Dotfiles are bookkeeping — `.gitkeep` is what holds the empty\n // `references/` directories in git — and install nothing.\n if (entry.name.startsWith(\".\")) continue;\n const rel = prefix ? path.join(prefix, entry.name) : entry.name;\n if (entry.isDirectory()) walk(path.join(dir, entry.name), rel);\n else if (rel !== \"SKILL.md\") found.push(rel);\n }\n };\n\n walk(skillDir, \"\");\n return found.sort();\n}\n\n/** Read all skill directories and return their names, content and assets. */\nexport function loadSkills(skillsDir: string): LoadedSkill[] {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true });\n const skills: LoadedSkill[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const skillDir = path.join(skillsDir, entry.name);\n const skillMdPath = path.join(skillDir, \"SKILL.md\");\n if (!fs.existsSync(skillMdPath)) continue;\n skills.push({\n name: entry.name,\n dir: skillDir,\n content: fs.readFileSync(skillMdPath, \"utf-8\"),\n assets: loadSkillAssets(skillDir)\n });\n }\n\n return skills;\n}\n\n/**\n * The skills this CLI ships, from its own `@rebasepro/agent-skills`.\n *\n * Throws rather than exiting, so `rebase init` can report a missing bundle as a\n * warning and still finish the scaffold.\n */\nexport function loadBundledSkills(): LoadedSkill[] {\n const skillsDir = getSkillsSourceDir();\n const skills = loadSkills(skillsDir);\n if (skills.length === 0) throw new Error(`No skills found in ${skillsDir}`);\n return skills;\n}\n\n/**\n * The one always-on rule a flat-layout agent gets, in place of every skill.\n *\n * Cursor and Windsurf load their whole rules directory into each request. With\n * a rule file per skill that was ~84,000 characters of Rebase reference in\n * front of every question, whether or not the question was about Rebase — and\n * an instruction an assistant skims is an instruction it does not follow.\n *\n * So the always-on part is this: a table of what exists and where to read it.\n * `alwaysApply: true` because knowing the skills are there costs a few hundred\n * characters and is the whole point; the bodies stay on disk until the agent\n * opens one.\n */\nexport function renderSkillIndex(skills: LoadedSkill[], agentKey: AgentKey): string {\n const agent = AGENTS[agentKey];\n const bodyPath = (name: string) => agent.transformFile(name, \"\").fileName.split(path.sep).join(\"/\");\n const rows = skills\n .map((skill) => `| \\`${skill.name}\\` | ${describe(skill)} | \\`${bodyPath(skill.name)}\\` |`)\n .join(\"\\n\");\n\n return [\n \"---\",\n \"description: Rebase — the skills index. Read the file named for the task before writing Rebase code.\",\n \"alwaysApply: true\",\n \"---\",\n \"\",\n \"# Rebase skills\",\n \"\",\n `This project uses [Rebase](https://rebase.pro). ${skills.length} reference skills are`,\n \"installed beside this file. **Read the one that covers the task before writing\",\n \"code** — they are the difference between a collection that compiles and one that\",\n \"does not.\",\n \"\",\n \"| Skill | Covers | Read |\",\n \"|---|---|---|\",\n rows,\n \"\",\n \"Start with `rebase-basics` if you are not sure: it opens with the recipes for\",\n \"adding a collection, a function and an RLS rule, and names the commands.\",\n \"\"\n ].join(\"\\n\");\n}\n\n/**\n * The body of a YAML block scalar, given the offset of its `description:` line.\n *\n * Everything indented more than the key belongs to the value; the first line\n * that is not blank and not indented ends it.\n */\nfunction blockScalarBody(frontMatter: string, keyOffset: number): string {\n const lines = frontMatter.slice(keyOffset).split(\"\\n\").slice(1);\n const body: string[] = [];\n for (const line of lines) {\n if (line.trim() === \"\") { body.push(\"\"); continue; }\n if (!/^[ \\t]/.test(line)) break;\n body.push(line.trim());\n }\n return body.join(\" \");\n}\n\n/**\n * A skill's one-line summary, from its own front matter.\n *\n * Read rather than written here so a new skill needs no edit in this file, and\n * truncated because the index is only worth having while it stays short.\n */\nfunction describe(skill: LoadedSkill): string {\n const frontMatter = skill.content.split(\"---\")[1] ?? \"\";\n const described = /^description:[ \\t]*(.*)$/m.exec(frontMatter);\n // `description: |` (or `>`) is a YAML block scalar: the value is the\n // indented lines that follow, and capturing the line itself captured the\n // `|`. That rendered an empty \"Covers\" cell for `rebase-design-language`,\n // the one skill whose description says agents MUST read it.\n const raw = /^[|>][-+]?$/.test((described?.[1] ?? \"\").trim())\n ? blockScalarBody(frontMatter, described?.index ?? 0)\n : (described?.[1] ?? \"\");\n const text = raw.trim().replace(/\\s+/g, \" \").replace(/\\|/g, \"\\\\|\");\n const firstSentence = /^.*?\\.(?:\\s|$)/.exec(text)?.[0]?.trim() ?? text;\n const fallback = skill.name.replace(/^rebase-/, \"\").replace(/-/g, \" \");\n if (!firstSentence) return fallback;\n if (firstSentence.length <= 140) return firstSentence;\n // Cut at a word boundary. A description sliced mid-word (\"using the collect\")\n // reads as a corrupted file rather than as a summary.\n const cut = firstSentence.slice(0, 140);\n return `${cut.slice(0, cut.lastIndexOf(\" \")).replace(/[,;:]$/, \"\")}…`;\n}\n\n/**\n * Detect which agent environments already exist in the project.\n *\n * A `detectDir` has to be evidence *the user* created, not something a scaffold\n * ships. An agent whose only marker fails that test declares `detectDir: null`\n * and is reachable by `--agent` alone; guessing wrong here is worse than not\n * guessing, because the wrong guess installs 21 files and reports success.\n */\nexport function detectAgents(projectDir: string): AgentKey[] {\n return AGENT_KEYS.filter((key) => {\n const { detectDir } = AGENTS[key];\n return detectDir !== null && fs.existsSync(path.join(projectDir, detectDir));\n });\n}\n\n/** What `detectInstalledAgents` found for one assistant. */\nexport interface InstalledAgent {\n key: AgentKey;\n label: string;\n /** The home directory checked, as `~/…` — the one found, else the first. */\n marker: string | null;\n found: boolean;\n}\n\n/**\n * Which assistants are installed on this machine, from their home directories.\n *\n * The question `rebase init` needs answered, and one `detectAgents` cannot: a\n * project that does not exist yet has no `.claude/` in it. What the machine has\n * is only a default — the developer confirms it — so a false positive costs an\n * unticked box, not 21 files nobody asked for.\n */\nexport function detectInstalledAgents(home: string = os.homedir()): InstalledAgent[] {\n return AGENT_KEYS.map((key) => {\n const agent = AGENTS[key];\n const markers: readonly string[] = agent.homeMarkers;\n const hit = markers.find((m) => fs.existsSync(path.join(home, m)));\n const shown = hit ?? markers[0];\n return {\n key,\n label: agent.label,\n marker: shown === undefined ? null : `~/${shown}`,\n found: hit !== undefined\n };\n });\n}\n\n/** Install skills for a specific agent into the project directory. */\n/**\n * The file in each target directory that records what the installer wrote:\n * relative path → sha256 of the bytes written. A dotfile, so no agent loads it\n * as a rule.\n */\nexport const SKILLS_MANIFEST = \".rebase-skills.json\";\n\ninterface SkillsManifest {\n version: 1;\n files: Record<string, string>;\n}\n\nfunction sha256(bytes: Buffer | string): string {\n return createHash(\"sha256\").update(bytes).digest(\"hex\");\n}\n\nfunction readManifest(targetBase: string): SkillsManifest | null {\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(path.join(targetBase, SKILLS_MANIFEST), \"utf-8\"));\n if (parsed && typeof parsed === \"object\" && \"files\" in parsed) {\n const files = (parsed as { files: unknown }).files;\n if (files && typeof files === \"object\") {\n const entries = Object.entries(files).filter((e): e is [string, string] => typeof e[1] === \"string\");\n return { version: 1, files: Object.fromEntries(entries) };\n }\n }\n } catch {\n // No manifest, or one that is not ours: the first install, or one made\n // before manifests existed.\n }\n return null;\n}\n\n/** What one agent's install did. */\nexport interface AgentInstallResult {\n skills: number;\n assets: number;\n /** Files left as they are because they were edited since the last install (relative to the target dir). */\n kept: string[];\n /** Files of skills the bundle no longer ships, removed because nobody edited them. */\n removed: string[];\n}\n\nexport function installForAgent(\n agentKey: AgentKey,\n skills: LoadedSkill[],\n projectDir: string,\n options: { force?: boolean } = {}\n): AgentInstallResult {\n const agent = AGENTS[agentKey];\n const targetBase = path.join(projectDir, agent.targetDir);\n\n // Ensure the target directory exists\n fs.mkdirSync(targetBase, { recursive: true });\n\n // A re-run used to overwrite every file unconditionally and never remove\n // one: a note a team appended to an installed skill and committed — as\n // the command tells them to — vanished on the next `skills install`, and a\n // skill a release retired stayed installed beside its replacement. The\n // manifest records what was written, so an edit is told apart from an\n // older bundle's copy. A file with no record (an install from before\n // manifests) is overwritten, as before.\n const previous = readManifest(targetBase);\n const recorded: Record<string, string> = {};\n const kept: string[] = [];\n\n const place = (relPath: string, bytes: Buffer | string) => {\n const target = path.join(targetBase, relPath);\n const wanted = sha256(bytes);\n const last = previous?.files[relPath];\n if (fs.existsSync(target)) {\n const current = sha256(fs.readFileSync(target));\n if (current === wanted) {\n recorded[relPath] = wanted;\n return;\n }\n if (last !== undefined && current !== last && !options.force) {\n // Edited since it was installed: left alone, and still recorded\n // as what was installed, so the next run says so again.\n kept.push(relPath);\n recorded[relPath] = last;\n return;\n }\n }\n fs.mkdirSync(path.dirname(target), { recursive: true });\n fs.writeFileSync(target, bytes);\n recorded[relPath] = wanted;\n };\n\n let count = 0;\n let assetCount = 0;\n for (const skill of skills) {\n // Every layout now writes the body into a per-skill subdirectory, so a\n // link the skill spells `references/x.md` resolves as written and no\n // rewriting is needed. What differs is whether that file is also the\n // agent's *rule* file (subdirectory layouts) or something the index\n // points at (flat ones).\n const { fileName, content } = agent.transformFile(skill.name, skill.content);\n place(fileName, content);\n count++;\n\n // A skill's assets always land in a directory named after the skill,\n // whatever the rule file's layout, so two skills' `references/` trees\n // cannot collide in a flat target directory.\n for (const asset of skill.assets) {\n place(path.join(skill.name, asset), fs.readFileSync(path.join(skill.dir, asset)));\n assetCount++;\n }\n }\n\n if (agent.flatLayout && agent.indexFile) {\n place(agent.indexFile, renderSkillIndex(skills, agentKey));\n }\n\n // Retired files: removed when they are exactly what was installed, kept\n // (and reported) when someone edited them.\n const removed: string[] = [];\n for (const [relPath, last] of Object.entries(previous?.files ?? {})) {\n if (relPath in recorded) continue;\n const target = path.join(targetBase, relPath);\n if (!fs.existsSync(target)) continue;\n if (sha256(fs.readFileSync(target)) !== last && !options.force) {\n kept.push(relPath);\n continue;\n }\n fs.rmSync(target);\n removed.push(relPath);\n // Empty directories the removal leaves behind, up to the target dir.\n for (let dir = path.dirname(target); dir.startsWith(targetBase + path.sep); dir = path.dirname(dir)) {\n if (fs.readdirSync(dir).length > 0) break;\n fs.rmdirSync(dir);\n }\n }\n\n fs.writeFileSync(\n path.join(targetBase, SKILLS_MANIFEST),\n JSON.stringify({ version: 1, files: recorded } satisfies SkillsManifest, null, 2) + \"\\n\",\n \"utf-8\"\n );\n\n return { skills: count, assets: assetCount, kept, removed };\n}\n\n/** What `installSkills` did for one agent. */\nexport interface SkillsInstallResult extends AgentInstallResult {\n agent: AgentKey;\n /** An earlier agent in the same run that already wrote this directory. */\n sharedWith?: AgentKey;\n}\n\n/**\n * Install for several agents, writing each target directory once.\n *\n * Gemini and Codex both read `.agents/skills`; installing twice would write the\n * same 21 files twice and report two installs of one directory.\n */\nexport function installSkills(\n agents: readonly AgentKey[],\n skills: LoadedSkill[],\n projectDir: string,\n options: { force?: boolean } = {}\n): SkillsInstallResult[] {\n const written = new Map<string, SkillsInstallResult>();\n return agents.map((agent) => {\n const earlier = written.get(AGENTS[agent].targetDir);\n if (earlier) return { ...earlier, agent, sharedWith: earlier.agent };\n const result = { agent, ...installForAgent(agent, skills, projectDir, options) };\n written.set(AGENTS[agent].targetDir, result);\n return result;\n });\n}\n\n/** Everything the switch below dispatches, for the did-you-mean. */\nexport const SKILLS_SUBCOMMANDS = [\"install\"] as const;\n\nexport async function skillsCommand(subcommand: string | undefined, rawArgs: string[]) {\n // `--help` cannot reach `skillsInstall`. `cli.ts` only rewrites the\n // subcommand to `\"--help\"` when none was named, so `rebase skills install\n // --help` used to run the install: it detected the agents and overwrote\n // `.claude/skills/*/SKILL.md`, `.cursor/rules/*.mdc` and the rest. A flag\n // that prints text must not write files.\n if (wantsHelp(rawArgs)) {\n printSkillsHelp();\n return;\n }\n\n switch (subcommand) {\n case \"install\":\n await skillsInstall(rawArgs);\n break;\n case \"--help\":\n case undefined:\n printSkillsHelp();\n break;\n default:\n unknownCommand(subcommand, SKILLS_SUBCOMMANDS, \"skills\");\n }\n}\n\n/**\n * Agents named explicitly on the command line, e.g. `--agent claude --agent cursor`\n * (also accepts a comma-separated list, and `all`). Returns null when none were\n * given.\n */\nexport function parseAgentFlags(rawArgs: string[]): AgentKey[] | null {\n return parseSkillsInstallLine(rawArgs).agents;\n}\n\n/** `rebase skills install`'s whole line: the agents it names, `--mcp` and `--force`. */\nexport function parseSkillsInstallLine(rawArgs: string[]): { agents: AgentKey[] | null; mcp: boolean; force: boolean } {\n // Strict, and before anything is written. This used to scan `rawArgs` for\n // `--agent` and ignore every other token, so `rebase skills install\n // --frobnicate --agent claude` exited 0 after writing 21 files — the CLI's\n // other families reject an unknown flag, and an installer is the last place\n // to guess what the person meant.\n const { flags } = parseCommandArgs({\n spec: { \"--agent\": [String], \"-a\": \"--agent\", \"--mcp\": Boolean, \"--force\": Boolean },\n rawArgs,\n commandWords: 2,\n command: \"skills install\",\n maxPositionals: 0\n });\n\n return {\n agents: resolveAgentNames(flags[\"--agent\"] ?? []),\n mcp: flags[\"--mcp\"] === true,\n force: flags[\"--force\"] === true\n };\n}\n\n/**\n * The agents a list of `--agent` values names, or null when it names none.\n *\n * Shared by `rebase skills install` and `rebase init`, so both accept the same\n * spellings. Throws a UsageError on a name that is not an agent.\n */\nexport function resolveAgentNames(values: readonly string[]): AgentKey[] | null {\n const requested = values\n .flatMap(value => value.split(\",\").map(v => v.trim()))\n .filter(Boolean);\n if (requested.length === 0) return null;\n\n // `all` exists for the non-interactive case. Detection cannot help there: a\n // scaffolded project ships `.cursorrules`, `CLAUDE.md`, `.windsurfrules`,\n // `AGENTS.md` and `.github/copilot-instructions.md` all at once, so every\n // pointer file is present and none of them says which assistant the\n // developer actually uses. Guessing from them would install several agents'\n // skills unasked; refusing leaves a scripted setup with no way through.\n // Naming `all` is the developer saying it out loud.\n if (requested.includes(\"all\")) return [...AGENT_KEYS];\n\n const unknown = requested.filter(a => !isAgentKey(a));\n if (unknown.length > 0) {\n throw new UsageError(`Unknown agent(s): ${unknown.join(\", \")}. Available: ${AGENT_KEYS.join(\", \")}, all`);\n }\n // Deduplicated, in the order given: `--agent claude,claude` is one install.\n return [...new Set(requested.filter(isAgentKey))];\n}\n\nasync function skillsInstall(rawArgs: string[] = []) {\n // Parse the line before anything else touches the disk: a bad flag must\n // cost nothing, and everything below this writes or resolves paths.\n const { agents: namedAgents, mcp: withMcp, force } = parseSkillsInstallLine(rawArgs);\n\n // The project root, not the cwd. Agent skills belong beside the repository's\n // other agent configuration — `.claude/`, `.cursor/` — which is both what\n // `detectAgents` looks for and where an assistant reads them from. Resolving\n // against wherever the command happened to be typed put them in\n // `backend/.claude/skills` when run from `backend/`, detected no agent\n // there, and left the skills somewhere nothing would look. Outside a project\n // the cwd is still the honest answer: this command is useful in a repository\n // that is not a Rebase one.\n const projectDir = findProjectRoot() ?? process.cwd();\n\n // 1. Load skills from @rebasepro/agent-skills\n let skills: LoadedSkill[];\n try {\n skills = loadBundledSkills();\n } catch (err) {\n console.error(`${chalk.red.bold(\"ERROR\")} ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n // 2. Explicit --agent wins; otherwise detect existing agent environments\n let agents = namedAgents ?? detectAgents(projectDir);\n\n // 3. If none detected, ask the user\n if (agents.length === 0) {\n // A scaffolded project ships `.cursorrules`, `CLAUDE.md`, `AGENTS.md`\n // and `.github/copilot-instructions.md`, but none of the *directories*\n // detectAgents looks for, so a fresh project always lands here — which\n // is correct: those files say what `rebase init` writes, not what the\n // developer uses. On a non-TTY that used to abort with a raw\n // ExitPromptError.\n if (!process.stdin.isTTY) {\n console.error(chalk.red(\"Cannot prompt: this is a non-interactive terminal (no TTY).\"));\n console.error(chalk.yellow(` Name the agents explicitly, e.g. rebase skills install --agent ${Object.keys(AGENTS)[0]}`));\n console.error(chalk.yellow(\" Or install for every supported agent: rebase skills install --agent all\"));\n console.error(chalk.gray(` Available: ${Object.keys(AGENTS).join(\", \")}`));\n process.exit(1);\n }\n\n // The project says nothing, but the machine may: pre-tick the agents\n // whose home directory exists, as `rebase init` does.\n const installed = detectInstalledAgents();\n const choices = installed.map(({ key, label, found }) => ({\n name: label,\n value: key,\n checked: found\n }));\n\n const { selectedAgents } = await inquirer.prompt([{\n type: \"checkbox\",\n name: \"selectedAgents\",\n message: \"No AI agent configuration in this project. Which agents do you use?\",\n choices,\n validate: (input: readonly unknown[]) => {\n if (input.length === 0) return \"Please select at least one agent.\";\n return true;\n }\n }]);\n\n agents = AGENT_KEYS.filter(key => selectedAgents.includes(key));\n }\n\n // 4. Install skills for each agent\n console.log(\"\");\n console.log(chalk.gray(` Found ${chalk.white(skills.length)} Rebase skills`));\n console.log(\"\");\n\n let anyKept = false;\n for (const result of installSkills(agents, skills, projectDir, { force })) {\n const agent = AGENTS[result.agent];\n // Relative to where the command was typed, now that the destination is\n // the project root rather than the cwd — otherwise `.claude/skills`\n // names a directory that is not the one it wrote to.\n const shown = path.relative(process.cwd(), path.join(projectDir, agent.targetDir)) || agent.targetDir;\n if (result.sharedWith) {\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — reads the same ${chalk.gray(shown)} as ${AGENTS[result.sharedWith].label}`);\n continue;\n }\n const withAssets = result.assets > 0 ? ` (+ ${result.assets} reference file${result.assets === 1 ? \"\" : \"s\"})` : \"\";\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — ${result.skills} skills installed${withAssets} to ${chalk.gray(shown)}`);\n for (const file of result.removed) {\n console.log(chalk.gray(` − removed ${path.join(shown, file)} (no longer in the bundle)`));\n }\n for (const file of result.kept) {\n anyKept = true;\n console.log(chalk.yellow(` ! kept ${path.join(shown, file)}: edited since it was installed`));\n }\n }\n if (anyKept) {\n console.log(chalk.yellow(\" Edited files were left as they are. To replace them with the bundle's copy:\"));\n console.log(` ${chalk.cyan(\"rebase skills install --force\")}`);\n }\n\n // The MCP server, as `rebase init --agent` registers it. Imported here\n // rather than at the top: agent-setup imports this module.\n const {\n MCP_SERVER_NAME, ensureMcpDependency, mcpDependencyNote, mcpServerRegistered, writeMcpConfig\n } = await import(\"./agent-setup\");\n const { detectPackageManager } = await import(\"../utils/package-manager\");\n const shownFile = (file: string) => path.relative(process.cwd(), path.join(projectDir, file)) || file;\n if (withMcp) {\n console.log(\"\");\n const pm = detectPackageManager(projectDir);\n let registered = false;\n for (const agentKey of agents) {\n const agent = AGENTS[agentKey];\n const result = writeMcpConfig(agentKey, projectDir, pm);\n if (result && result.status !== \"unreadable\") registered = true;\n if (!result) {\n console.log(chalk.gray(` · ${agent.label} has no project-level MCP config — add @rebasepro/mcp in its MCP settings.`));\n } else if (result.status === \"added\") {\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — MCP server in ${chalk.gray(shownFile(result.file))}`);\n } else if (result.status === \"present\") {\n console.log(` ${chalk.green(\"✓\")} ${chalk.bold(agent.label)} — MCP server already in ${chalk.gray(shownFile(result.file))}`);\n } else {\n console.log(chalk.yellow(` ! ${agent.label}: ${shownFile(result.file)} is not plain JSON, so it was left alone — add the \"${MCP_SERVER_NAME}\" server by hand.`));\n }\n }\n // The configs run the project's own copy of the server.\n const note = registered ? mcpDependencyNote(ensureMcpDependency(projectDir), pm) : undefined;\n if (note) console.log(chalk.yellow(` ${note}`));\n }\n\n console.log(\"\");\n console.log(chalk.gray(\" Skills are project-local. Commit them to share with your team.\"));\n // The skills ship inside the CLI's own pinned `@rebasepro/agent-skills`,\n // so a re-run reinstalls that version; newer ones come with a newer CLI.\n console.log(chalk.gray(\" Re-run this command to restore them. Upgrade @rebasepro/cli for newer skills.\"));\n if (!withMcp) {\n // Only the agents whose config does not have the server yet: on a\n // project `init --agent` set up, a refresh says nothing about it.\n const missing = agents.filter(agentKey => mcpServerRegistered(agentKey, projectDir) === false);\n if (missing.length > 0) {\n console.log(chalk.gray(\" To register the Rebase MCP server for them too, as `rebase init --agent` does:\"));\n console.log(` ${chalk.cyan(`rebase skills install --agent ${missing.join(\",\")} --mcp`)}`);\n }\n }\n console.log(\"\");\n}\n\nfunction printSkillsHelp() {\n console.log(`\n${chalk.bold(\"rebase skills\")} — Manage AI agent skills\n\n${chalk.green.bold(\"Usage\")}\n rebase skills ${chalk.blue(\"<subcommand>\")}\n\n${chalk.green.bold(\"Subcommands\")}\n ${chalk.blue.bold(\"install\")} Install Rebase agent skills for your AI coding assistant\n Supports: ${Object.values(AGENTS).map(a => a.label).join(\", \")}\n\n${chalk.green.bold(\"Options\")}\n ${chalk.blue(\"--force\")} Replace installed skill files you edited, and remove edited files of\n skills the bundle no longer ships. Without it, edits are kept.\n ${chalk.blue(\"--mcp\")} Also register the Rebase MCP server in each agent's project config\n (.mcp.json, .cursor/mcp.json, …), as ${chalk.cyan(\"rebase init --agent\")} does.\n Servers already in the file are kept.\n ${chalk.blue(\"--agent, -a\")} Agent(s) to install for, skipping detection and the prompt.\n Repeat the flag or pass a comma-separated list, or ${chalk.bold(\"all\")}.\n Required without a TTY: detection looks for an agent's own\n directory (${Object.values(AGENTS).filter(a => a.detectDir).map(a => a.detectDir).join(\", \")}),\n and a fresh scaffold has none. GitHub Copilot is never detected\n — ${chalk.gray(\".github/\")} is not evidence anyone uses it — so name it.\n Available: ${Object.keys(AGENTS).join(\", \")}, all\n\n${chalk.green.bold(\"Examples\")}\n ${chalk.cyan(\"rebase skills install\")}\n ${chalk.cyan(\"rebase skills install --agent claude\")}\n ${chalk.cyan(\"rebase skills install --agent claude,cursor\")}\n ${chalk.cyan(\"rebase skills install --agent cursor --mcp\")} ${chalk.gray(\"# skills + the MCP server\")}\n ${chalk.cyan(\"rebase skills install --agent all\")} ${chalk.gray(\"# scripted / CI\")}\n`);\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { n as getPMCommands, t as detectPackageManager } from "./package-manager-DSEOwTqM.js";
2
2
  import { d as readEnvFile, o as findEnvFile, p as requireProjectRoot } from "./project-Wp2zoyeN.js";
3
- import { n as parseCommandArgs, r as wantsHelp } from "./args-CcBphFdj.js";
4
- import { t as DEFAULT_BUNDLE_DIR } from "./bundle-CqURVN-K.js";
3
+ import { n as parseCommandArgs, r as wantsHelp } from "./args-BX0YD14a.js";
4
+ import { t as DEFAULT_BUNDLE_DIR } from "./bundle-DtCCCr4w.js";
5
5
  import chalk from "chalk";
6
6
  import fs from "fs";
7
7
  import path from "path";
@@ -207,7 +207,7 @@ async function startCommand(rawArgs = []) {
207
207
  quiet: true
208
208
  });
209
209
  if (!process.env.DATABASE_URL?.trim()) {
210
- const { devDatabaseKind } = await import("./prepare-C1iaiTC4.js");
210
+ const { devDatabaseKind } = await import("./prepare-DdnU70z8.js");
211
211
  const missing = missingDatabaseUrl(process.env, devDatabaseKind(projectRoot), getPMCommands(detectPackageManager(projectRoot)));
212
212
  if (missing) {
213
213
  for (const line of missing) console.error(line);
@@ -303,4 +303,4 @@ async function startWorkspaceBackend(projectRoot, env) {
303
303
  //#endregion
304
304
  export { startCommand };
305
305
 
306
- //# sourceMappingURL=start-BsPJedPQ.js.map
306
+ //# sourceMappingURL=start-BmEZkS0n.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"start-BsPJedPQ.js","names":[],"sources":["../src/commands/start.ts"],"sourcesContent":["/**\n * CLI command: rebase start\n *\n * Runs a built bundle through the Rebase runtime — the same path the official\n * container image takes, so what you test locally is what a deployment runs.\n *\n * When there is no bundle (an ejected backend, or a project that has not adopted\n * `rebase.json`) this falls back to the backend workspace's own `start` script,\n * which is what such a project has always used.\n */\nimport fs from \"fs\";\nimport path from \"path\";\nimport chalk from \"chalk\";\nimport { execa } from \"execa\";\nimport { requireProjectRoot, findEnvFile, readEnvFile } from \"../utils/project\";\nimport { parseCommandArgs, wantsHelp } from \"../utils/args\";\nimport { detectPackageManager, getPMCommands, type PMCommands } from \"../utils/package-manager\";\nimport { DEFAULT_BUNDLE_DIR } from \"../bundle\";\nimport type { RebaseBundleStatic } from \"@rebasepro/types\";\nimport type { DevDatabase } from \"../dev-db/resolve\";\nimport type { DaemonState } from \"../dev-db/state\";\nimport { MANAGED_POOL_MAX } from \"../dev-db/constraints\";\n\nfunction printHelp(): void {\n console.log(`\n${chalk.bold(\"rebase start\")} — run a built bundle\n\n${chalk.bold(\"Usage\")}\n rebase start [options]\n\n${chalk.bold(\"Options\")}\n --bundle <dir> Bundle directory (default: ${DEFAULT_BUNDLE_DIR})\n --workspace Run the backend workspace's own start script\n -h, --help Show this help\n\nRuns the bundle the way a deployment does, reading PORT and the rest of .env,\nin whatever NODE_ENV the shell or .env sets. A scaffolded .env says\ndevelopment, which keeps development behaviour on — the first account to\nregister becomes the admin. For a production server, run it with\nNODE_ENV=production; it says so at start when it is not one.\n\nIt needs a DATABASE_URL, and starts no database of its own. To run it on the\ndatabase ${chalk.cyan(\"rebase dev\")} uses: ${chalk.cyan('DATABASE_URL=\"$(rebase db url)\" rebase start')}\n\nBuild first with ${chalk.cyan(\"rebase build\")}.\n`.trim());\n}\n\n/** The NODE_ENV the runtime will see, and where it comes from. */\nexport interface StartNodeEnv {\n value: string | undefined;\n source: \"shell\" | \".env\" | \"unset\";\n}\n\n/**\n * What NODE_ENV the server will boot in. The shell wins — dotenv never\n * overrides a variable that is already set — then `.env`.\n */\nexport function startNodeEnv(projectRoot: string): StartNodeEnv {\n if (process.env.NODE_ENV !== undefined) return { value: process.env.NODE_ENV, source: \"shell\" };\n const fromFile = readEnvFile(projectRoot).NODE_ENV;\n return fromFile !== undefined ? { value: fromFile, source: \".env\" } : { value: undefined, source: \"unset\" };\n}\n\n/**\n * The banner for a `rebase start` that is not a production server, or `null`.\n *\n * Not forced to production: the scaffold's `.env` points at a loopback\n * database, which a production boot refuses, so forcing it would break the\n * documented local run. What must not happen is the silence — a command the\n * help called \"(production)\" booting with the first-registration window open.\n */\nexport function nonProductionWarning(nodeEnv: StartNodeEnv): string[] | null {\n if (nodeEnv.value === \"production\") return null;\n const what = nodeEnv.value === undefined\n ? \"NODE_ENV is not set\"\n : `NODE_ENV=${nodeEnv.value}${nodeEnv.source === \".env\" ? \" (from .env)\" : \"\"}`;\n return [\n chalk.yellow.bold(`⚠ ${what} — this is not a production server.`),\n chalk.yellow(\" Development behaviour is on: the first account to register becomes the admin\"),\n chalk.yellow(\" (REBASE_ADMIN_EMAIL is ignored), and with no SMTP configured, sign-in and\"),\n chalk.yellow(\" password-reset links are printed to this log instead of sent.\"),\n chalk.gray(\" For a production server, run it with NODE_ENV=production (in the shell or .env).\")\n ];\n}\n\n/**\n * What to say about the apps a bundle serves only on their own hostname.\n *\n * The runtime prints \"Server running at http://localhost:<port>\", and an app\n * whose `path` names a hostname answers only requests carrying that `Host` —\n * so opening the printed URL showed a 404 for an app that was running fine,\n * and nothing said why. One line per such app, with a command that reaches it.\n */\nexport function hostRoutedNotice(staticApps: readonly RebaseBundleStatic[], port: string | undefined): string[] {\n const routed = staticApps.filter(app => app.host);\n if (routed.length === 0) return [];\n const origin = `http://localhost:${port || \"<port>\"}`;\n return [\n ...routed.map(app => {\n const name = app.name ?? path.posix.basename(app.dir);\n return chalk.yellow(`⚠ ${name} answers only on ${app.host}, so ${origin}${app.path} will not show it here. Reach it with:`)\n + `\\n ${chalk.cyan(`curl -H \"Host: ${app.host}\" ${origin}${app.path}`)}`;\n }),\n chalk.gray(` (or point ${routed.map(app => app.host).join(\", \")} at 127.0.0.1 in /etc/hosts)`),\n \"\"\n ];\n}\n\n/**\n * What to say when there is no DATABASE_URL for the runtime, or `null`.\n *\n * The runtime requires one, and its refusal — `DATABASE_URL: is required`, and\n * a link to the self-hosting docs — is right for a deployment and says nothing\n * to the reader most likely to see it: someone running the scaffold's own\n * `.env`, which leaves DATABASE_URL commented out because `rebase dev` brings a\n * database of its own. That database is one command away, so this names it.\n *\n * `kind` is what `rebase dev` runs this project on, so the line can say which\n * database `rebase db url` will print. A blank value is unset, as the runtime\n * reads it. `pm` spells the command: a scaffold has `rebase` in\n * `node_modules/.bin`, not on PATH, so it is `pnpm exec rebase …` or\n * `npx rebase …`, as in the CLI's other hints.\n */\nexport function missingDatabaseUrl(\n env: Record<string, string | undefined>,\n kind: DevDatabase[\"kind\"] | null,\n pm: PMCommands\n): string[] | null {\n if (env.DATABASE_URL?.trim()) return null;\n\n const rebase = (args: string[]): string => pm.exec(\"rebase\", args).join(\" \");\n\n const which = kind === \"docker\"\n ? \"the Docker database `rebase dev --docker` uses\"\n : kind === \"managed\"\n ? \"the managed development database (PGlite) `rebase dev` uses\"\n : \"this project's development database\";\n return [\n chalk.red(\"✗ DATABASE_URL is not set, so the runtime has no database to start against.\"),\n chalk.gray(\" `rebase start` runs the bundle the way a deployment does, against the database you name;\"),\n chalk.gray(\" `rebase dev` is the command that brings one of its own.\"),\n \"\",\n ` To run it on ${which}:`,\n ` ${chalk.cyan(`DATABASE_URL=\"$(${rebase([\"db\", \"url\"])})\" ${rebase([\"start\"])}`)}`,\n \"\",\n chalk.gray(\" For a deployment, set DATABASE_URL in the environment or in .env.\"),\n chalk.gray(\" See https://rebase.pro/docs/deployment/self-hosting/ for the variables a deployment needs.\")\n ];\n}\n\n/**\n * What the runtime needs when DATABASE_URL is this project's managed\n * development database, or nothing.\n *\n * The connection ceiling `rebase dev` sets for it (`MANAGED_POOL_MAX`). PGlite\n * is one session behind a socket server that accepts only a few connections,\n * and a running `rebase dev` holds some. With an ordinary pool, the boot's\n * concurrent probes opened more than were left, the ones over were refused,\n * and a probe that could not connect read as a table that was not there:\n * `❌ rebase.cron_claims is unavailable`, beside tables that existed.\n *\n * Recognised by the running daemon's ports, which is what `rebase db url`\n * prints from. A ceiling set by hand is kept.\n */\nexport function managedDatabaseRuntimeEnv(\n databaseUrl: string | undefined,\n daemon: DaemonState | null,\n env: Record<string, string | undefined>\n): Record<string, string> {\n if (!databaseUrl || !daemon || env.REBASE_DB_POOL_MAX !== undefined) return {};\n let url: URL;\n try {\n url = new URL(databaseUrl);\n } catch {\n return {};\n }\n if (![\"127.0.0.1\", \"localhost\", \"[::1]\"].includes(url.hostname)) return {};\n const ports = [daemon.port, ...Object.values(daemon.databases ?? {}).map(database => database.port)];\n if (!ports.includes(Number(url.port))) return {};\n return { REBASE_DB_POOL_MAX: String(MANAGED_POOL_MAX) };\n}\n\nfunction readBundleStatic(bundleDir: string): RebaseBundleStatic[] {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(bundleDir, \"manifest.json\"), \"utf8\")) as { entry?: { static?: RebaseBundleStatic[] } };\n return manifest.entry?.static ?? [];\n } catch {\n return [];\n }\n}\n\nfunction printNonProductionWarning(projectRoot: string): void {\n const warning = nonProductionWarning(startNodeEnv(projectRoot));\n if (!warning) return;\n console.log(warning.join(\"\\n\") + \"\\n\");\n}\n\nexport async function startCommand(rawArgs: string[] = []): Promise<void> {\n if (wantsHelp(rawArgs)) {\n printHelp();\n return;\n }\n\n const { flags: args } = parseCommandArgs({\n spec: {\n \"--bundle\": String,\n \"--workspace\": Boolean\n },\n rawArgs,\n commandWords: 1,\n command: \"start\",\n maxPositionals: 0\n });\n\n const projectRoot = requireProjectRoot();\n\n const envFile = findEnvFile(projectRoot);\n const env: Record<string, string> = { ...process.env as Record<string, string> };\n if (envFile) {\n env.DOTENV_CONFIG_PATH = envFile;\n }\n\n const bundleDir = path.resolve(projectRoot, args[\"--bundle\"] ?? DEFAULT_BUNDLE_DIR);\n const hasBundle = fs.existsSync(path.join(bundleDir, \"manifest.json\"));\n\n // Before either path, and before the runtime's own boot logs bury it.\n printNonProductionWarning(projectRoot);\n\n if (args[\"--workspace\"] || !hasBundle) {\n if (!args[\"--workspace\"] && !hasBundle) {\n console.log(chalk.dim(\n `No bundle at ${path.relative(projectRoot, bundleDir)}/ — ` +\n \"starting the backend workspace instead.\\n\"\n ));\n }\n await startWorkspaceBackend(projectRoot, env);\n return;\n }\n\n ensureBundleDependencies(projectRoot, bundleDir);\n\n console.log(`${chalk.bold(\"Rebase\")} — starting runtime from ${chalk.cyan(path.relative(projectRoot, bundleDir))}/\\n`);\n\n // Loaded in-process rather than spawned: the runtime is a library here, so\n // signals, exit codes and stdio need no forwarding, and there is one less\n // process between the developer and a stack trace.\n if (envFile && fs.existsSync(envFile)) {\n const dotenv = await import(\"dotenv\");\n dotenv.config({ path: envFile, quiet: true });\n }\n\n // Before the runtime, which would refuse with a message written for a\n // deployment. See `missingDatabaseUrl`.\n if (!process.env.DATABASE_URL?.trim()) {\n const { devDatabaseKind } = await import(\"../dev-db/prepare\");\n const missing = missingDatabaseUrl(process.env, devDatabaseKind(projectRoot), getPMCommands(detectPackageManager(projectRoot)));\n if (missing) {\n for (const line of missing) console.error(line);\n process.exit(1);\n }\n }\n\n // See `managedDatabaseRuntimeEnv`.\n const { findRunningDaemon } = await import(\"../dev-db/daemon\");\n const managedEnv = managedDatabaseRuntimeEnv(process.env.DATABASE_URL, await findRunningDaemon(projectRoot), process.env);\n if (managedEnv.REBASE_DB_POOL_MAX) {\n Object.assign(process.env, managedEnv);\n console.log(chalk.dim(\n \"DATABASE_URL is this project's managed development database: the runtime uses one connection,\\n\" +\n \"as `rebase dev` does, so requests are served one at a time.\\n\"\n ));\n }\n\n process.env.REBASE_BUNDLE = bundleDir;\n\n for (const line of hostRoutedNotice(readBundleStatic(bundleDir), process.env.PORT)) console.log(line);\n\n try {\n const { runFromBundle } = await import(\"@rebasepro/server\");\n await runFromBundle({ bundleDir });\n } catch (err) {\n console.error(chalk.red(\"\\n✗ Failed to start the runtime.\"));\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n}\n\n/**\n * Make a bundle's imports resolvable for a local run.\n *\n * Node resolves a module by walking up from the *importing file*, so compiled\n * code sitting in `dist-bundle/` no longer sees the per-package `node_modules`\n * its source could: pnpm and npm both install a workspace package's\n * dependencies inside that package, and the bundle is not inside any of them.\n *\n * A deployment solves this by installing the bundle's own `package.json` beside\n * it — that is what the generated `package.json` is for. Locally, doing a second\n * install to run code whose dependencies are already on disk would be wasteful,\n * so this links what is already there instead.\n *\n * Only ever created when absent, and only under the bundle directory, so a real\n * install always wins and nothing here is ever uploaded (`rebase build` cleans\n * the directory and never writes this).\n */\nfunction ensureBundleDependencies(projectRoot: string, bundleDir: string): void {\n const target = path.join(bundleDir, \"node_modules\");\n if (fs.existsSync(target)) return;\n\n // Later sources fill gaps left by earlier ones; the backend's tree wins\n // because it holds the server and driver the runtime itself needs.\n const sources = [\"backend/node_modules\", \"config/node_modules\", \"node_modules\"]\n .map(relative => path.join(projectRoot, relative))\n .filter(dir => fs.existsSync(dir));\n\n if (sources.length === 0) return;\n\n let linked = 0;\n fs.mkdirSync(target, { recursive: true });\n\n const linkInto = (sourceDir: string, targetDir: string): void => {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(sourceDir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name === \".bin\" || entry.name.startsWith(\".\")) continue;\n const from = path.join(sourceDir, entry.name);\n const to = path.join(targetDir, entry.name);\n\n // A scope is a directory of packages, not a package — merge into it\n // so `@a/one` from one tree and `@a/two` from another both resolve.\n if (entry.name.startsWith(\"@\") && entry.isDirectory()) {\n fs.mkdirSync(to, { recursive: true });\n linkInto(from, to);\n continue;\n }\n\n if (fs.existsSync(to)) continue;\n try {\n fs.symlinkSync(fs.realpathSync(from), to, \"junction\");\n linked++;\n } catch {\n // A package that cannot be linked simply stays unresolved, and\n // the runtime will say so by name if it actually needed it.\n }\n }\n };\n\n for (const source of sources) linkInto(source, target);\n\n if (linked > 0) {\n console.log(chalk.dim(\n ` linked ${linked} package(s) into the bundle for this local run\\n` +\n \" (a deployment installs the bundle's package.json instead)\\n\"\n ));\n }\n}\n\nasync function startWorkspaceBackend(projectRoot: string, env: Record<string, string>): Promise<void> {\n const pm = detectPackageManager(projectRoot);\n const cmds = getPMCommands(pm);\n const startCmd = cmds.runWorkspace(\"backend\", \"start\");\n\n console.log(`${chalk.bold(\"Rebase\")} — Starting backend server...\\n`);\n\n try {\n await execa(startCmd[0], startCmd.slice(1), {\n cwd: projectRoot,\n stdio: \"inherit\",\n env\n });\n } catch {\n console.error(chalk.red(\"\\n✗ Failed to start server.\"));\n process.exit(1);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"start-BmEZkS0n.js","names":[],"sources":["../src/commands/start.ts"],"sourcesContent":["/**\n * CLI command: rebase start\n *\n * Runs a built bundle through the Rebase runtime — the same path the official\n * container image takes, so what you test locally is what a deployment runs.\n *\n * When there is no bundle (an ejected backend, or a project that has not adopted\n * `rebase.json`) this falls back to the backend workspace's own `start` script,\n * which is what such a project has always used.\n */\nimport fs from \"fs\";\nimport path from \"path\";\nimport chalk from \"chalk\";\nimport { execa } from \"execa\";\nimport { requireProjectRoot, findEnvFile, readEnvFile } from \"../utils/project\";\nimport { parseCommandArgs, wantsHelp } from \"../utils/args\";\nimport { detectPackageManager, getPMCommands, type PMCommands } from \"../utils/package-manager\";\nimport { DEFAULT_BUNDLE_DIR } from \"../bundle\";\nimport type { RebaseBundleStatic } from \"@rebasepro/types\";\nimport type { DevDatabase } from \"../dev-db/resolve\";\nimport type { DaemonState } from \"../dev-db/state\";\nimport { MANAGED_POOL_MAX } from \"../dev-db/constraints\";\n\nfunction printHelp(): void {\n console.log(`\n${chalk.bold(\"rebase start\")} — run a built bundle\n\n${chalk.bold(\"Usage\")}\n rebase start [options]\n\n${chalk.bold(\"Options\")}\n --bundle <dir> Bundle directory (default: ${DEFAULT_BUNDLE_DIR})\n --workspace Run the backend workspace's own start script\n -h, --help Show this help\n\nRuns the bundle the way a deployment does, reading PORT and the rest of .env,\nin whatever NODE_ENV the shell or .env sets. A scaffolded .env says\ndevelopment, which keeps development behaviour on — the first account to\nregister becomes the admin. For a production server, run it with\nNODE_ENV=production; it says so at start when it is not one.\n\nIt needs a DATABASE_URL, and starts no database of its own. To run it on the\ndatabase ${chalk.cyan(\"rebase dev\")} uses: ${chalk.cyan('DATABASE_URL=\"$(rebase db url)\" rebase start')}\n\nBuild first with ${chalk.cyan(\"rebase build\")}.\n`.trim());\n}\n\n/** The NODE_ENV the runtime will see, and where it comes from. */\nexport interface StartNodeEnv {\n value: string | undefined;\n source: \"shell\" | \".env\" | \"unset\";\n}\n\n/**\n * What NODE_ENV the server will boot in. The shell wins — dotenv never\n * overrides a variable that is already set — then `.env`.\n */\nexport function startNodeEnv(projectRoot: string): StartNodeEnv {\n if (process.env.NODE_ENV !== undefined) return { value: process.env.NODE_ENV, source: \"shell\" };\n const fromFile = readEnvFile(projectRoot).NODE_ENV;\n return fromFile !== undefined ? { value: fromFile, source: \".env\" } : { value: undefined, source: \"unset\" };\n}\n\n/**\n * The banner for a `rebase start` that is not a production server, or `null`.\n *\n * Not forced to production: the scaffold's `.env` points at a loopback\n * database, which a production boot refuses, so forcing it would break the\n * documented local run. What must not happen is the silence — a command the\n * help called \"(production)\" booting with the first-registration window open.\n */\nexport function nonProductionWarning(nodeEnv: StartNodeEnv): string[] | null {\n if (nodeEnv.value === \"production\") return null;\n const what = nodeEnv.value === undefined\n ? \"NODE_ENV is not set\"\n : `NODE_ENV=${nodeEnv.value}${nodeEnv.source === \".env\" ? \" (from .env)\" : \"\"}`;\n return [\n chalk.yellow.bold(`⚠ ${what} — this is not a production server.`),\n chalk.yellow(\" Development behaviour is on: the first account to register becomes the admin\"),\n chalk.yellow(\" (REBASE_ADMIN_EMAIL is ignored), and with no SMTP configured, sign-in and\"),\n chalk.yellow(\" password-reset links are printed to this log instead of sent.\"),\n chalk.gray(\" For a production server, run it with NODE_ENV=production (in the shell or .env).\")\n ];\n}\n\n/**\n * What to say about the apps a bundle serves only on their own hostname.\n *\n * The runtime prints \"Server running at http://localhost:<port>\", and an app\n * whose `path` names a hostname answers only requests carrying that `Host` —\n * so opening the printed URL showed a 404 for an app that was running fine,\n * and nothing said why. One line per such app, with a command that reaches it.\n */\nexport function hostRoutedNotice(staticApps: readonly RebaseBundleStatic[], port: string | undefined): string[] {\n const routed = staticApps.filter(app => app.host);\n if (routed.length === 0) return [];\n const origin = `http://localhost:${port || \"<port>\"}`;\n return [\n ...routed.map(app => {\n const name = app.name ?? path.posix.basename(app.dir);\n return chalk.yellow(`⚠ ${name} answers only on ${app.host}, so ${origin}${app.path} will not show it here. Reach it with:`)\n + `\\n ${chalk.cyan(`curl -H \"Host: ${app.host}\" ${origin}${app.path}`)}`;\n }),\n chalk.gray(` (or point ${routed.map(app => app.host).join(\", \")} at 127.0.0.1 in /etc/hosts)`),\n \"\"\n ];\n}\n\n/**\n * What to say when there is no DATABASE_URL for the runtime, or `null`.\n *\n * The runtime requires one, and its refusal — `DATABASE_URL: is required`, and\n * a link to the self-hosting docs — is right for a deployment and says nothing\n * to the reader most likely to see it: someone running the scaffold's own\n * `.env`, which leaves DATABASE_URL commented out because `rebase dev` brings a\n * database of its own. That database is one command away, so this names it.\n *\n * `kind` is what `rebase dev` runs this project on, so the line can say which\n * database `rebase db url` will print. A blank value is unset, as the runtime\n * reads it. `pm` spells the command: a scaffold has `rebase` in\n * `node_modules/.bin`, not on PATH, so it is `pnpm exec rebase …` or\n * `npx rebase …`, as in the CLI's other hints.\n */\nexport function missingDatabaseUrl(\n env: Record<string, string | undefined>,\n kind: DevDatabase[\"kind\"] | null,\n pm: PMCommands\n): string[] | null {\n if (env.DATABASE_URL?.trim()) return null;\n\n const rebase = (args: string[]): string => pm.exec(\"rebase\", args).join(\" \");\n\n const which = kind === \"docker\"\n ? \"the Docker database `rebase dev --docker` uses\"\n : kind === \"managed\"\n ? \"the managed development database (PGlite) `rebase dev` uses\"\n : \"this project's development database\";\n return [\n chalk.red(\"✗ DATABASE_URL is not set, so the runtime has no database to start against.\"),\n chalk.gray(\" `rebase start` runs the bundle the way a deployment does, against the database you name;\"),\n chalk.gray(\" `rebase dev` is the command that brings one of its own.\"),\n \"\",\n ` To run it on ${which}:`,\n ` ${chalk.cyan(`DATABASE_URL=\"$(${rebase([\"db\", \"url\"])})\" ${rebase([\"start\"])}`)}`,\n \"\",\n chalk.gray(\" For a deployment, set DATABASE_URL in the environment or in .env.\"),\n chalk.gray(\" See https://rebase.pro/docs/deployment/self-hosting/ for the variables a deployment needs.\")\n ];\n}\n\n/**\n * What the runtime needs when DATABASE_URL is this project's managed\n * development database, or nothing.\n *\n * The connection ceiling `rebase dev` sets for it (`MANAGED_POOL_MAX`). PGlite\n * is one session behind a socket server that accepts only a few connections,\n * and a running `rebase dev` holds some. With an ordinary pool, the boot's\n * concurrent probes opened more than were left, the ones over were refused,\n * and a probe that could not connect read as a table that was not there:\n * `❌ rebase.cron_claims is unavailable`, beside tables that existed.\n *\n * Recognised by the running daemon's ports, which is what `rebase db url`\n * prints from. A ceiling set by hand is kept.\n */\nexport function managedDatabaseRuntimeEnv(\n databaseUrl: string | undefined,\n daemon: DaemonState | null,\n env: Record<string, string | undefined>\n): Record<string, string> {\n if (!databaseUrl || !daemon || env.REBASE_DB_POOL_MAX !== undefined) return {};\n let url: URL;\n try {\n url = new URL(databaseUrl);\n } catch {\n return {};\n }\n if (![\"127.0.0.1\", \"localhost\", \"[::1]\"].includes(url.hostname)) return {};\n const ports = [daemon.port, ...Object.values(daemon.databases ?? {}).map(database => database.port)];\n if (!ports.includes(Number(url.port))) return {};\n return { REBASE_DB_POOL_MAX: String(MANAGED_POOL_MAX) };\n}\n\nfunction readBundleStatic(bundleDir: string): RebaseBundleStatic[] {\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(bundleDir, \"manifest.json\"), \"utf8\")) as { entry?: { static?: RebaseBundleStatic[] } };\n return manifest.entry?.static ?? [];\n } catch {\n return [];\n }\n}\n\nfunction printNonProductionWarning(projectRoot: string): void {\n const warning = nonProductionWarning(startNodeEnv(projectRoot));\n if (!warning) return;\n console.log(warning.join(\"\\n\") + \"\\n\");\n}\n\nexport async function startCommand(rawArgs: string[] = []): Promise<void> {\n if (wantsHelp(rawArgs)) {\n printHelp();\n return;\n }\n\n const { flags: args } = parseCommandArgs({\n spec: {\n \"--bundle\": String,\n \"--workspace\": Boolean\n },\n rawArgs,\n commandWords: 1,\n command: \"start\",\n maxPositionals: 0\n });\n\n const projectRoot = requireProjectRoot();\n\n const envFile = findEnvFile(projectRoot);\n const env: Record<string, string> = { ...process.env as Record<string, string> };\n if (envFile) {\n env.DOTENV_CONFIG_PATH = envFile;\n }\n\n const bundleDir = path.resolve(projectRoot, args[\"--bundle\"] ?? DEFAULT_BUNDLE_DIR);\n const hasBundle = fs.existsSync(path.join(bundleDir, \"manifest.json\"));\n\n // Before either path, and before the runtime's own boot logs bury it.\n printNonProductionWarning(projectRoot);\n\n if (args[\"--workspace\"] || !hasBundle) {\n if (!args[\"--workspace\"] && !hasBundle) {\n console.log(chalk.dim(\n `No bundle at ${path.relative(projectRoot, bundleDir)}/ — ` +\n \"starting the backend workspace instead.\\n\"\n ));\n }\n await startWorkspaceBackend(projectRoot, env);\n return;\n }\n\n ensureBundleDependencies(projectRoot, bundleDir);\n\n console.log(`${chalk.bold(\"Rebase\")} — starting runtime from ${chalk.cyan(path.relative(projectRoot, bundleDir))}/\\n`);\n\n // Loaded in-process rather than spawned: the runtime is a library here, so\n // signals, exit codes and stdio need no forwarding, and there is one less\n // process between the developer and a stack trace.\n if (envFile && fs.existsSync(envFile)) {\n const dotenv = await import(\"dotenv\");\n dotenv.config({ path: envFile, quiet: true });\n }\n\n // Before the runtime, which would refuse with a message written for a\n // deployment. See `missingDatabaseUrl`.\n if (!process.env.DATABASE_URL?.trim()) {\n const { devDatabaseKind } = await import(\"../dev-db/prepare\");\n const missing = missingDatabaseUrl(process.env, devDatabaseKind(projectRoot), getPMCommands(detectPackageManager(projectRoot)));\n if (missing) {\n for (const line of missing) console.error(line);\n process.exit(1);\n }\n }\n\n // See `managedDatabaseRuntimeEnv`.\n const { findRunningDaemon } = await import(\"../dev-db/daemon\");\n const managedEnv = managedDatabaseRuntimeEnv(process.env.DATABASE_URL, await findRunningDaemon(projectRoot), process.env);\n if (managedEnv.REBASE_DB_POOL_MAX) {\n Object.assign(process.env, managedEnv);\n console.log(chalk.dim(\n \"DATABASE_URL is this project's managed development database: the runtime uses one connection,\\n\" +\n \"as `rebase dev` does, so requests are served one at a time.\\n\"\n ));\n }\n\n process.env.REBASE_BUNDLE = bundleDir;\n\n for (const line of hostRoutedNotice(readBundleStatic(bundleDir), process.env.PORT)) console.log(line);\n\n try {\n const { runFromBundle } = await import(\"@rebasepro/server\");\n await runFromBundle({ bundleDir });\n } catch (err) {\n console.error(chalk.red(\"\\n✗ Failed to start the runtime.\"));\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n}\n\n/**\n * Make a bundle's imports resolvable for a local run.\n *\n * Node resolves a module by walking up from the *importing file*, so compiled\n * code sitting in `dist-bundle/` no longer sees the per-package `node_modules`\n * its source could: pnpm and npm both install a workspace package's\n * dependencies inside that package, and the bundle is not inside any of them.\n *\n * A deployment solves this by installing the bundle's own `package.json` beside\n * it — that is what the generated `package.json` is for. Locally, doing a second\n * install to run code whose dependencies are already on disk would be wasteful,\n * so this links what is already there instead.\n *\n * Only ever created when absent, and only under the bundle directory, so a real\n * install always wins and nothing here is ever uploaded (`rebase build` cleans\n * the directory and never writes this).\n */\nfunction ensureBundleDependencies(projectRoot: string, bundleDir: string): void {\n const target = path.join(bundleDir, \"node_modules\");\n if (fs.existsSync(target)) return;\n\n // Later sources fill gaps left by earlier ones; the backend's tree wins\n // because it holds the server and driver the runtime itself needs.\n const sources = [\"backend/node_modules\", \"config/node_modules\", \"node_modules\"]\n .map(relative => path.join(projectRoot, relative))\n .filter(dir => fs.existsSync(dir));\n\n if (sources.length === 0) return;\n\n let linked = 0;\n fs.mkdirSync(target, { recursive: true });\n\n const linkInto = (sourceDir: string, targetDir: string): void => {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(sourceDir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (entry.name === \".bin\" || entry.name.startsWith(\".\")) continue;\n const from = path.join(sourceDir, entry.name);\n const to = path.join(targetDir, entry.name);\n\n // A scope is a directory of packages, not a package — merge into it\n // so `@a/one` from one tree and `@a/two` from another both resolve.\n if (entry.name.startsWith(\"@\") && entry.isDirectory()) {\n fs.mkdirSync(to, { recursive: true });\n linkInto(from, to);\n continue;\n }\n\n if (fs.existsSync(to)) continue;\n try {\n fs.symlinkSync(fs.realpathSync(from), to, \"junction\");\n linked++;\n } catch {\n // A package that cannot be linked simply stays unresolved, and\n // the runtime will say so by name if it actually needed it.\n }\n }\n };\n\n for (const source of sources) linkInto(source, target);\n\n if (linked > 0) {\n console.log(chalk.dim(\n ` linked ${linked} package(s) into the bundle for this local run\\n` +\n \" (a deployment installs the bundle's package.json instead)\\n\"\n ));\n }\n}\n\nasync function startWorkspaceBackend(projectRoot: string, env: Record<string, string>): Promise<void> {\n const pm = detectPackageManager(projectRoot);\n const cmds = getPMCommands(pm);\n const startCmd = cmds.runWorkspace(\"backend\", \"start\");\n\n console.log(`${chalk.bold(\"Rebase\")} — Starting backend server...\\n`);\n\n try {\n await execa(startCmd[0], startCmd.slice(1), {\n cwd: projectRoot,\n stdio: \"inherit\",\n env\n });\n } catch {\n console.error(chalk.red(\"\\n✗ Failed to start server.\"));\n process.exit(1);\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { d as readEnvFile, i as failAsJson, p as requireProjectRoot } from "./project-Wp2zoyeN.js";
2
- import { n as parseCommandArgs, r as wantsHelp } from "./args-CcBphFdj.js";
2
+ import { n as parseCommandArgs, r as wantsHelp } from "./args-BX0YD14a.js";
3
3
  import { d as findBackendApp, g as resolveBackendPaths, o as ManifestError, p as loadManifest } from "./manifest-DRp0UVTj.js";
4
- import { c as withImplicitDefaults, n as deriveOptionsFor, o as readResourceGraphFile, r as deriveResourceGraph, s as serializeResourceGraph, t as RESOURCE_GRAPH_FILENAME } from "./derive-Dl0z2OWi.js";
5
- import { a as parseLoopbackDsn, o as probeTcp } from "./dev-preflight-CorMg7Ub.js";
4
+ import { c as withImplicitDefaults, n as deriveOptionsFor, o as readResourceGraphFile, r as deriveResourceGraph, s as serializeResourceGraph, t as RESOURCE_GRAPH_FILENAME } from "./derive-CsoFGAnK.js";
5
+ import { a as parseLoopbackDsn, o as probeTcp } from "./dev-preflight-unHpiYO1.js";
6
6
  import { i as managedUrl, r as findRunningDaemon } from "./daemon-CknkDjd6.js";
7
- import { resolveProjectDatabaseAndBranch } from "./prepare-C1iaiTC4.js";
8
- import { describeTarget } from "./pull-COpNJFzY.js";
7
+ import { resolveProjectDatabaseAndBranch } from "./prepare-DdnU70z8.js";
8
+ import { describeTarget } from "./pull-C3PXduzl.js";
9
9
  import chalk from "chalk";
10
10
  import fs from "fs";
11
11
  import path from "path";
@@ -315,7 +315,7 @@ async function statusDatabase(projectRoot, envFile) {
315
315
  return {
316
316
  dockerDatabase: {
317
317
  url: database.url,
318
- detail: which + ", as the last `rebase dev --docker` · " + describeTarget(database.url) + (listening ? "" : " — not running; `rebase dev --docker` starts it")
318
+ detail: which + ", as the last `rebase dev --docker` did · " + describeTarget(database.url) + (listening ? "" : " — not running; `rebase dev --docker` starts it")
319
319
  },
320
320
  branch,
321
321
  ignoredBranch
@@ -418,9 +418,9 @@ async function statusCommand(rawArgs) {
418
418
  console.log(` ${chalk.red("✗")} ${chalk.red(blocked)}`);
419
419
  }
420
420
  const committed = readResourceGraphFile(projectRoot);
421
- if (committed !== serializeResourceGraph(graph) && !(committed === null && graph.resources.length === 0)) {
421
+ if (committed !== null && committed !== serializeResourceGraph(graph)) {
422
422
  console.log("");
423
- console.log(` ${chalk.yellow("○")} ${RESOURCE_GRAPH_FILENAME} is ${committed === null ? "missing" : "stale"} — ${chalk.blue("rebase resources --write")}`);
423
+ console.log(` ${chalk.yellow("○")} ${RESOURCE_GRAPH_FILENAME} is stale — ${chalk.blue("rebase resources --write")}`);
424
424
  console.log(chalk.gray(" a host reads it to decide what to provision before it runs anything"));
425
425
  }
426
426
  console.log("");
@@ -428,4 +428,4 @@ async function statusCommand(rawArgs) {
428
428
  //#endregion
429
429
  export { statusCommand };
430
430
 
431
- //# sourceMappingURL=status-D_B3tlDy.js.map
431
+ //# sourceMappingURL=status-BJ26yZOo.js.map