@rebasepro/cli 0.24.0 → 0.24.1-canary.g56af4a4

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 (118) hide show
  1. package/bin/rebase.js +3 -0
  2. package/dist/agent-setup-B31F00DC.js +324 -0
  3. package/dist/agent-setup-B31F00DC.js.map +1 -0
  4. package/dist/api-keys-BPBi5nOF.js +524 -0
  5. package/dist/api-keys-BPBi5nOF.js.map +1 -0
  6. package/dist/apps-CPXNElX4.js +256 -0
  7. package/dist/apps-CPXNElX4.js.map +1 -0
  8. package/dist/args-BnmsEXvu.js +109 -0
  9. package/dist/args-BnmsEXvu.js.map +1 -0
  10. package/dist/atlas-dev-database-DTMuEScS.js +62 -0
  11. package/dist/atlas-dev-database-DTMuEScS.js.map +1 -0
  12. package/dist/auth-DvY94ZuL.js +395 -0
  13. package/dist/auth-DvY94ZuL.js.map +1 -0
  14. package/dist/branch-pointer-DIhKbQLp.js +2 -0
  15. package/dist/build-CFwUx-Ff.js +336 -0
  16. package/dist/build-CFwUx-Ff.js.map +1 -0
  17. package/dist/bundle-BQhP_xiA.js +1617 -0
  18. package/dist/bundle-BQhP_xiA.js.map +1 -0
  19. package/dist/cli.d.ts +2 -2
  20. package/dist/cloud-CkTr-yt0.js +9640 -0
  21. package/dist/cloud-CkTr-yt0.js.map +1 -0
  22. package/dist/collection-drift-BKj_BoMq.js +106 -0
  23. package/dist/collection-drift-BKj_BoMq.js.map +1 -0
  24. package/dist/commands/api-keys.d.ts +2 -0
  25. package/dist/commands/db.d.ts +37 -24
  26. package/dist/commands/dev.d.ts +103 -0
  27. package/dist/commands/generate_sdk.d.ts +26 -0
  28. package/dist/commands/skills.d.ts +22 -7
  29. package/dist/commands/storage.d.ts +46 -0
  30. package/dist/consent-BWHaYx5p.js +76 -0
  31. package/dist/consent-BWHaYx5p.js.map +1 -0
  32. package/dist/context-BqhRydBg.js +1234 -0
  33. package/dist/context-BqhRydBg.js.map +1 -0
  34. package/dist/daemon-DBn-shxX.js +2 -0
  35. package/dist/{daemon-entry-LTFKtpHy.js → daemon-entry-Da-D3rmH.js} +3 -4
  36. package/dist/daemon-entry-Da-D3rmH.js.map +1 -0
  37. package/dist/db-CMpBK3sv.js +1252 -0
  38. package/dist/db-CMpBK3sv.js.map +1 -0
  39. package/dist/derive-RPIsAVO4.js +474 -0
  40. package/dist/derive-RPIsAVO4.js.map +1 -0
  41. package/dist/dev-C9P5Yp9k.js +1593 -0
  42. package/dist/dev-C9P5Yp9k.js.map +1 -0
  43. package/dist/dev-db/atlas-dev-database.d.ts +32 -0
  44. package/dist/dev-db/daemon-entry.d.ts +13 -0
  45. package/dist/dev-preflight-N1evJL3S.js +2 -0
  46. package/dist/doctor-DYs-NqFI.js +771 -0
  47. package/dist/doctor-DYs-NqFI.js.map +1 -0
  48. package/dist/eject-BT6t37D0.js +335 -0
  49. package/dist/eject-BT6t37D0.js.map +1 -0
  50. package/dist/fold-static-C1EtHlfV.js +201 -0
  51. package/dist/fold-static-C1EtHlfV.js.map +1 -0
  52. package/dist/function-portability-BIvlvR9q.js +265 -0
  53. package/dist/function-portability-BIvlvR9q.js.map +1 -0
  54. package/dist/generate_sdk-UV635zSe.js +477 -0
  55. package/dist/generate_sdk-UV635zSe.js.map +1 -0
  56. package/dist/index.es.js +141 -24674
  57. package/dist/index.es.js.map +1 -1
  58. package/dist/init-fXeBcxuN.js +1042 -0
  59. package/dist/init-fXeBcxuN.js.map +1 -0
  60. package/dist/manifest-DzlC60WX.js +871 -0
  61. package/dist/manifest-DzlC60WX.js.map +1 -0
  62. package/dist/normalize-imports-CmVdE9iZ.js +100 -0
  63. package/dist/normalize-imports-CmVdE9iZ.js.map +1 -0
  64. package/dist/{package-manager-CXQevsE1.js → package-manager-DSEOwTqM.js} +2 -2
  65. package/dist/{package-manager-CXQevsE1.js.map → package-manager-DSEOwTqM.js.map} +1 -1
  66. package/dist/path-flags-DUBuI2i1.js +77 -0
  67. package/dist/path-flags-DUBuI2i1.js.map +1 -0
  68. package/dist/precompress.d.ts +17 -0
  69. package/dist/prepare-Btn9Ewbn.js +1052 -0
  70. package/dist/prepare-Btn9Ewbn.js.map +1 -0
  71. package/dist/{project-DmaGLmH2.js → project-Wp2zoyeN.js} +33 -4
  72. package/dist/project-Wp2zoyeN.js.map +1 -0
  73. package/dist/resolve-CJLkfFV0.js +2 -0
  74. package/dist/resources-DT1COn_6.js +161 -0
  75. package/dist/resources-DT1COn_6.js.map +1 -0
  76. package/dist/schema-BDBWvCHu.js +125 -0
  77. package/dist/schema-BDBWvCHu.js.map +1 -0
  78. package/dist/skills-DTFibT1m.js +642 -0
  79. package/dist/skills-DTFibT1m.js.map +1 -0
  80. package/dist/start-rU2e3Kp5.js +228 -0
  81. package/dist/start-rU2e3Kp5.js.map +1 -0
  82. package/dist/{state-C59Elrnt.js → state-Do7ZJYkF.js} +49 -4
  83. package/dist/state-Do7ZJYkF.js.map +1 -0
  84. package/dist/status-BR9oyi-y.js +351 -0
  85. package/dist/status-BR9oyi-y.js.map +1 -0
  86. package/dist/storage-DyvrSTH3.js +303 -0
  87. package/dist/storage-DyvrSTH3.js.map +1 -0
  88. package/dist/telemetry-CAWNCugs.js +338 -0
  89. package/dist/telemetry-CAWNCugs.js.map +1 -0
  90. package/dist/telemetry-DbLqGIBW.js +129 -0
  91. package/dist/telemetry-DbLqGIBW.js.map +1 -0
  92. package/dist/unknown-command-DNwDcX3w.js +98 -0
  93. package/dist/unknown-command-DNwDcX3w.js.map +1 -0
  94. package/dist/upgrade-D-5-Od96.js +1265 -0
  95. package/dist/upgrade-D-5-Od96.js.map +1 -0
  96. package/dist/utils/project.d.ts +16 -0
  97. package/dist/{version-DsgBmP22.js → version-Buyyo1ut.js} +2 -2
  98. package/dist/{version-DsgBmP22.js.map → version-Buyyo1ut.js.map} +1 -1
  99. package/package.json +7 -7
  100. package/templates/eject/backend/src/env.ts +1 -1
  101. package/templates/eject/backend/src/index.ts +10 -2
  102. package/templates/template/README.md +1 -1
  103. package/templates/template/ai-instructions.md +2 -1
  104. package/templates/template/frontend/vite.config.ts +11 -3
  105. package/templates/template/scripts/example.ts +2 -1
  106. package/dist/branch-pointer-kk9F5FBO.js +0 -111
  107. package/dist/branch-pointer-kk9F5FBO.js.map +0 -1
  108. package/dist/constraints-DKGbNfUW.js +0 -48
  109. package/dist/constraints-DKGbNfUW.js.map +0 -1
  110. package/dist/daemon-C8p32i-n.js +0 -357
  111. package/dist/daemon-C8p32i-n.js.map +0 -1
  112. package/dist/daemon-entry-LTFKtpHy.js.map +0 -1
  113. package/dist/dev-preflight-CNLl4rdo.js +0 -367
  114. package/dist/dev-preflight-CNLl4rdo.js.map +0 -1
  115. package/dist/project-DmaGLmH2.js.map +0 -1
  116. package/dist/resolve-BAeRUBhg.js +0 -74
  117. package/dist/resolve-BAeRUBhg.js.map +0 -1
  118. package/dist/state-C59Elrnt.js.map +0 -1
@@ -0,0 +1,642 @@
1
+ import { c as findProjectRoot } from "./project-Wp2zoyeN.js";
2
+ import { n as parseCommandArgs, r as wantsHelp, t as UsageError } from "./args-BnmsEXvu.js";
3
+ import { t as unknownCommand } from "./unknown-command-DNwDcX3w.js";
4
+ import chalk from "chalk";
5
+ import fs from "fs";
6
+ import os from "os";
7
+ import path from "path";
8
+ import { createHash } from "crypto";
9
+ import { createRequire } from "module";
10
+ import inquirer from "inquirer";
11
+ //#region src/commands/skills.ts
12
+ var require = createRequire(import.meta.url);
13
+ /**
14
+ * Supported agent environments and their target directories.
15
+ *
16
+ * Every agent `rebase init` writes a pointer file for has an entry here. That
17
+ * was not true: `rebase init` writes `CLAUDE.md`, `AGENTS.md`, `.cursorrules`,
18
+ * `.windsurfrules` and `.github/copilot-instructions.md`, and the installer
19
+ * covered four of the five — so a Codex or Copilot user was told, by a file in
20
+ * their own repository, to run an installer that had nothing to offer them.
21
+ *
22
+ * `flatLayout` marks the agents whose rules directory is loaded *whole*, into
23
+ * every request. Writing 21 skills there put ~84,000 characters of always-on
24
+ * context in front of every question a person asked Cursor — more than the task
25
+ * itself, on every turn, most of it about parts of Rebase they were not
26
+ * touching. Those agents get one short index rule instead, and the skill bodies
27
+ * go in per-skill subdirectories the index names, which the agent reads when it
28
+ * needs one. `indexFile` is the name of that rule.
29
+ *
30
+ * A subdirectory layout has no such problem: the rule file already sits beside
31
+ * its own assets, so `references/x.md` resolves as written and nothing is
32
+ * loaded until it is opened.
33
+ *
34
+ * `homeMarkers` are the directories under `~` that say the assistant is
35
+ * installed on this machine. They answer a different question from
36
+ * `detectDir`: a fresh scaffold has no `.claude/` of its own, so `rebase init`
37
+ * asks the machine which assistants the developer has and pre-selects those.
38
+ *
39
+ * `mcp` is where the assistant reads a *project-level* MCP server list, and in
40
+ * which shape — see `agent-setup.ts`. `projectDir` is what `REBASE_PROJECT_DIR`
41
+ * is set to: `"."` where the server is spawned in the project, the editor's own
42
+ * workspace variable where it may not be. `null` means there is no project
43
+ * file to write: Windsurf reads only a user-level config, and one entry there
44
+ * cannot name every project on the machine.
45
+ */
46
+ var AGENTS = {
47
+ cursor: {
48
+ label: "Cursor",
49
+ detectDir: ".cursor",
50
+ homeMarkers: [".cursor"],
51
+ targetDir: ".cursor/rules",
52
+ flatLayout: true,
53
+ indexFile: "rebase.mdc",
54
+ mcp: {
55
+ file: ".cursor/mcp.json",
56
+ format: "mcpServers",
57
+ projectDir: "${workspaceFolder}"
58
+ },
59
+ /** The body is not a rule file here — `indexFile` is. */
60
+ transformFile: (skillName, content) => ({
61
+ fileName: path.join(skillName, "SKILL.md"),
62
+ content
63
+ })
64
+ },
65
+ claude: {
66
+ label: "Claude Code",
67
+ detectDir: ".claude",
68
+ homeMarkers: [".claude"],
69
+ targetDir: ".claude/skills",
70
+ flatLayout: false,
71
+ mcp: {
72
+ file: ".mcp.json",
73
+ format: "mcpServers",
74
+ projectDir: "."
75
+ },
76
+ /** Claude Code uses the standard SKILL.md format in subdirectories. */
77
+ transformFile: (skillName, content) => ({
78
+ fileName: path.join(skillName, "SKILL.md"),
79
+ content
80
+ })
81
+ },
82
+ windsurf: {
83
+ label: "Windsurf",
84
+ detectDir: ".windsurf",
85
+ homeMarkers: [".codeium/windsurf", ".windsurf"],
86
+ targetDir: ".windsurf/rules",
87
+ flatLayout: true,
88
+ indexFile: "rebase.md",
89
+ mcp: null,
90
+ /** The body is not a rule file here — `indexFile` is. */
91
+ transformFile: (skillName, content) => ({
92
+ fileName: path.join(skillName, "SKILL.md"),
93
+ content
94
+ })
95
+ },
96
+ gemini: {
97
+ label: "Gemini CLI / Antigravity",
98
+ detectDir: ".agents",
99
+ homeMarkers: [".gemini"],
100
+ targetDir: ".agents/skills",
101
+ flatLayout: false,
102
+ mcp: {
103
+ file: ".gemini/settings.json",
104
+ format: "mcpServers",
105
+ projectDir: "."
106
+ },
107
+ /** Gemini uses the standard SKILL.md format in subdirectories. */
108
+ transformFile: (skillName, content) => ({
109
+ fileName: path.join(skillName, "SKILL.md"),
110
+ content
111
+ })
112
+ },
113
+ codex: {
114
+ label: "Codex CLI",
115
+ detectDir: ".codex",
116
+ homeMarkers: [".codex"],
117
+ targetDir: ".agents/skills",
118
+ flatLayout: false,
119
+ mcp: {
120
+ file: ".codex/config.toml",
121
+ format: "codexToml",
122
+ projectDir: "."
123
+ },
124
+ /** Codex uses the standard SKILL.md format in subdirectories. */
125
+ transformFile: (skillName, content) => ({
126
+ fileName: path.join(skillName, "SKILL.md"),
127
+ content
128
+ })
129
+ },
130
+ kiro: {
131
+ label: "Kiro",
132
+ detectDir: ".kiro",
133
+ homeMarkers: [".kiro"],
134
+ targetDir: ".kiro/steering",
135
+ flatLayout: true,
136
+ indexFile: "rebase.md",
137
+ mcp: {
138
+ file: ".kiro/settings/mcp.json",
139
+ format: "mcpServers",
140
+ projectDir: "."
141
+ },
142
+ /** The body is not a rule file here — `indexFile` is. */
143
+ transformFile: (skillName, content) => ({
144
+ fileName: path.join(skillName, "SKILL.md"),
145
+ content
146
+ })
147
+ },
148
+ copilot: {
149
+ label: "GitHub Copilot",
150
+ detectDir: null,
151
+ homeMarkers: [],
152
+ targetDir: ".github/instructions",
153
+ flatLayout: true,
154
+ indexFile: "rebase.instructions.md",
155
+ mcp: {
156
+ file: ".vscode/mcp.json",
157
+ format: "vscode",
158
+ projectDir: "${workspaceFolder}"
159
+ },
160
+ /** The body is not a rule file here — `indexFile` is. */
161
+ transformFile: (skillName, content) => ({
162
+ fileName: path.join(skillName, "SKILL.md"),
163
+ content
164
+ })
165
+ }
166
+ };
167
+ /** Narrows a string to an agent name, by the table's own keys. */
168
+ function isAgentKey(value) {
169
+ return Object.hasOwn(AGENTS, value);
170
+ }
171
+ /** Every agent, in table order. */
172
+ var AGENT_KEYS = Object.keys(AGENTS).filter(isAgentKey);
173
+ /**
174
+ * Resolve the path to the skills directory from @rebasepro/agent-skills.
175
+ * Works in both workspace (symlink) and published (real files) layouts.
176
+ */
177
+ function getSkillsSourceDir() {
178
+ const pkgJsonPath = require.resolve("@rebasepro/agent-skills/package.json");
179
+ const pkgRoot = path.dirname(pkgJsonPath);
180
+ const skillsDir = path.join(pkgRoot, "skills");
181
+ if (!fs.existsSync(skillsDir)) throw new Error(`Skills directory not found at ${skillsDir}. Make sure @rebasepro/agent-skills is installed.`);
182
+ return skillsDir;
183
+ }
184
+ /**
185
+ * Everything a skill ships alongside its SKILL.md — the `references/` tree the
186
+ * Agent Skills format uses for progressive disclosure.
187
+ *
188
+ * These used to be dropped on install, because the installer read exactly
189
+ * `<skill>/SKILL.md` and nothing else. That left `rebase-design-language`
190
+ * telling the agent three separate times to read `references/view-patterns.md`
191
+ * — 379 lines of view skeletons — in a project where the file had never
192
+ * landed, and the instruction it carries is "extend an existing pattern; do not
193
+ * invent a layout".
194
+ */
195
+ function loadSkillAssets(skillDir) {
196
+ const found = [];
197
+ const walk = (dir, prefix) => {
198
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
199
+ if (entry.name.startsWith(".")) continue;
200
+ const rel = prefix ? path.join(prefix, entry.name) : entry.name;
201
+ if (entry.isDirectory()) walk(path.join(dir, entry.name), rel);
202
+ else if (rel !== "SKILL.md") found.push(rel);
203
+ }
204
+ };
205
+ walk(skillDir, "");
206
+ return found.sort();
207
+ }
208
+ /** Read all skill directories and return their names, content and assets. */
209
+ function loadSkills(skillsDir) {
210
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
211
+ const skills = [];
212
+ for (const entry of entries) {
213
+ if (!entry.isDirectory()) continue;
214
+ const skillDir = path.join(skillsDir, entry.name);
215
+ const skillMdPath = path.join(skillDir, "SKILL.md");
216
+ if (!fs.existsSync(skillMdPath)) continue;
217
+ skills.push({
218
+ name: entry.name,
219
+ dir: skillDir,
220
+ content: fs.readFileSync(skillMdPath, "utf-8"),
221
+ assets: loadSkillAssets(skillDir)
222
+ });
223
+ }
224
+ return skills;
225
+ }
226
+ /**
227
+ * The skills this CLI ships, from its own `@rebasepro/agent-skills`.
228
+ *
229
+ * Throws rather than exiting, so `rebase init` can report a missing bundle as a
230
+ * warning and still finish the scaffold.
231
+ */
232
+ function loadBundledSkills() {
233
+ const skillsDir = getSkillsSourceDir();
234
+ const skills = loadSkills(skillsDir);
235
+ if (skills.length === 0) throw new Error(`No skills found in ${skillsDir}`);
236
+ return skills;
237
+ }
238
+ /**
239
+ * The one always-on rule a flat-layout agent gets, in place of every skill.
240
+ *
241
+ * Cursor and Windsurf load their whole rules directory into each request. With
242
+ * a rule file per skill that was ~84,000 characters of Rebase reference in
243
+ * front of every question, whether or not the question was about Rebase — and
244
+ * an instruction an assistant skims is an instruction it does not follow.
245
+ *
246
+ * So the always-on part is this: a table of what exists and where to read it.
247
+ * `alwaysApply: true` because knowing the skills are there costs a few hundred
248
+ * characters and is the whole point; the bodies stay on disk until the agent
249
+ * opens one.
250
+ */
251
+ function renderSkillIndex(skills, agentKey) {
252
+ const agent = AGENTS[agentKey];
253
+ const bodyPath = (name) => agent.transformFile(name, "").fileName.split(path.sep).join("/");
254
+ const rows = skills.map((skill) => `| \`${skill.name}\` | ${describe(skill)} | \`${bodyPath(skill.name)}\` |`).join("\n");
255
+ return [
256
+ "---",
257
+ "description: Rebase — the skills index. Read the file named for the task before writing Rebase code.",
258
+ "alwaysApply: true",
259
+ "---",
260
+ "",
261
+ "# Rebase skills",
262
+ "",
263
+ `This project uses [Rebase](https://rebase.pro). ${skills.length} reference skills are`,
264
+ "installed beside this file. **Read the one that covers the task before writing",
265
+ "code** — they are the difference between a collection that compiles and one that",
266
+ "does not.",
267
+ "",
268
+ "| Skill | Covers | Read |",
269
+ "|---|---|---|",
270
+ rows,
271
+ "",
272
+ "Start with `rebase-basics` if you are not sure: it opens with the recipes for",
273
+ "adding a collection, a function and an RLS rule, and names the commands.",
274
+ ""
275
+ ].join("\n");
276
+ }
277
+ /**
278
+ * The body of a YAML block scalar, given the offset of its `description:` line.
279
+ *
280
+ * Everything indented more than the key belongs to the value; the first line
281
+ * that is not blank and not indented ends it.
282
+ */
283
+ function blockScalarBody(frontMatter, keyOffset) {
284
+ const lines = frontMatter.slice(keyOffset).split("\n").slice(1);
285
+ const body = [];
286
+ for (const line of lines) {
287
+ if (line.trim() === "") {
288
+ body.push("");
289
+ continue;
290
+ }
291
+ if (!/^[ \t]/.test(line)) break;
292
+ body.push(line.trim());
293
+ }
294
+ return body.join(" ");
295
+ }
296
+ /**
297
+ * A skill's one-line summary, from its own front matter.
298
+ *
299
+ * Read rather than written here so a new skill needs no edit in this file, and
300
+ * truncated because the index is only worth having while it stays short.
301
+ */
302
+ function describe(skill) {
303
+ const frontMatter = skill.content.split("---")[1] ?? "";
304
+ const described = /^description:[ \t]*(.*)$/m.exec(frontMatter);
305
+ const text = (/^[|>][-+]?$/.test((described?.[1] ?? "").trim()) ? blockScalarBody(frontMatter, described?.index ?? 0) : described?.[1] ?? "").trim().replace(/\s+/g, " ").replace(/\|/g, "\\|");
306
+ const firstSentence = /^.*?\.(?:\s|$)/.exec(text)?.[0]?.trim() ?? text;
307
+ const fallback = skill.name.replace(/^rebase-/, "").replace(/-/g, " ");
308
+ if (!firstSentence) return fallback;
309
+ if (firstSentence.length <= 140) return firstSentence;
310
+ const cut = firstSentence.slice(0, 140);
311
+ return `${cut.slice(0, cut.lastIndexOf(" ")).replace(/[,;:]$/, "")}…`;
312
+ }
313
+ /**
314
+ * Detect which agent environments already exist in the project.
315
+ *
316
+ * A `detectDir` has to be evidence *the user* created, not something a scaffold
317
+ * ships. An agent whose only marker fails that test declares `detectDir: null`
318
+ * and is reachable by `--agent` alone; guessing wrong here is worse than not
319
+ * guessing, because the wrong guess installs 21 files and reports success.
320
+ */
321
+ function detectAgents(projectDir) {
322
+ return AGENT_KEYS.filter((key) => {
323
+ const { detectDir } = AGENTS[key];
324
+ return detectDir !== null && fs.existsSync(path.join(projectDir, detectDir));
325
+ });
326
+ }
327
+ /**
328
+ * Which assistants are installed on this machine, from their home directories.
329
+ *
330
+ * The question `rebase init` needs answered, and one `detectAgents` cannot: a
331
+ * project that does not exist yet has no `.claude/` in it. What the machine has
332
+ * is only a default — the developer confirms it — so a false positive costs an
333
+ * unticked box, not 21 files nobody asked for.
334
+ */
335
+ function detectInstalledAgents(home = os.homedir()) {
336
+ return AGENT_KEYS.map((key) => {
337
+ const agent = AGENTS[key];
338
+ const markers = agent.homeMarkers;
339
+ const hit = markers.find((m) => fs.existsSync(path.join(home, m)));
340
+ const shown = hit ?? markers[0];
341
+ return {
342
+ key,
343
+ label: agent.label,
344
+ marker: shown === void 0 ? null : `~/${shown}`,
345
+ found: hit !== void 0
346
+ };
347
+ });
348
+ }
349
+ /** Install skills for a specific agent into the project directory. */
350
+ /**
351
+ * The file in each target directory that records what the installer wrote:
352
+ * relative path → sha256 of the bytes written. A dotfile, so no agent loads it
353
+ * as a rule.
354
+ */
355
+ var SKILLS_MANIFEST = ".rebase-skills.json";
356
+ function sha256(bytes) {
357
+ return createHash("sha256").update(bytes).digest("hex");
358
+ }
359
+ function readManifest(targetBase) {
360
+ try {
361
+ const parsed = JSON.parse(fs.readFileSync(path.join(targetBase, SKILLS_MANIFEST), "utf-8"));
362
+ if (parsed && typeof parsed === "object" && "files" in parsed) {
363
+ const files = parsed.files;
364
+ if (files && typeof files === "object") {
365
+ const entries = Object.entries(files).filter((e) => typeof e[1] === "string");
366
+ return {
367
+ version: 1,
368
+ files: Object.fromEntries(entries)
369
+ };
370
+ }
371
+ }
372
+ } catch {}
373
+ return null;
374
+ }
375
+ function installForAgent(agentKey, skills, projectDir, options = {}) {
376
+ const agent = AGENTS[agentKey];
377
+ const targetBase = path.join(projectDir, agent.targetDir);
378
+ fs.mkdirSync(targetBase, { recursive: true });
379
+ const previous = readManifest(targetBase);
380
+ const recorded = {};
381
+ const kept = [];
382
+ const place = (relPath, bytes) => {
383
+ const target = path.join(targetBase, relPath);
384
+ const wanted = sha256(bytes);
385
+ const last = previous?.files[relPath];
386
+ if (fs.existsSync(target)) {
387
+ const current = sha256(fs.readFileSync(target));
388
+ if (current === wanted) {
389
+ recorded[relPath] = wanted;
390
+ return;
391
+ }
392
+ if (last !== void 0 && current !== last && !options.force) {
393
+ kept.push(relPath);
394
+ recorded[relPath] = last;
395
+ return;
396
+ }
397
+ }
398
+ fs.mkdirSync(path.dirname(target), { recursive: true });
399
+ fs.writeFileSync(target, bytes);
400
+ recorded[relPath] = wanted;
401
+ };
402
+ let count = 0;
403
+ let assetCount = 0;
404
+ for (const skill of skills) {
405
+ const { fileName, content } = agent.transformFile(skill.name, skill.content);
406
+ place(fileName, content);
407
+ count++;
408
+ for (const asset of skill.assets) {
409
+ place(path.join(skill.name, asset), fs.readFileSync(path.join(skill.dir, asset)));
410
+ assetCount++;
411
+ }
412
+ }
413
+ if (agent.flatLayout && agent.indexFile) place(agent.indexFile, renderSkillIndex(skills, agentKey));
414
+ const removed = [];
415
+ for (const [relPath, last] of Object.entries(previous?.files ?? {})) {
416
+ if (relPath in recorded) continue;
417
+ const target = path.join(targetBase, relPath);
418
+ if (!fs.existsSync(target)) continue;
419
+ if (sha256(fs.readFileSync(target)) !== last && !options.force) {
420
+ kept.push(relPath);
421
+ continue;
422
+ }
423
+ fs.rmSync(target);
424
+ removed.push(relPath);
425
+ for (let dir = path.dirname(target); dir.startsWith(targetBase + path.sep); dir = path.dirname(dir)) {
426
+ if (fs.readdirSync(dir).length > 0) break;
427
+ fs.rmdirSync(dir);
428
+ }
429
+ }
430
+ fs.writeFileSync(path.join(targetBase, SKILLS_MANIFEST), JSON.stringify({
431
+ version: 1,
432
+ files: recorded
433
+ }, null, 2) + "\n", "utf-8");
434
+ return {
435
+ skills: count,
436
+ assets: assetCount,
437
+ kept,
438
+ removed
439
+ };
440
+ }
441
+ /**
442
+ * Install for several agents, writing each target directory once.
443
+ *
444
+ * Gemini and Codex both read `.agents/skills`; installing twice would write the
445
+ * same 21 files twice and report two installs of one directory.
446
+ */
447
+ function installSkills(agents, skills, projectDir, options = {}) {
448
+ const written = /* @__PURE__ */ new Map();
449
+ return agents.map((agent) => {
450
+ const earlier = written.get(AGENTS[agent].targetDir);
451
+ if (earlier) return {
452
+ ...earlier,
453
+ agent,
454
+ sharedWith: earlier.agent
455
+ };
456
+ const result = {
457
+ agent,
458
+ ...installForAgent(agent, skills, projectDir, options)
459
+ };
460
+ written.set(AGENTS[agent].targetDir, result);
461
+ return result;
462
+ });
463
+ }
464
+ /** Everything the switch below dispatches, for the did-you-mean. */
465
+ var SKILLS_SUBCOMMANDS = ["install"];
466
+ async function skillsCommand(subcommand, rawArgs) {
467
+ if (wantsHelp(rawArgs)) {
468
+ printSkillsHelp();
469
+ return;
470
+ }
471
+ switch (subcommand) {
472
+ case "install":
473
+ await skillsInstall(rawArgs);
474
+ break;
475
+ case "--help":
476
+ case void 0:
477
+ printSkillsHelp();
478
+ break;
479
+ default: unknownCommand(subcommand, SKILLS_SUBCOMMANDS, "skills");
480
+ }
481
+ }
482
+ /** `rebase skills install`'s whole line: the agents it names, `--mcp` and `--force`. */
483
+ function parseSkillsInstallLine(rawArgs) {
484
+ const { flags } = parseCommandArgs({
485
+ spec: {
486
+ "--agent": [String],
487
+ "-a": "--agent",
488
+ "--mcp": Boolean,
489
+ "--force": Boolean
490
+ },
491
+ rawArgs,
492
+ commandWords: 2,
493
+ command: "skills install",
494
+ maxPositionals: 0
495
+ });
496
+ return {
497
+ agents: resolveAgentNames(flags["--agent"] ?? []),
498
+ mcp: flags["--mcp"] === true,
499
+ force: flags["--force"] === true
500
+ };
501
+ }
502
+ /**
503
+ * The agents a list of `--agent` values names, or null when it names none.
504
+ *
505
+ * Shared by `rebase skills install` and `rebase init`, so both accept the same
506
+ * spellings. Throws a UsageError on a name that is not an agent.
507
+ */
508
+ function resolveAgentNames(values) {
509
+ const requested = values.flatMap((value) => value.split(",").map((v) => v.trim())).filter(Boolean);
510
+ if (requested.length === 0) return null;
511
+ if (requested.includes("all")) return [...AGENT_KEYS];
512
+ const unknown = requested.filter((a) => !isAgentKey(a));
513
+ if (unknown.length > 0) throw new UsageError(`Unknown agent(s): ${unknown.join(", ")}. Available: ${AGENT_KEYS.join(", ")}, all`);
514
+ return [...new Set(requested.filter(isAgentKey))];
515
+ }
516
+ async function skillsInstall(rawArgs = []) {
517
+ const { agents: namedAgents, mcp: withMcp, force } = parseSkillsInstallLine(rawArgs);
518
+ const projectDir = findProjectRoot() ?? process.cwd();
519
+ let skills;
520
+ try {
521
+ skills = loadBundledSkills();
522
+ } catch (err) {
523
+ console.error(`${chalk.red.bold("ERROR")} ${err instanceof Error ? err.message : String(err)}`);
524
+ process.exit(1);
525
+ }
526
+ let agents = namedAgents ?? detectAgents(projectDir);
527
+ if (agents.length === 0) {
528
+ if (!process.stdin.isTTY) {
529
+ console.error(chalk.red("Cannot prompt: this is a non-interactive terminal (no TTY)."));
530
+ console.error(chalk.yellow(` Name the agents explicitly, e.g. rebase skills install --agent ${Object.keys(AGENTS)[0]}`));
531
+ console.error(chalk.yellow(" Or install for every supported agent: rebase skills install --agent all"));
532
+ console.error(chalk.gray(` Available: ${Object.keys(AGENTS).join(", ")}`));
533
+ process.exit(1);
534
+ }
535
+ const choices = detectInstalledAgents().map(({ key, label, found }) => ({
536
+ name: label,
537
+ value: key,
538
+ checked: found
539
+ }));
540
+ const { selectedAgents } = await inquirer.prompt([{
541
+ type: "checkbox",
542
+ name: "selectedAgents",
543
+ message: "No AI agent configuration in this project. Which agents do you use?",
544
+ choices,
545
+ validate: (input) => {
546
+ if (input.length === 0) return "Please select at least one agent.";
547
+ return true;
548
+ }
549
+ }]);
550
+ agents = AGENT_KEYS.filter((key) => selectedAgents.includes(key));
551
+ }
552
+ console.log("");
553
+ console.log(chalk.gray(` Found ${chalk.white(skills.length)} Rebase skills`));
554
+ console.log("");
555
+ let anyKept = false;
556
+ for (const result of installSkills(agents, skills, projectDir, { force })) {
557
+ const agent = AGENTS[result.agent];
558
+ const shown = path.relative(process.cwd(), path.join(projectDir, agent.targetDir)) || agent.targetDir;
559
+ if (result.sharedWith) {
560
+ console.log(` ${chalk.green("✓")} ${chalk.bold(agent.label)} — reads the same ${chalk.gray(shown)} as ${AGENTS[result.sharedWith].label}`);
561
+ continue;
562
+ }
563
+ const withAssets = result.assets > 0 ? ` (+ ${result.assets} reference file${result.assets === 1 ? "" : "s"})` : "";
564
+ console.log(` ${chalk.green("✓")} ${chalk.bold(agent.label)} — ${result.skills} skills installed${withAssets} to ${chalk.gray(shown)}`);
565
+ for (const file of result.removed) console.log(chalk.gray(` − removed ${path.join(shown, file)} (no longer in the bundle)`));
566
+ for (const file of result.kept) {
567
+ anyKept = true;
568
+ console.log(chalk.yellow(` ! kept ${path.join(shown, file)}: edited since it was installed`));
569
+ }
570
+ }
571
+ if (anyKept) {
572
+ console.log(chalk.yellow(" Edited files were left as they are. To replace them with the bundle's copy:"));
573
+ console.log(` ${chalk.cyan("rebase skills install --force")}`);
574
+ }
575
+ const { MCP_SERVER_NAME, ensureMcpDependency, mcpDependencyNote, mcpServerRegistered, writeMcpConfig } = await import("./agent-setup-B31F00DC.js");
576
+ const { detectPackageManager } = await import("./package-manager-DSEOwTqM.js").then((n) => n.r);
577
+ const shownFile = (file) => path.relative(process.cwd(), path.join(projectDir, file)) || file;
578
+ if (withMcp) {
579
+ console.log("");
580
+ const pm = detectPackageManager(projectDir);
581
+ let registered = false;
582
+ for (const agentKey of agents) {
583
+ const agent = AGENTS[agentKey];
584
+ const result = writeMcpConfig(agentKey, projectDir, pm);
585
+ if (result && result.status !== "unreadable") registered = true;
586
+ if (!result) console.log(chalk.gray(` · ${agent.label} has no project-level MCP config — add @rebasepro/mcp in its MCP settings.`));
587
+ else if (result.status === "added") console.log(` ${chalk.green("✓")} ${chalk.bold(agent.label)} — MCP server in ${chalk.gray(shownFile(result.file))}`);
588
+ else if (result.status === "present") console.log(` ${chalk.green("✓")} ${chalk.bold(agent.label)} — MCP server already in ${chalk.gray(shownFile(result.file))}`);
589
+ else 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.`));
590
+ }
591
+ const note = registered ? mcpDependencyNote(ensureMcpDependency(projectDir), pm) : void 0;
592
+ if (note) console.log(chalk.yellow(` ${note}`));
593
+ }
594
+ console.log("");
595
+ console.log(chalk.gray(" Skills are project-local. Commit them to share with your team."));
596
+ console.log(chalk.gray(" Re-run this command to restore them. Upgrade @rebasepro/cli for newer skills."));
597
+ if (!withMcp) {
598
+ const missing = agents.filter((agentKey) => mcpServerRegistered(agentKey, projectDir) === false);
599
+ if (missing.length > 0) {
600
+ console.log(chalk.gray(" To register the Rebase MCP server for them too, as `rebase init --agent` does:"));
601
+ console.log(` ${chalk.cyan(`rebase skills install --agent ${missing.join(",")} --mcp`)}`);
602
+ }
603
+ }
604
+ console.log("");
605
+ }
606
+ function printSkillsHelp() {
607
+ console.log(`
608
+ ${chalk.bold("rebase skills")} — Manage AI agent skills
609
+
610
+ ${chalk.green.bold("Usage")}
611
+ rebase skills ${chalk.blue("<subcommand>")}
612
+
613
+ ${chalk.green.bold("Subcommands")}
614
+ ${chalk.blue.bold("install")} Install Rebase agent skills for your AI coding assistant
615
+ Supports: ${Object.values(AGENTS).map((a) => a.label).join(", ")}
616
+
617
+ ${chalk.green.bold("Options")}
618
+ ${chalk.blue("--force")} Replace installed skill files you edited, and remove edited files of
619
+ skills the bundle no longer ships. Without it, edits are kept.
620
+ ${chalk.blue("--mcp")} Also register the Rebase MCP server in each agent's project config
621
+ (.mcp.json, .cursor/mcp.json, …), as ${chalk.cyan("rebase init --agent")} does.
622
+ Servers already in the file are kept.
623
+ ${chalk.blue("--agent, -a")} Agent(s) to install for, skipping detection and the prompt.
624
+ Repeat the flag or pass a comma-separated list, or ${chalk.bold("all")}.
625
+ Required without a TTY: detection looks for an agent's own
626
+ directory (${Object.values(AGENTS).filter((a) => a.detectDir).map((a) => a.detectDir).join(", ")}),
627
+ and a fresh scaffold has none. GitHub Copilot is never detected
628
+ — ${chalk.gray(".github/")} is not evidence anyone uses it — so name it.
629
+ Available: ${Object.keys(AGENTS).join(", ")}, all
630
+
631
+ ${chalk.green.bold("Examples")}
632
+ ${chalk.cyan("rebase skills install")}
633
+ ${chalk.cyan("rebase skills install --agent claude")}
634
+ ${chalk.cyan("rebase skills install --agent claude,cursor")}
635
+ ${chalk.cyan("rebase skills install --agent cursor --mcp")} ${chalk.gray("# skills + the MCP server")}
636
+ ${chalk.cyan("rebase skills install --agent all")} ${chalk.gray("# scripted / CI")}
637
+ `);
638
+ }
639
+ //#endregion
640
+ export { AGENTS, AGENT_KEYS, SKILLS_SUBCOMMANDS, detectInstalledAgents, installSkills, loadBundledSkills, resolveAgentNames, skillsCommand };
641
+
642
+ //# sourceMappingURL=skills-DTFibT1m.js.map