@rebasepro/cli 0.24.0 → 0.24.1-canary.gcb918fc
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.
- package/bin/rebase.js +3 -0
- package/dist/agent-setup-B31F00DC.js +324 -0
- package/dist/agent-setup-B31F00DC.js.map +1 -0
- package/dist/api-keys-BPBi5nOF.js +524 -0
- package/dist/api-keys-BPBi5nOF.js.map +1 -0
- package/dist/apps-CPXNElX4.js +256 -0
- package/dist/apps-CPXNElX4.js.map +1 -0
- package/dist/args-BnmsEXvu.js +109 -0
- package/dist/args-BnmsEXvu.js.map +1 -0
- package/dist/atlas-dev-database-DTMuEScS.js +62 -0
- package/dist/atlas-dev-database-DTMuEScS.js.map +1 -0
- package/dist/auth-DvY94ZuL.js +395 -0
- package/dist/auth-DvY94ZuL.js.map +1 -0
- package/dist/branch-pointer-DIhKbQLp.js +2 -0
- package/dist/build-BHvC6Bjs.js +336 -0
- package/dist/build-BHvC6Bjs.js.map +1 -0
- package/dist/bundle-BQhP_xiA.js +1617 -0
- package/dist/bundle-BQhP_xiA.js.map +1 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cloud-K6P5IHUb.js +9667 -0
- package/dist/cloud-K6P5IHUb.js.map +1 -0
- package/dist/collection-drift-BKj_BoMq.js +106 -0
- package/dist/collection-drift-BKj_BoMq.js.map +1 -0
- package/dist/commands/api-keys.d.ts +2 -0
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/db.d.ts +64 -24
- package/dist/commands/dev.d.ts +103 -0
- package/dist/commands/generate_sdk.d.ts +26 -0
- package/dist/commands/skills.d.ts +22 -7
- package/dist/commands/storage.d.ts +46 -0
- package/dist/consent-BWHaYx5p.js +76 -0
- package/dist/consent-BWHaYx5p.js.map +1 -0
- package/dist/console-Cs00E-dt.js +102 -0
- package/dist/console-Cs00E-dt.js.map +1 -0
- package/dist/context-BqhRydBg.js +1234 -0
- package/dist/context-BqhRydBg.js.map +1 -0
- package/dist/daemon-DBn-shxX.js +2 -0
- package/dist/{daemon-entry-LTFKtpHy.js → daemon-entry-Da-D3rmH.js} +3 -4
- package/dist/daemon-entry-Da-D3rmH.js.map +1 -0
- package/dist/db-YbPMFY4Y.js +1252 -0
- package/dist/db-YbPMFY4Y.js.map +1 -0
- package/dist/derive-QMPMcQZJ.js +484 -0
- package/dist/derive-QMPMcQZJ.js.map +1 -0
- package/dist/dev-CJ_dd9uP.js +1593 -0
- package/dist/dev-CJ_dd9uP.js.map +1 -0
- package/dist/dev-db/atlas-dev-database.d.ts +32 -0
- package/dist/dev-db/daemon-entry.d.ts +13 -0
- package/dist/dev-preflight-N1evJL3S.js +2 -0
- package/dist/doctor-CIcpWl_L.js +771 -0
- package/dist/doctor-CIcpWl_L.js.map +1 -0
- package/dist/eject-BT6t37D0.js +335 -0
- package/dist/eject-BT6t37D0.js.map +1 -0
- package/dist/fold-static-C1EtHlfV.js +201 -0
- package/dist/fold-static-C1EtHlfV.js.map +1 -0
- package/dist/function-portability-BIvlvR9q.js +265 -0
- package/dist/function-portability-BIvlvR9q.js.map +1 -0
- package/dist/generate_sdk-UV635zSe.js +477 -0
- package/dist/generate_sdk-UV635zSe.js.map +1 -0
- package/dist/index.es.js +150 -24674
- package/dist/index.es.js.map +1 -1
- package/dist/init-fXeBcxuN.js +1042 -0
- package/dist/init-fXeBcxuN.js.map +1 -0
- package/dist/manifest-DzlC60WX.js +871 -0
- package/dist/manifest-DzlC60WX.js.map +1 -0
- package/dist/normalize-imports-CmVdE9iZ.js +100 -0
- package/dist/normalize-imports-CmVdE9iZ.js.map +1 -0
- package/dist/{package-manager-CXQevsE1.js → package-manager-DSEOwTqM.js} +2 -2
- package/dist/{package-manager-CXQevsE1.js.map → package-manager-DSEOwTqM.js.map} +1 -1
- package/dist/path-flags-DUBuI2i1.js +77 -0
- package/dist/path-flags-DUBuI2i1.js.map +1 -0
- package/dist/precompress.d.ts +17 -0
- package/dist/prepare-Btn9Ewbn.js +1052 -0
- package/dist/prepare-Btn9Ewbn.js.map +1 -0
- package/dist/{project-DmaGLmH2.js → project-Wp2zoyeN.js} +33 -4
- package/dist/project-Wp2zoyeN.js.map +1 -0
- package/dist/resolve-CJLkfFV0.js +2 -0
- package/dist/resources-3J3UQg5r.js +161 -0
- package/dist/resources-3J3UQg5r.js.map +1 -0
- package/dist/schema-ovO8qJDw.js +125 -0
- package/dist/schema-ovO8qJDw.js.map +1 -0
- package/dist/skills-DTFibT1m.js +642 -0
- package/dist/skills-DTFibT1m.js.map +1 -0
- package/dist/start-rU2e3Kp5.js +228 -0
- package/dist/start-rU2e3Kp5.js.map +1 -0
- package/dist/{state-C59Elrnt.js → state-Do7ZJYkF.js} +49 -4
- package/dist/state-Do7ZJYkF.js.map +1 -0
- package/dist/status-Cfy0TtSb.js +351 -0
- package/dist/status-Cfy0TtSb.js.map +1 -0
- package/dist/storage-DyvrSTH3.js +303 -0
- package/dist/storage-DyvrSTH3.js.map +1 -0
- package/dist/telemetry-CAWNCugs.js +338 -0
- package/dist/telemetry-CAWNCugs.js.map +1 -0
- package/dist/telemetry-DbLqGIBW.js +129 -0
- package/dist/telemetry-DbLqGIBW.js.map +1 -0
- package/dist/unknown-command-DNwDcX3w.js +98 -0
- package/dist/unknown-command-DNwDcX3w.js.map +1 -0
- package/dist/upgrade-D-5-Od96.js +1265 -0
- package/dist/upgrade-D-5-Od96.js.map +1 -0
- package/dist/utils/project.d.ts +16 -0
- package/dist/utils/temp-archive.d.ts +9 -0
- package/dist/{version-DsgBmP22.js → version-Buyyo1ut.js} +2 -2
- package/dist/{version-DsgBmP22.js.map → version-Buyyo1ut.js.map} +1 -1
- package/package.json +7 -7
- package/templates/eject/backend/src/env.ts +1 -1
- package/templates/eject/backend/src/index.ts +10 -2
- package/templates/template/README.md +1 -1
- package/templates/template/ai-instructions.md +2 -1
- package/templates/template/frontend/vite.config.ts +13 -3
- package/templates/template/scripts/example.ts +2 -1
- package/dist/branch-pointer-kk9F5FBO.js +0 -111
- package/dist/branch-pointer-kk9F5FBO.js.map +0 -1
- package/dist/constraints-DKGbNfUW.js +0 -48
- package/dist/constraints-DKGbNfUW.js.map +0 -1
- package/dist/daemon-C8p32i-n.js +0 -357
- package/dist/daemon-C8p32i-n.js.map +0 -1
- package/dist/daemon-entry-LTFKtpHy.js.map +0 -1
- package/dist/dev-preflight-CNLl4rdo.js +0 -367
- package/dist/dev-preflight-CNLl4rdo.js.map +0 -1
- package/dist/project-DmaGLmH2.js.map +0 -1
- package/dist/resolve-BAeRUBhg.js +0 -74
- package/dist/resolve-BAeRUBhg.js.map +0 -1
- 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
|