@teckedd-code2save/b2dp 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Edward Twumasi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # AI Build Tools
2
+
3
+ A collection of AI build tools and skills that help you do things faster.
4
+
5
+ ## Datafy MCP Server Setup
6
+
7
+ The `@teckedd-code2save/datafy` Model Context Protocol (MCP) server allows AI assistants to interact with your databases, extract schema information, execute SQL, and generate code.
8
+
9
+ To use the Datafy MCP server, you need to configure your AI assistant with the following MCP settings:
10
+
11
+ ```json
12
+ "datafy": {
13
+ "command": "npx",
14
+ "args": [
15
+ "@teckedd-code2save/datafy@latest",
16
+ "--config",
17
+ "/path/to/your/dbhub.toml",
18
+ "--transport",
19
+ "stdio"
20
+ ]
21
+ }
22
+ ```
23
+
24
+ *(Note: Replace `/path/to/your/dbhub.toml` with the absolute path to your `dbhub.toml` configuration file).*
25
+
26
+ The server requires a `dbhub.toml` configuration file to define your data sources. Here is an example:
27
+
28
+ ```toml
29
+ [sources.pet_market]
30
+ type = "postgres"
31
+ url = "postgres://user:pass@localhost:5432/pet_market"
32
+ description = "Pet market database"
33
+
34
+ [sources.ride_sharing]
35
+ type = "postgres"
36
+ url = "postgres://user:pass@localhost:5432/ride_sharing"
37
+ description = "Ride sharing database"
38
+
39
+ [sources.session_storage]
40
+ type = "redis"
41
+ url = "redis://localhost:6379"
42
+ description = "Redis for session storage"
43
+
44
+ [sources.logs_and_analytics]
45
+ type = "elasticsearch"
46
+ host = "localhost"
47
+ port = 9200
48
+ lazy = true
49
+ description = "Elasticsearch for logs and analytics"
50
+ ```
51
+
52
+ ### Adding to Claude Desktop
53
+
54
+ 1. Open Claude Desktop Settings -> Developer
55
+ 2. Click "Edit Config"
56
+ 3. Add the `datafy` server configuration to the `mcpServers` object in your `claude_desktop_config.json`:
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "datafy": {
62
+ "command": "npx",
63
+ "args": [
64
+ "@teckedd-code2save/datafy@latest",
65
+ "--config",
66
+ "/path/to/your/dbhub.toml",
67
+ "--transport",
68
+ "stdio"
69
+ ]
70
+ }
71
+ }
72
+ }
73
+ ```
74
+ 4. Restart Claude Desktop.
75
+
76
+ ### Adding to Cursor
77
+
78
+ 1. Open Cursor Settings -> Features -> MCP
79
+ 2. Click "+ Add New MCP Server"
80
+ 3. Name: `datafy`
81
+ 4. Type: `command`
82
+ 5. Command: `npx @teckedd-code2save/datafy@latest --config /path/to/your/dbhub.toml --transport stdio`
83
+ 6. Click Save and reload the window (or restart Cursor).
84
+
85
+ ### Adding to VS Code / Windsurf
86
+
87
+ 1. Follow the respective editor's MCP integration guides to add a standard stdio-based MCP server.
88
+ 2. Provide the exact same `npx` command and arguments as shown above.
89
+ 3. Reload the editor window to ensure the new MCP server is detected.
90
+
91
+ ### Adding to Gemini CLI / Antigravity
92
+
93
+ For command-line tools and agents that support `mcp_config.json` (like Antigravity or Gemini CLI extensions):
94
+ 1. For **Gemini CLI**, open or create `~/.gemini/settings.json`.
95
+ 2. For **Antigravity**, open or create `~/.gemini/antigravity/mcp_config.json`.
96
+ 3. Add the `datafy` server configuration inside the `mcpServers` key:
97
+
98
+ ```json
99
+ {
100
+ "mcpServers": {
101
+ "datafy": {
102
+ "command": "npx",
103
+ "args": [
104
+ "@teckedd-code2save/datafy@latest",
105
+ "--config",
106
+ "/path/to/your/dbhub.toml",
107
+ "--transport",
108
+ "stdio"
109
+ ]
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ ## Including the `business-to-data-platform` Skill
116
+
117
+ The `business-to-data-platform` skill transforms any business description into a production-grade database, schema, repository code, and data seeded through Datafy.
118
+
119
+ If you want to use this skill, ensure the `business-to-data-platform/SKILL.md` file is placed in the specific directory monitored by your AI assistants:
120
+ - **Claude**: Place skills in `~/.claude/skills/`
121
+ - **Cursor**: Embed skill logic inside `.cursorrules` or project-specific system prompts.
122
+ - **Gemini CLI**: Place skills in `~/.gemini/skills/`
123
+ - **Antigravity**: Place skills in `~/.gemini/antigravity/skills/` or `.agent/workflows/` in your workspace.
124
+
125
+ ## Full Dependency Stack (The Orchestrator Ecosystem)
126
+
127
+ The `business-to-data-platform` skill acts as an **Orchestrator**. To achieve the full production-grade workflow (automated design, testing, UI generation, and IaC provisioning), you should also include the following sibling skills and MCP servers:
128
+
129
+ ### Mandatory Sibling Skills
130
+ - **cloud-solution-architect**: Designs the Docker/K8s + GitHub Actions stack.
131
+ - **api-test-generator**: Generates comprehensive integration tests for the scaffolded backend.
132
+ - **frontend-data-consumer**: Scaffolds Vite/Next.js components using Tailwind CSS and Shadcn/UI for the frontend.
133
+ - **infrastructure-as-code-architect**: Generates Dockerfiles, K8s manifests, and GitHub Actions workflows.
134
+
135
+ ### Integrated MCP Servers
136
+ - **Datafy MCP** (`@teckedd-code2save/datafy`): Required for all database operations and code generation.
137
+ - **Context7 MCP**: (Optional) Fetches latest best practices and patterns for the chosen stack.
138
+ - **Prisma MCP**: (Optional) For migrations and database exploration if using Prisma.
139
+ - **GitHub MCP**: (Optional) For repository discovery and CI/CD setup.
package/dist/index.js ADDED
@@ -0,0 +1,694 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import figlet from "figlet";
6
+ import pc5 from "picocolors";
7
+
8
+ // src/constants.ts
9
+ var VERSION = "1.0.0";
10
+ var ORCHESTRATOR_SKILL = "business-to-data-platform";
11
+ var ALL_SKILLS = [
12
+ "business-to-data-platform",
13
+ "cloud-solution-architect",
14
+ "api-test-generator",
15
+ "frontend-data-consumer",
16
+ "frontend-design-review",
17
+ "infrastructure-as-code-architect",
18
+ "context7-mcp"
19
+ ];
20
+ var SKILL_DESCRIPTIONS = {
21
+ "business-to-data-platform": "Orchestrator \u2014 converts any business spec into a production-grade data platform",
22
+ "cloud-solution-architect": "Designs Docker/K8s + GitHub Actions cloud architectures",
23
+ "api-test-generator": "Generates comprehensive integration tests for scaffolded backends",
24
+ "frontend-data-consumer": "Scaffolds Vite/Next.js components with Tailwind CSS and Shadcn/UI",
25
+ "frontend-design-review": "Reviews and creates distinctive, production-grade frontend interfaces",
26
+ "infrastructure-as-code-architect": "Generates Dockerfiles, K8s manifests, and GitHub Actions workflows",
27
+ "context7-mcp": "Fetches up-to-date docs and patterns for any library or framework"
28
+ };
29
+ var RULE_FILENAME = "b2dp.md";
30
+
31
+ // src/commands/setup.ts
32
+ import pc2 from "picocolors";
33
+ import ora from "ora";
34
+ import { checkbox, input } from "@inquirer/prompts";
35
+ import { writeFile as writeFile3 } from "fs/promises";
36
+ import { join as join3 } from "path";
37
+
38
+ // src/utils/logger.ts
39
+ import pc from "picocolors";
40
+ var log = {
41
+ info: (msg) => console.log(`${pc.cyan("\u2139")} ${msg}`),
42
+ success: (msg) => console.log(`${pc.green("\u2714")} ${msg}`),
43
+ warn: (msg) => console.log(`${pc.yellow("\u26A0")} ${msg}`),
44
+ error: (msg) => console.error(`${pc.red("\u2716")} ${msg}`),
45
+ dim: (msg) => console.log(pc.dim(msg)),
46
+ blank: () => console.log("")
47
+ };
48
+
49
+ // src/utils/fs.ts
50
+ import { access, mkdir } from "fs/promises";
51
+ async function pathExists(p) {
52
+ try {
53
+ await access(p);
54
+ return true;
55
+ } catch {
56
+ return false;
57
+ }
58
+ }
59
+ async function ensureDir(dir) {
60
+ await mkdir(dir, { recursive: true });
61
+ }
62
+
63
+ // src/setup/agents.ts
64
+ import { join } from "path";
65
+ import { homedir } from "os";
66
+ var agents = {
67
+ // Antigravity / Gemini CLI โ€” ~/.gemini/antigravity/
68
+ antigravity: {
69
+ name: "antigravity",
70
+ displayName: "Antigravity (Gemini CLI)",
71
+ skillsDir: (scope) => scope === "global" ? join(homedir(), ".gemini", "antigravity", "skills") : join(".agent", "skills"),
72
+ rulesDir: (scope) => scope === "global" ? join(homedir(), ".gemini", "antigravity", "rules") : join(".agent", "rules"),
73
+ mcpConfigPath: (scope) => scope === "global" ? join(homedir(), ".gemini", "antigravity", "mcp_config.json") : join(".agent", "mcp_config.json"),
74
+ mcpConfigKey: "mcpServers",
75
+ detect: {
76
+ projectPaths: [".agent"],
77
+ globalPaths: [join(homedir(), ".gemini", "antigravity")]
78
+ }
79
+ },
80
+ // Claude Code โ€” ~/.claude/
81
+ claude: {
82
+ name: "claude",
83
+ displayName: "Claude Code",
84
+ skillsDir: (scope) => scope === "global" ? join(homedir(), ".claude", "skills") : join(".claude", "skills"),
85
+ rulesDir: (scope) => scope === "global" ? join(homedir(), ".claude", "rules") : join(".claude", "rules"),
86
+ mcpConfigPath: (scope) => scope === "global" ? join(homedir(), ".claude.json") : join(".mcp.json"),
87
+ mcpConfigKey: "mcpServers",
88
+ detect: {
89
+ projectPaths: [".mcp.json", ".claude"],
90
+ globalPaths: [join(homedir(), ".claude")]
91
+ }
92
+ },
93
+ // Cursor โ€” ~/.cursor/
94
+ cursor: {
95
+ name: "cursor",
96
+ displayName: "Cursor",
97
+ skillsDir: (scope) => scope === "global" ? join(homedir(), ".cursor", "skills") : join(".cursor", "skills"),
98
+ rulesDir: (scope) => scope === "global" ? join(homedir(), ".cursor", "rules") : join(".cursor", "rules"),
99
+ mcpConfigPath: (scope) => scope === "global" ? join(homedir(), ".cursor", "mcp.json") : join(".cursor", "mcp.json"),
100
+ mcpConfigKey: "mcpServers",
101
+ detect: {
102
+ projectPaths: [".cursor"],
103
+ globalPaths: [join(homedir(), ".cursor")]
104
+ }
105
+ },
106
+ // VS Code โ€” uses .vscode/ for project-level config
107
+ vscode: {
108
+ name: "vscode",
109
+ displayName: "VS Code (Copilot)",
110
+ skillsDir: (scope) => scope === "global" ? join(homedir(), ".vscode", "skills") : join(".vscode", "skills"),
111
+ rulesDir: (scope) => scope === "global" ? join(homedir(), ".vscode", "rules") : join(".vscode", "rules"),
112
+ mcpConfigPath: (scope) => scope === "global" ? join(homedir(), ".vscode", "mcp.json") : join(".vscode", "mcp.json"),
113
+ mcpConfigKey: "servers",
114
+ detect: {
115
+ projectPaths: [".vscode"],
116
+ globalPaths: [join(homedir(), ".vscode")]
117
+ }
118
+ },
119
+ // Gemini CLI โ€” ~/.gemini/settings.json
120
+ gemini: {
121
+ name: "gemini",
122
+ displayName: "Gemini CLI",
123
+ skillsDir: (scope) => scope === "global" ? join(homedir(), ".gemini", "skills") : join(".agent", "skills"),
124
+ rulesDir: (scope) => scope === "global" ? join(homedir(), ".gemini", "rules") : join(".agent", "rules"),
125
+ mcpConfigPath: (scope) => scope === "global" ? join(homedir(), ".gemini", "settings.json") : join(".agent", "settings.json"),
126
+ mcpConfigKey: "mcpServers",
127
+ detect: {
128
+ projectPaths: [".agent"],
129
+ globalPaths: [join(homedir(), ".gemini", "settings.json")]
130
+ }
131
+ }
132
+ };
133
+ function getAgent(name) {
134
+ return agents[name];
135
+ }
136
+ var ALL_AGENT_NAMES = Object.keys(agents);
137
+ var AGENT_DISPLAY_NAMES = {
138
+ antigravity: "Antigravity (Gemini CLI)",
139
+ claude: "Claude Code",
140
+ cursor: "Cursor",
141
+ vscode: "VS Code (Copilot)",
142
+ gemini: "Gemini CLI"
143
+ };
144
+ async function detectAgents(scope) {
145
+ const detected = [];
146
+ for (const name of ALL_AGENT_NAMES) {
147
+ const agent = agents[name];
148
+ const paths = scope === "project" ? agent.detect.projectPaths : agent.detect.globalPaths;
149
+ for (const p of paths) {
150
+ if (await pathExists(p)) {
151
+ detected.push(name);
152
+ break;
153
+ }
154
+ }
155
+ }
156
+ return detected;
157
+ }
158
+
159
+ // src/setup/skill-writer.ts
160
+ import { readFile, writeFile } from "fs/promises";
161
+ import { join as join2, resolve, dirname } from "path";
162
+ import { fileURLToPath } from "url";
163
+ var __dirname = dirname(fileURLToPath(import.meta.url));
164
+ var SKILLS_REPO_DIR = resolve(__dirname, "..", "..", "skills");
165
+ async function readSkillFile(skill) {
166
+ const skillPath = join2(SKILLS_REPO_DIR, skill, "SKILL.md");
167
+ try {
168
+ return await readFile(skillPath, "utf-8");
169
+ } catch {
170
+ return null;
171
+ }
172
+ }
173
+ async function installSkill(skill, targetDir) {
174
+ const skillDir = join2(targetDir, skill);
175
+ const targetPath = join2(skillDir, "SKILL.md");
176
+ const alreadyExisted = await pathExists(targetPath);
177
+ const content = await readSkillFile(skill);
178
+ if (!content) {
179
+ throw new Error(`Could not read SKILL.md for skill: ${skill}`);
180
+ }
181
+ if (alreadyExisted) {
182
+ const existingContent = await readFile(targetPath, "utf-8");
183
+ if (existingContent === content) {
184
+ return { skill, targetPath, alreadyExisted: true };
185
+ }
186
+ }
187
+ await ensureDir(skillDir);
188
+ await writeFile(targetPath, content, "utf-8");
189
+ return { skill, targetPath, alreadyExisted };
190
+ }
191
+ async function installSkills(skills, targetDir) {
192
+ const results = [];
193
+ for (const skill of skills) {
194
+ const result = await installSkill(skill, targetDir);
195
+ results.push(result);
196
+ }
197
+ return results;
198
+ }
199
+ async function getAvailableSkills() {
200
+ return Promise.all(
201
+ ALL_SKILLS.map(async (skill) => ({
202
+ name: skill,
203
+ available: await pathExists(join2(SKILLS_REPO_DIR, skill, "SKILL.md"))
204
+ }))
205
+ );
206
+ }
207
+ function parseSkillDescription(content) {
208
+ const match = /^description:\s*[>|]?\s*\n?([\s\S]*?)(?=\n\w|\n---)/m.exec(content);
209
+ if (!match) return null;
210
+ return match[1].split("\n").map((l) => l.trim()).filter(Boolean).join(" ").slice(0, 120);
211
+ }
212
+
213
+ // src/setup/mcp-writer.ts
214
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
215
+ import { dirname as dirname2 } from "path";
216
+ async function readJsonConfig(filePath) {
217
+ let raw;
218
+ try {
219
+ raw = await readFile2(filePath, "utf-8");
220
+ } catch {
221
+ return {};
222
+ }
223
+ raw = raw.trim();
224
+ if (!raw) return {};
225
+ return JSON.parse(raw);
226
+ }
227
+ function mergeServerEntry(existing, configKey, serverName, entry) {
228
+ const section = existing[configKey] ?? {};
229
+ if (serverName in section) {
230
+ const existingEntry = section[serverName];
231
+ const newEnv = entry.env ?? {};
232
+ const existingEnv = existingEntry.env ?? {};
233
+ let envChanged = false;
234
+ for (const [key, value] of Object.entries(newEnv)) {
235
+ if (value && existingEnv[key] !== value) {
236
+ existingEnv[key] = value;
237
+ envChanged = true;
238
+ }
239
+ }
240
+ if (!envChanged) {
241
+ return { config: existing, alreadyExists: true };
242
+ }
243
+ return {
244
+ config: {
245
+ ...existing,
246
+ [configKey]: {
247
+ ...section,
248
+ [serverName]: { ...existingEntry, env: existingEnv }
249
+ }
250
+ },
251
+ alreadyExists: true
252
+ };
253
+ }
254
+ return {
255
+ config: {
256
+ ...existing,
257
+ [configKey]: { ...section, [serverName]: entry }
258
+ },
259
+ alreadyExists: false
260
+ };
261
+ }
262
+ async function writeJsonConfig(filePath, config) {
263
+ await mkdir2(dirname2(filePath), { recursive: true });
264
+ await writeFile2(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
265
+ }
266
+ var B2DP_MCP_SERVERS = {
267
+ datafy: {
268
+ command: "npx",
269
+ args: [
270
+ "@teckedd-code2save/datafy@latest",
271
+ "--config",
272
+ "/path/to/your/dbhub.toml",
273
+ "--transport",
274
+ "stdio"
275
+ ]
276
+ },
277
+ "prisma-mcp-server": {
278
+ command: "npx",
279
+ args: ["-y", "prisma", "mcp"]
280
+ },
281
+ "github-mcp-server": {
282
+ command: "docker",
283
+ args: [
284
+ "run",
285
+ "-i",
286
+ "--rm",
287
+ "-e",
288
+ "GITHUB_PERSONAL_ACCESS_TOKEN",
289
+ "ghcr.io/github/github-mcp-server"
290
+ ],
291
+ env: {
292
+ GITHUB_PERSONAL_ACCESS_TOKEN: ""
293
+ }
294
+ },
295
+ context7: {
296
+ command: "npx",
297
+ args: ["-y", "@upstash/context7-mcp"]
298
+ },
299
+ redis: {
300
+ command: "npx",
301
+ args: ["-y", "@modelcontextprotocol/server-redis"],
302
+ env: {
303
+ REDIS_URL: ""
304
+ }
305
+ }
306
+ };
307
+
308
+ // src/setup/templates.ts
309
+ var RULE_CONTENT = `---
310
+ alwaysApply: true
311
+ ---
312
+
313
+ # Business-to-Data-Platform Orchestrator
314
+
315
+ This project uses the **business-to-data-platform** skill as the central orchestrator. When building any backend feature, database schema, or data-driven system:
316
+
317
+ 1. **Invoke \`business-to-data-platform\`** as the primary skill \u2014 it drives architecture design, schema creation, ORM setup, testing, frontend scaffolding, and infra provisioning.
318
+ 2. **Never hardcode data in UI components.** All data must come from real DB queries or API endpoints.
319
+ 3. **Always use Datafy MCP** for database operations \u2014 never raw psql in the terminal.
320
+ 4. **Follow the 12-step workflow** defined in the orchestrator skill \u2014 from architecture design through infrastructure provisioning.
321
+
322
+ ## Required MCPs for the full stack
323
+ - **datafy** \u2014 DB operations, SQL execution, code generation
324
+ - **prisma-mcp-server** \u2014 migrations and DB exploration
325
+ - **github-mcp-server** \u2014 repo discovery, CI/CD setup
326
+ - **context7** \u2014 up-to-date docs and patterns for any library
327
+
328
+ ## Sibling skills that get invoked automatically
329
+ - \`cloud-solution-architect\` \u2192 Docker/K8s architecture
330
+ - \`api-test-generator\` \u2192 integration tests
331
+ - \`frontend-data-consumer\` \u2192 Vite/Next.js UI components
332
+ - \`infrastructure-as-code-architect\` \u2192 Dockerfiles, K8s manifests, GitHub Actions
333
+ `;
334
+
335
+ // src/commands/setup.ts
336
+ function registerSetupCommand(program2) {
337
+ program2.command("setup").description("Set up the b2dp skill ecosystem for your AI coding agent").option("--antigravity", "Set up for Antigravity / Gemini CLI").option("--claude", "Set up for Claude Code").option("--cursor", "Set up for Cursor").option("--vscode", "Set up for VS Code (Copilot)").option("--gemini", "Set up for Gemini CLI").option(
338
+ "-p, --project",
339
+ "Configure for current project only (default: global)"
340
+ ).option("-y, --yes", "Skip confirmation prompts, install everything").action(async (options) => {
341
+ await setupCommand(options);
342
+ });
343
+ }
344
+ function getSelectedAgentsFromOptions(options) {
345
+ const agents2 = [];
346
+ if (options.antigravity) agents2.push("antigravity");
347
+ if (options.claude) agents2.push("claude");
348
+ if (options.cursor) agents2.push("cursor");
349
+ if (options.vscode) agents2.push("vscode");
350
+ if (options.gemini) agents2.push("gemini");
351
+ return agents2;
352
+ }
353
+ async function resolveTargetAgents(options, scope) {
354
+ let selected = getSelectedAgentsFromOptions(options);
355
+ if (selected.length > 0) return selected;
356
+ if (options.yes) {
357
+ const spinner = ora("Detecting AI coding agents...").start();
358
+ selected = await detectAgents(scope);
359
+ spinner.stop();
360
+ if (selected.length === 0) {
361
+ log.warn(
362
+ "No agents detected. Defaulting to Antigravity. Use a flag like --claude to target a specific agent."
363
+ );
364
+ return ["antigravity"];
365
+ }
366
+ log.info(
367
+ `Auto-detected: ${selected.map((a) => AGENT_DISPLAY_NAMES[a]).join(", ")}`
368
+ );
369
+ return selected;
370
+ }
371
+ const detected = await detectAgents(scope);
372
+ const choices = ALL_AGENT_NAMES.map((name) => ({
373
+ name: AGENT_DISPLAY_NAMES[name],
374
+ value: name,
375
+ checked: detected.includes(name),
376
+ description: detected.includes(name) ? pc2.dim("(detected)") : ""
377
+ }));
378
+ return checkbox({
379
+ message: "Which AI coding agents should b2dp be set up for?",
380
+ choices,
381
+ validate: (v) => v.length > 0 ? true : "Please select at least one agent."
382
+ });
383
+ }
384
+ async function resolveTargetSkills(options) {
385
+ if (options.yes) return [...ALL_SKILLS];
386
+ const siblingSkills = ALL_SKILLS.filter((s) => s !== ORCHESTRATOR_SKILL);
387
+ const choices = siblingSkills.map((skill) => ({
388
+ name: `${skill}`,
389
+ value: skill,
390
+ checked: true,
391
+ description: pc2.dim(SKILL_DESCRIPTIONS[skill])
392
+ }));
393
+ const picked = await checkbox({
394
+ message: "Which sibling skills should be installed? (orchestrator is always included)",
395
+ choices
396
+ });
397
+ return [ORCHESTRATOR_SKILL, ...picked];
398
+ }
399
+ async function resolveTargetMCPs(options) {
400
+ const allMcps = Object.keys(B2DP_MCP_SERVERS);
401
+ if (options.yes) return allMcps.filter((m) => m !== "redis");
402
+ const choices = allMcps.map((name) => ({
403
+ name,
404
+ value: name,
405
+ checked: name !== "redis"
406
+ }));
407
+ return checkbox({
408
+ message: "Which MCP servers should be installed?",
409
+ choices
410
+ });
411
+ }
412
+ async function installSkillFiles(skills, skillsDir) {
413
+ const skillSpinner = ora(`Installing ${skills.length} skills...`).start();
414
+ try {
415
+ const results = await installSkills(skills, skillsDir);
416
+ skillSpinner.stop();
417
+ for (const r of results) {
418
+ const prefix = r.alreadyExisted ? pc2.dim("(updated) ") : "";
419
+ log.success(`${prefix}${r.skill} \u2192 ${r.targetPath}`);
420
+ }
421
+ } catch (err) {
422
+ skillSpinner.fail("Failed to install skills");
423
+ log.error(err instanceof Error ? err.message : String(err));
424
+ }
425
+ }
426
+ async function configureMCPServers(agent, mcpConfigPath, selectedMcps, env) {
427
+ const mcpSpinner = ora("Writing MCP server entries...").start();
428
+ try {
429
+ const config = await readJsonConfig(mcpConfigPath);
430
+ let updatedConfig = config;
431
+ for (const serverName of selectedMcps) {
432
+ const baseEntry = B2DP_MCP_SERVERS[serverName];
433
+ const entry = { ...baseEntry };
434
+ if (serverName === "github-mcp-server" && env.GITHUB_PERSONAL_ACCESS_TOKEN) {
435
+ entry.env = {
436
+ ...entry.env,
437
+ GITHUB_PERSONAL_ACCESS_TOKEN: env.GITHUB_PERSONAL_ACCESS_TOKEN
438
+ };
439
+ }
440
+ if (serverName === "context7" && env.CONTEXT7_API_KEY) {
441
+ entry.args = [
442
+ ...entry.args,
443
+ "--api-key",
444
+ env.CONTEXT7_API_KEY
445
+ ];
446
+ }
447
+ const { config: merged, alreadyExists } = mergeServerEntry(
448
+ updatedConfig,
449
+ agent.mcpConfigKey,
450
+ serverName,
451
+ entry
452
+ );
453
+ updatedConfig = merged;
454
+ if (!alreadyExists) {
455
+ mcpSpinner.text = `Added MCP: ${serverName}`;
456
+ }
457
+ }
458
+ await writeJsonConfig(mcpConfigPath, updatedConfig);
459
+ mcpSpinner.succeed(`MCP config written \u2192 ${mcpConfigPath}`);
460
+ } catch (err) {
461
+ mcpSpinner.fail("Failed to write MCP config");
462
+ log.error(err instanceof Error ? err.message : String(err));
463
+ }
464
+ }
465
+ async function writeRuleFile(rulesDir) {
466
+ const rulesSpinner = ora("Writing b2dp rule file...").start();
467
+ try {
468
+ await ensureDir(rulesDir);
469
+ const rulePath = join3(rulesDir, RULE_FILENAME);
470
+ await writeFile3(rulePath, RULE_CONTENT, "utf-8");
471
+ rulesSpinner.succeed(`Rule written \u2192 ${rulePath}`);
472
+ } catch (err) {
473
+ rulesSpinner.fail("Failed to write rule file");
474
+ log.error(err instanceof Error ? err.message : String(err));
475
+ }
476
+ }
477
+ async function setupAgent(agentName, scope, skills, selectedMcps, env) {
478
+ const agent = getAgent(agentName);
479
+ log.info(pc2.bold(`Setting up ${agent.displayName}...`));
480
+ const skillsDir = agent.skillsDir(scope);
481
+ const rulesDir = agent.rulesDir(scope);
482
+ const mcpConfigPath = agent.mcpConfigPath(scope);
483
+ await installSkillFiles(skills, skillsDir);
484
+ await configureMCPServers(agent, mcpConfigPath, selectedMcps, env);
485
+ await writeRuleFile(rulesDir);
486
+ log.blank();
487
+ }
488
+ async function setupCommand(options) {
489
+ const scope = options.project ? "project" : "global";
490
+ const selectedAgents = await resolveTargetAgents(options, scope);
491
+ const selectedSkills = await resolveTargetSkills(options);
492
+ const selectedMcps = await resolveTargetMCPs(options);
493
+ const env = {};
494
+ if (!options.yes) {
495
+ log.blank();
496
+ log.info(pc2.yellow("Interactive Configuration:"));
497
+ if (selectedMcps.includes("github-mcp-server")) {
498
+ const githubToken = await input({
499
+ message: "GitHub Personal Access Token (for github-mcp-server):",
500
+ transformer: (val) => val ? "*".repeat(val.length) : val
501
+ });
502
+ if (githubToken) {
503
+ env["GITHUB_PERSONAL_ACCESS_TOKEN"] = githubToken;
504
+ }
505
+ }
506
+ if (selectedMcps.includes("context7")) {
507
+ const context7Key = await input({
508
+ message: "Context7 API Key (for context7):",
509
+ transformer: (val) => val ? "*".repeat(val.length) : val
510
+ });
511
+ if (context7Key) {
512
+ env["CONTEXT7_API_KEY"] = context7Key;
513
+ }
514
+ }
515
+ }
516
+ log.blank();
517
+ for (const agentName of selectedAgents) {
518
+ await setupAgent(agentName, scope, selectedSkills, selectedMcps, env);
519
+ }
520
+ log.success(pc2.bold("b2dp setup complete! \u{1F680}"));
521
+ log.blank();
522
+ log.dim("Next steps:");
523
+ log.dim(" 1. Edit your MCP config(s) to fill in the Datafy dbhub.toml path");
524
+ log.dim(" (usually ~/Documents/.../datafy/dbhub.toml)");
525
+ log.dim(" 2. Restart your AI agent to pick up the new skills and MCP servers.");
526
+ log.dim(
527
+ ' 3. Try: "Build me a SaaS platform for team task management" in your agent.'
528
+ );
529
+ log.blank();
530
+ log.dim("Run `b2dp check` to verify your MCP configuration at any time.");
531
+ }
532
+
533
+ // src/commands/check.ts
534
+ import pc3 from "picocolors";
535
+ import ora2 from "ora";
536
+ var REQUIRED_MCPS = ["datafy", "prisma-mcp-server", "github-mcp-server", "context7"];
537
+ function registerCheckCommand(program2) {
538
+ program2.command("check").description(
539
+ "Verify that the b2dp MCP servers are configured for your AI coding agents"
540
+ ).option("-p, --project", "Check project-level config instead of global").action(async (options) => {
541
+ await checkCommand(options);
542
+ });
543
+ }
544
+ async function checkCommand(options) {
545
+ const scope = options.project ? "project" : "global";
546
+ log.info(
547
+ pc3.bold(`Checking b2dp MCP configuration (${scope} scope)...`)
548
+ );
549
+ log.blank();
550
+ let anyAgentFound = false;
551
+ for (const agentName of ALL_AGENT_NAMES) {
552
+ const agent = getAgent(agentName);
553
+ const mcpConfigPath = agent.mcpConfigPath(scope);
554
+ const spinner = ora2(
555
+ `Reading ${agent.displayName} config...`
556
+ ).start();
557
+ let config;
558
+ try {
559
+ config = await readJsonConfig(mcpConfigPath);
560
+ } catch {
561
+ spinner.warn(`${agent.displayName}: config not readable (${mcpConfigPath})`);
562
+ continue;
563
+ }
564
+ const servers = config[agent.mcpConfigKey] ?? {};
565
+ if (Object.keys(servers).length === 0 && Object.keys(config).length === 0) {
566
+ spinner.warn(
567
+ `${agent.displayName}: no config found at ${mcpConfigPath}`
568
+ );
569
+ continue;
570
+ }
571
+ spinner.stop();
572
+ anyAgentFound = true;
573
+ console.log(pc3.bold(`
574
+ ${agent.displayName}`));
575
+ console.log(pc3.dim(` ${mcpConfigPath}`));
576
+ for (const mcp of REQUIRED_MCPS) {
577
+ const isPresent = mcp in servers;
578
+ const entry = servers[mcp];
579
+ const hasPlaceholder = isPresent && JSON.stringify(entry).includes("/path/to/your/dbhub.toml");
580
+ const icon = !isPresent ? pc3.red("\u2716") : hasPlaceholder ? pc3.yellow("\u26A0") : pc3.green("\u2714");
581
+ const note = !isPresent ? pc3.dim(" (missing \u2014 run `b2dp setup` to add)") : hasPlaceholder ? pc3.yellow(" (needs dbhub.toml path filled in)") : pc3.dim(" (configured)");
582
+ console.log(` ${icon} ${mcp}${note}`);
583
+ }
584
+ const optionalMcps = Object.keys(B2DP_MCP_SERVERS).filter(
585
+ (s) => !REQUIRED_MCPS.includes(s)
586
+ );
587
+ for (const mcp of optionalMcps) {
588
+ const isPresent = mcp in servers;
589
+ const icon = isPresent ? pc3.green("\u2714") : pc3.dim("\u25CB");
590
+ const label = isPresent ? pc3.dim(" (configured)") : pc3.dim(" (optional, not set)");
591
+ console.log(` ${icon} ${pc3.dim(mcp)}${label}`);
592
+ }
593
+ }
594
+ log.blank();
595
+ if (!anyAgentFound) {
596
+ log.warn(
597
+ "No agent configs found. Run `b2dp setup` to install the b2dp skill ecosystem."
598
+ );
599
+ } else {
600
+ log.dim("Run `b2dp setup` to add any missing MCPs or reinstall skills.");
601
+ }
602
+ }
603
+
604
+ // src/commands/skills.ts
605
+ import pc4 from "picocolors";
606
+ function registerSkillsCommand(program2) {
607
+ const skills = program2.command("skills").description("Manage b2dp skills");
608
+ skills.command("list").description("List all available b2dp skills").action(async () => {
609
+ await listSkillsCommand();
610
+ });
611
+ skills.command("info <skillName>").description("Show details about a specific skill").action(async (skillName) => {
612
+ await infoSkillCommand(skillName);
613
+ });
614
+ }
615
+ async function listSkillsCommand() {
616
+ log.blank();
617
+ console.log(pc4.bold("Available b2dp skills:"));
618
+ log.blank();
619
+ const available = await getAvailableSkills();
620
+ for (const { name, available: isAvailable } of available) {
621
+ const isOrchestrator = name === ORCHESTRATOR_SKILL;
622
+ const statusIcon = isAvailable ? pc4.green("\u2714") : pc4.red("\u2716");
623
+ const label = isOrchestrator ? pc4.bold(pc4.cyan(name)) + pc4.cyan(" (orchestrator)") : pc4.white(name);
624
+ const desc = pc4.dim(SKILL_DESCRIPTIONS[name]);
625
+ const missing = isAvailable ? "" : pc4.red(" [SKILL.md not found]");
626
+ console.log(` ${statusIcon} ${label}${missing}`);
627
+ console.log(` ${desc}`);
628
+ log.blank();
629
+ }
630
+ const availableCount = available.filter((s) => s.available).length;
631
+ log.dim(
632
+ `${availableCount}/${ALL_SKILLS.length} skills available locally.`
633
+ );
634
+ log.dim(
635
+ "Run `b2dp setup` to install all skills into your AI coding agent."
636
+ );
637
+ }
638
+ async function infoSkillCommand(skillName) {
639
+ const validSkill = ALL_SKILLS.find((s) => s === skillName);
640
+ if (!validSkill) {
641
+ log.error(
642
+ `Unknown skill: "${skillName}". Run \`b2dp skills list\` to see available skills.`
643
+ );
644
+ process.exit(1);
645
+ }
646
+ const content = await readSkillFile(validSkill);
647
+ if (!content) {
648
+ log.error(
649
+ `SKILL.md not found for "${validSkill}". Ensure the skills/ directory is intact.`
650
+ );
651
+ process.exit(1);
652
+ }
653
+ const description = parseSkillDescription(content) ?? SKILL_DESCRIPTIONS[validSkill];
654
+ const isOrchestrator = validSkill === ORCHESTRATOR_SKILL;
655
+ log.blank();
656
+ console.log(
657
+ pc4.bold(
658
+ isOrchestrator ? pc4.cyan(`${validSkill} (orchestrator)`) : pc4.white(validSkill)
659
+ )
660
+ );
661
+ log.blank();
662
+ console.log(` ${pc4.dim(description)}`);
663
+ log.blank();
664
+ const lineCount = content.split("\n").length;
665
+ log.dim(` ${lineCount} lines \xB7 SKILL.md`);
666
+ log.blank();
667
+ log.dim(
668
+ `Install with: b2dp setup --yes (or select it during interactive setup)`
669
+ );
670
+ }
671
+
672
+ // src/index.ts
673
+ function printBanner() {
674
+ const banner = figlet.textSync("b2dp", {
675
+ font: "Small",
676
+ horizontalLayout: "default"
677
+ });
678
+ console.log(pc5.cyan(banner));
679
+ console.log(
680
+ pc5.dim(
681
+ " Business-to-Data-Platform Orchestrator CLI \u2014 skill setup in one command\n"
682
+ )
683
+ );
684
+ }
685
+ var program = new Command();
686
+ printBanner();
687
+ program.name("b2dp").description(
688
+ "Set up the business-to-data-platform skill ecosystem for your AI coding agent"
689
+ ).version(VERSION, "-v, --version", "Output the current version");
690
+ registerSetupCommand(program);
691
+ registerCheckCommand(program);
692
+ registerSkillsCommand(program);
693
+ program.parse(process.argv);
694
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/commands/setup.ts","../src/utils/logger.ts","../src/utils/fs.ts","../src/setup/agents.ts","../src/setup/skill-writer.ts","../src/setup/mcp-writer.ts","../src/setup/templates.ts","../src/commands/check.ts","../src/commands/skills.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport figlet from \"figlet\";\nimport pc from \"picocolors\";\n\nimport { VERSION } from \"./constants.js\";\nimport { registerSetupCommand } from \"./commands/setup.js\";\nimport { registerCheckCommand } from \"./commands/check.js\";\nimport { registerSkillsCommand } from \"./commands/skills.js\";\n\nfunction printBanner(): void {\n const banner = figlet.textSync(\"b2dp\", {\n font: \"Small\",\n horizontalLayout: \"default\",\n });\n console.log(pc.cyan(banner));\n console.log(\n pc.dim(\n \" Business-to-Data-Platform Orchestrator CLI โ€” skill setup in one command\\n\"\n )\n );\n}\n\nconst program = new Command();\n\nprintBanner();\n\nprogram\n .name(\"b2dp\")\n .description(\n \"Set up the business-to-data-platform skill ecosystem for your AI coding agent\"\n )\n .version(VERSION, \"-v, --version\", \"Output the current version\");\n\nregisterSetupCommand(program);\nregisterCheckCommand(program);\nregisterSkillsCommand(program);\n\nprogram.parse(process.argv);\n","export const VERSION = \"1.0.0\";\n\nexport const SKILLS_DIR_NAME = \"skills\";\nexport const RULES_DIR_NAME = \"rules\";\n\nexport const ORCHESTRATOR_SKILL = \"business-to-data-platform\";\n\nexport const ALL_SKILLS = [\n \"business-to-data-platform\",\n \"cloud-solution-architect\",\n \"api-test-generator\",\n \"frontend-data-consumer\",\n \"frontend-design-review\",\n \"infrastructure-as-code-architect\",\n \"context7-mcp\",\n] as const;\n\nexport type SkillName = (typeof ALL_SKILLS)[number];\n\nexport const SKILL_DESCRIPTIONS: Record<SkillName, string> = {\n \"business-to-data-platform\":\n \"Orchestrator โ€” converts any business spec into a production-grade data platform\",\n \"cloud-solution-architect\":\n \"Designs Docker/K8s + GitHub Actions cloud architectures\",\n \"api-test-generator\":\n \"Generates comprehensive integration tests for scaffolded backends\",\n \"frontend-data-consumer\":\n \"Scaffolds Vite/Next.js components with Tailwind CSS and Shadcn/UI\",\n \"frontend-design-review\":\n \"Reviews and creates distinctive, production-grade frontend interfaces\",\n \"infrastructure-as-code-architect\":\n \"Generates Dockerfiles, K8s manifests, and GitHub Actions workflows\",\n \"context7-mcp\":\n \"Fetches up-to-date docs and patterns for any library or framework\",\n};\n\nexport const RULE_FILENAME = \"b2dp.md\";\n","import { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport ora from \"ora\";\nimport { checkbox, input } from \"@inquirer/prompts\";\nimport { writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { log } from \"../utils/logger.js\";\nimport { ensureDir } from \"../utils/fs.js\";\nimport {\n type AgentName,\n ALL_AGENT_NAMES,\n AGENT_DISPLAY_NAMES,\n getAgent,\n detectAgents,\n} from \"../setup/agents.js\";\nimport {\n type SkillName,\n ALL_SKILLS,\n SKILL_DESCRIPTIONS,\n ORCHESTRATOR_SKILL,\n RULE_FILENAME,\n} from \"../constants.js\";\nimport { installSkills } from \"../setup/skill-writer.js\";\nimport {\n readJsonConfig,\n mergeServerEntry,\n writeJsonConfig,\n B2DP_MCP_SERVERS,\n} from \"../setup/mcp-writer.js\";\nimport { RULE_CONTENT } from \"../setup/templates.js\";\n\ninterface SetupOptions {\n antigravity?: boolean;\n claude?: boolean;\n cursor?: boolean;\n vscode?: boolean;\n gemini?: boolean;\n project?: boolean;\n yes?: boolean;\n}\n\nexport function registerSetupCommand(program: Command): void {\n program\n .command(\"setup\")\n .description(\"Set up the b2dp skill ecosystem for your AI coding agent\")\n .option(\"--antigravity\", \"Set up for Antigravity / Gemini CLI\")\n .option(\"--claude\", \"Set up for Claude Code\")\n .option(\"--cursor\", \"Set up for Cursor\")\n .option(\"--vscode\", \"Set up for VS Code (Copilot)\")\n .option(\"--gemini\", \"Set up for Gemini CLI\")\n .option(\n \"-p, --project\",\n \"Configure for current project only (default: global)\"\n )\n .option(\"-y, --yes\", \"Skip confirmation prompts, install everything\")\n .action(async (options: SetupOptions) => {\n await setupCommand(options);\n });\n}\n\nfunction getSelectedAgentsFromOptions(options: SetupOptions): AgentName[] {\n const agents: AgentName[] = [];\n if (options.antigravity) agents.push(\"antigravity\");\n if (options.claude) agents.push(\"claude\");\n if (options.cursor) agents.push(\"cursor\");\n if (options.vscode) agents.push(\"vscode\");\n if (options.gemini) agents.push(\"gemini\");\n return agents;\n}\n\nasync function resolveTargetAgents(\n options: SetupOptions,\n scope: \"project\" | \"global\"\n): Promise<AgentName[]> {\n let selected = getSelectedAgentsFromOptions(options);\n if (selected.length > 0) return selected;\n\n if (options.yes) {\n const spinner = ora(\"Detecting AI coding agents...\").start();\n selected = await detectAgents(scope);\n spinner.stop();\n if (selected.length === 0) {\n log.warn(\n \"No agents detected. Defaulting to Antigravity. Use a flag like --claude to target a specific agent.\"\n );\n return [\"antigravity\"];\n }\n log.info(\n `Auto-detected: ${selected.map((a) => AGENT_DISPLAY_NAMES[a]).join(\", \")}`\n );\n return selected;\n }\n\n const detected = await detectAgents(scope);\n const choices = ALL_AGENT_NAMES.map((name) => ({\n name: AGENT_DISPLAY_NAMES[name],\n value: name,\n checked: detected.includes(name),\n description: detected.includes(name) ? pc.dim(\"(detected)\") : \"\",\n }));\n\n return checkbox({\n message: \"Which AI coding agents should b2dp be set up for?\",\n choices,\n validate: (v) => (v.length > 0 ? true : \"Please select at least one agent.\"),\n });\n}\n\nasync function resolveTargetSkills(options: SetupOptions): Promise<SkillName[]> {\n if (options.yes) return [...ALL_SKILLS];\n\n const siblingSkills = ALL_SKILLS.filter((s) => s !== ORCHESTRATOR_SKILL);\n const choices = siblingSkills.map((skill) => ({\n name: `${skill}`,\n value: skill,\n checked: true,\n description: pc.dim(SKILL_DESCRIPTIONS[skill]),\n }));\n\n const picked = await checkbox<SkillName>({\n message:\n \"Which sibling skills should be installed? (orchestrator is always included)\",\n choices,\n });\n\n return [ORCHESTRATOR_SKILL, ...picked];\n}\n\nasync function resolveTargetMCPs(options: SetupOptions): Promise<string[]> {\n const allMcps = Object.keys(B2DP_MCP_SERVERS);\n if (options.yes) return allMcps.filter((m) => m !== \"redis\");\n\n const choices = allMcps.map((name) => ({\n name,\n value: name,\n checked: name !== \"redis\",\n }));\n\n return checkbox({\n message: \"Which MCP servers should be installed?\",\n choices,\n });\n}\n\nasync function installSkillFiles(skills: SkillName[], skillsDir: string) {\n const skillSpinner = ora(`Installing ${skills.length} skills...`).start();\n try {\n const results = await installSkills(skills, skillsDir);\n skillSpinner.stop();\n for (const r of results) {\n const prefix = r.alreadyExisted ? pc.dim(\"(updated) \") : \"\";\n log.success(`${prefix}${r.skill} โ†’ ${r.targetPath}`);\n }\n } catch (err) {\n skillSpinner.fail(\"Failed to install skills\");\n log.error(err instanceof Error ? err.message : String(err));\n }\n}\n\nasync function configureMCPServers(\n agent: Record<string, any>,\n mcpConfigPath: string,\n selectedMcps: string[],\n env: Record<string, string>\n) {\n const mcpSpinner = ora(\"Writing MCP server entries...\").start();\n try {\n const config = await readJsonConfig(mcpConfigPath);\n let updatedConfig = config;\n\n for (const serverName of selectedMcps) {\n const baseEntry = B2DP_MCP_SERVERS[serverName];\n const entry = { ...baseEntry };\n\n // Apply specific env vars to specific servers\n if (\n serverName === \"github-mcp-server\" &&\n env.GITHUB_PERSONAL_ACCESS_TOKEN\n ) {\n entry.env = {\n ...(entry.env as Record<string, string>),\n GITHUB_PERSONAL_ACCESS_TOKEN: env.GITHUB_PERSONAL_ACCESS_TOKEN,\n };\n }\n\n if (serverName === \"context7\" && env.CONTEXT7_API_KEY) {\n entry.args = [\n ...(entry.args as string[]),\n \"--api-key\",\n env.CONTEXT7_API_KEY,\n ];\n }\n\n const { config: merged, alreadyExists } = mergeServerEntry(\n updatedConfig,\n agent.mcpConfigKey,\n serverName,\n entry\n );\n updatedConfig = merged;\n if (!alreadyExists) {\n mcpSpinner.text = `Added MCP: ${serverName}`;\n }\n }\n\n await writeJsonConfig(mcpConfigPath, updatedConfig);\n mcpSpinner.succeed(`MCP config written โ†’ ${mcpConfigPath}`);\n } catch (err) {\n mcpSpinner.fail(\"Failed to write MCP config\");\n log.error(err instanceof Error ? err.message : String(err));\n }\n}\n\nasync function writeRuleFile(rulesDir: string) {\n const rulesSpinner = ora(\"Writing b2dp rule file...\").start();\n try {\n await ensureDir(rulesDir);\n const rulePath = join(rulesDir, RULE_FILENAME);\n await writeFile(rulePath, RULE_CONTENT, \"utf-8\");\n rulesSpinner.succeed(`Rule written โ†’ ${rulePath}`);\n } catch (err) {\n rulesSpinner.fail(\"Failed to write rule file\");\n log.error(err instanceof Error ? err.message : String(err));\n }\n}\n\nasync function setupAgent(\n agentName: AgentName,\n scope: \"project\" | \"global\",\n skills: SkillName[],\n selectedMcps: string[],\n env: Record<string, string>\n) {\n const agent = getAgent(agentName);\n log.info(pc.bold(`Setting up ${agent.displayName}...`));\n\n const skillsDir = agent.skillsDir(scope);\n const rulesDir = agent.rulesDir(scope);\n const mcpConfigPath = agent.mcpConfigPath(scope);\n\n await installSkillFiles(skills, skillsDir);\n await configureMCPServers(agent, mcpConfigPath, selectedMcps, env);\n await writeRuleFile(rulesDir);\n\n log.blank();\n}\n\nasync function setupCommand(options: SetupOptions): Promise<void> {\n const scope: \"project\" | \"global\" = options.project ? \"project\" : \"global\";\n\n const selectedAgents = await resolveTargetAgents(options, scope);\n const selectedSkills = await resolveTargetSkills(options);\n const selectedMcps = await resolveTargetMCPs(options);\n\n // Ask for keys if not in -y mode\n const env: Record<string, string> = {};\n if (!options.yes) {\n log.blank();\n log.info(pc.yellow(\"Interactive Configuration:\"));\n\n if (selectedMcps.includes(\"github-mcp-server\")) {\n const githubToken = await input({\n message: \"GitHub Personal Access Token (for github-mcp-server):\",\n transformer: (val) => (val ? \"*\".repeat(val.length) : val),\n });\n if (githubToken) {\n env[\"GITHUB_PERSONAL_ACCESS_TOKEN\"] = githubToken;\n }\n }\n\n if (selectedMcps.includes(\"context7\")) {\n const context7Key = await input({\n message: \"Context7 API Key (for context7):\",\n transformer: (val) => (val ? \"*\".repeat(val.length) : val),\n });\n if (context7Key) {\n env[\"CONTEXT7_API_KEY\"] = context7Key;\n }\n }\n }\n\n log.blank();\n\n for (const agentName of selectedAgents) {\n await setupAgent(agentName, scope, selectedSkills, selectedMcps, env);\n }\n\n log.success(pc.bold(\"b2dp setup complete! ๐Ÿš€\"));\n log.blank();\n log.dim(\"Next steps:\");\n log.dim(\" 1. Edit your MCP config(s) to fill in the Datafy dbhub.toml path\");\n log.dim(\" (usually ~/Documents/.../datafy/dbhub.toml)\");\n log.dim(\" 2. Restart your AI agent to pick up the new skills and MCP servers.\");\n log.dim(\n ' 3. Try: \"Build me a SaaS platform for team task management\" in your agent.'\n );\n log.blank();\n log.dim(\"Run `b2dp check` to verify your MCP configuration at any time.\");\n}\n","import pc from \"picocolors\";\n\nexport const log = {\n info: (msg: string) => console.log(`${pc.cyan(\"โ„น\")} ${msg}`),\n success: (msg: string) => console.log(`${pc.green(\"โœ”\")} ${msg}`),\n warn: (msg: string) => console.log(`${pc.yellow(\"โš \")} ${msg}`),\n error: (msg: string) => console.error(`${pc.red(\"โœ–\")} ${msg}`),\n dim: (msg: string) => console.log(pc.dim(msg)),\n blank: () => console.log(\"\"),\n};\n","import { access, mkdir } from \"fs/promises\";\n\nexport async function pathExists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function ensureDir(dir: string): Promise<void> {\n await mkdir(dir, { recursive: true });\n}\n","import { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { pathExists } from \"../utils/fs.js\";\n\nexport type AgentName = \"antigravity\" | \"claude\" | \"cursor\" | \"vscode\" | \"gemini\";\n\nexport interface AgentConfig {\n name: AgentName;\n displayName: string;\n skillsDir: (scope: \"project\" | \"global\") => string;\n rulesDir: (scope: \"project\" | \"global\") => string;\n mcpConfigPath: (scope: \"project\" | \"global\") => string;\n mcpConfigKey: string;\n detect: {\n projectPaths: string[];\n globalPaths: string[];\n };\n}\n\nconst agents: Record<AgentName, AgentConfig> = {\n // Antigravity / Gemini CLI โ€” ~/.gemini/antigravity/\n antigravity: {\n name: \"antigravity\",\n displayName: \"Antigravity (Gemini CLI)\",\n skillsDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".gemini\", \"antigravity\", \"skills\")\n : join(\".agent\", \"skills\"),\n rulesDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".gemini\", \"antigravity\", \"rules\")\n : join(\".agent\", \"rules\"),\n mcpConfigPath: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".gemini\", \"antigravity\", \"mcp_config.json\")\n : join(\".agent\", \"mcp_config.json\"),\n mcpConfigKey: \"mcpServers\",\n detect: {\n projectPaths: [\".agent\"],\n globalPaths: [join(homedir(), \".gemini\", \"antigravity\")],\n },\n },\n\n // Claude Code โ€” ~/.claude/\n claude: {\n name: \"claude\",\n displayName: \"Claude Code\",\n skillsDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".claude\", \"skills\")\n : join(\".claude\", \"skills\"),\n rulesDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".claude\", \"rules\")\n : join(\".claude\", \"rules\"),\n mcpConfigPath: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".claude.json\")\n : join(\".mcp.json\"),\n mcpConfigKey: \"mcpServers\",\n detect: {\n projectPaths: [\".mcp.json\", \".claude\"],\n globalPaths: [join(homedir(), \".claude\")],\n },\n },\n\n // Cursor โ€” ~/.cursor/\n cursor: {\n name: \"cursor\",\n displayName: \"Cursor\",\n skillsDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".cursor\", \"skills\")\n : join(\".cursor\", \"skills\"),\n rulesDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".cursor\", \"rules\")\n : join(\".cursor\", \"rules\"),\n mcpConfigPath: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".cursor\", \"mcp.json\")\n : join(\".cursor\", \"mcp.json\"),\n mcpConfigKey: \"mcpServers\",\n detect: {\n projectPaths: [\".cursor\"],\n globalPaths: [join(homedir(), \".cursor\")],\n },\n },\n\n // VS Code โ€” uses .vscode/ for project-level config\n vscode: {\n name: \"vscode\",\n displayName: \"VS Code (Copilot)\",\n skillsDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".vscode\", \"skills\")\n : join(\".vscode\", \"skills\"),\n rulesDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".vscode\", \"rules\")\n : join(\".vscode\", \"rules\"),\n mcpConfigPath: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".vscode\", \"mcp.json\")\n : join(\".vscode\", \"mcp.json\"),\n mcpConfigKey: \"servers\",\n detect: {\n projectPaths: [\".vscode\"],\n globalPaths: [join(homedir(), \".vscode\")],\n },\n },\n\n // Gemini CLI โ€” ~/.gemini/settings.json\n gemini: {\n name: \"gemini\",\n displayName: \"Gemini CLI\",\n skillsDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".gemini\", \"skills\")\n : join(\".agent\", \"skills\"),\n rulesDir: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".gemini\", \"rules\")\n : join(\".agent\", \"rules\"),\n mcpConfigPath: (scope) =>\n scope === \"global\"\n ? join(homedir(), \".gemini\", \"settings.json\")\n : join(\".agent\", \"settings.json\"),\n mcpConfigKey: \"mcpServers\",\n detect: {\n projectPaths: [\".agent\"],\n globalPaths: [join(homedir(), \".gemini\", \"settings.json\")],\n },\n },\n};\n\nexport function getAgent(name: AgentName): AgentConfig {\n return agents[name];\n}\n\nexport const ALL_AGENT_NAMES = Object.keys(agents) as AgentName[];\n\nexport const AGENT_DISPLAY_NAMES: Record<AgentName, string> = {\n antigravity: \"Antigravity (Gemini CLI)\",\n claude: \"Claude Code\",\n cursor: \"Cursor\",\n vscode: \"VS Code (Copilot)\",\n gemini: \"Gemini CLI\",\n};\n\nexport async function detectAgents(\n scope: \"project\" | \"global\"\n): Promise<AgentName[]> {\n const detected: AgentName[] = [];\n for (const name of ALL_AGENT_NAMES) {\n const agent = agents[name];\n const paths =\n scope === \"project\" ? agent.detect.projectPaths : agent.detect.globalPaths;\n for (const p of paths) {\n if (await pathExists(p)) {\n detected.push(name);\n break;\n }\n }\n }\n return detected;\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join, resolve, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ensureDir, pathExists } from \"../utils/fs.js\";\nimport { type SkillName, ALL_SKILLS } from \"../constants.js\";\n\n// After tsup bundles src/ โ†’ dist/index.js, __dirname === cli/dist/\n// The skills/ directory lives at cli/../skills (one level up from cli/)\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst SKILLS_REPO_DIR = resolve(__dirname, \"..\", \"..\", \"skills\");\n\nexport interface InstallResult {\n skill: SkillName;\n targetPath: string;\n alreadyExisted: boolean;\n}\n\n/**\n * Read the SKILL.md for a given skill name from the local skills/ directory.\n */\nexport async function readSkillFile(skill: SkillName): Promise<string | null> {\n const skillPath = join(SKILLS_REPO_DIR, skill, \"SKILL.md\");\n try {\n return await readFile(skillPath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Install a single skill into the target directory.\n * Creates <targetDir>/<skillName>/SKILL.md\n */\nexport async function installSkill(\n skill: SkillName,\n targetDir: string\n): Promise<InstallResult> {\n const skillDir = join(targetDir, skill);\n const targetPath = join(skillDir, \"SKILL.md\");\n\n const alreadyExisted = await pathExists(targetPath);\n const content = await readSkillFile(skill);\n\n if (!content) {\n throw new Error(`Could not read SKILL.md for skill: ${skill}`);\n }\n\n if (alreadyExisted) {\n const existingContent = await readFile(targetPath, \"utf-8\");\n if (existingContent === content) {\n return { skill, targetPath, alreadyExisted: true };\n }\n }\n\n await ensureDir(skillDir);\n await writeFile(targetPath, content, \"utf-8\");\n\n return { skill, targetPath, alreadyExisted };\n}\n\n/**\n * Install multiple skills into the target directory.\n */\nexport async function installSkills(\n skills: SkillName[],\n targetDir: string\n): Promise<InstallResult[]> {\n const results: InstallResult[] = [];\n for (const skill of skills) {\n const result = await installSkill(skill, targetDir);\n results.push(result);\n }\n return results;\n}\n\n/**\n * Get which skills are available in the local skills/ directory.\n */\nexport async function getAvailableSkills(): Promise<\n { name: SkillName; available: boolean }[]\n> {\n return Promise.all(\n ALL_SKILLS.map(async (skill) => ({\n name: skill,\n available: await pathExists(join(SKILLS_REPO_DIR, skill, \"SKILL.md\")),\n }))\n );\n}\n\n/**\n * Extract the description line from a SKILL.md frontmatter.\n */\nexport function parseSkillDescription(content: string): string | null {\n const match = /^description:\\s*[>|]?\\s*\\n?([\\s\\S]*?)(?=\\n\\w|\\n---)/m.exec(content);\n if (!match) return null;\n return match[1]\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter(Boolean)\n .join(\" \")\n .slice(0, 120);\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\nexport async function readJsonConfig(\n filePath: string\n): Promise<Record<string, unknown>> {\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch {\n return {};\n }\n raw = raw.trim();\n if (!raw) return {};\n return JSON.parse(raw) as Record<string, unknown>;\n}\n\nexport function mergeServerEntry(\n existing: Record<string, unknown>,\n configKey: string,\n serverName: string,\n entry: Record<string, unknown>\n): { config: Record<string, unknown>; alreadyExists: boolean } {\n const section =\n (existing[configKey] as Record<string, unknown> | undefined) ?? {};\n\n // If server already exists, we might still want to merge env vars if they were empty\n if (serverName in section) {\n const existingEntry = section[serverName] as Record<string, any>;\n const newEnv = (entry.env as Record<string, string>) ?? {};\n const existingEnv = (existingEntry.env as Record<string, string>) ?? {};\n\n let envChanged = false;\n for (const [key, value] of Object.entries(newEnv)) {\n if (value && existingEnv[key] !== value) {\n existingEnv[key] = value;\n envChanged = true;\n }\n }\n\n if (!envChanged) {\n return { config: existing, alreadyExists: true };\n }\n\n return {\n config: {\n ...existing,\n [configKey]: {\n ...section,\n [serverName]: { ...existingEntry, env: existingEnv },\n },\n },\n alreadyExists: true,\n };\n }\n\n return {\n config: {\n ...existing,\n [configKey]: { ...section, [serverName]: entry },\n },\n alreadyExists: false,\n };\n}\n\nexport async function writeJsonConfig(\n filePath: string,\n config: Record<string, unknown>\n): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\n/** MCP server entries for the b2dp ecosystem */\nexport const B2DP_MCP_SERVERS: Record<string, Record<string, unknown>> = {\n datafy: {\n command: \"npx\",\n args: [\n \"@teckedd-code2save/datafy@latest\",\n \"--config\",\n \"/path/to/your/dbhub.toml\",\n \"--transport\",\n \"stdio\",\n ],\n },\n \"prisma-mcp-server\": {\n command: \"npx\",\n args: [\"-y\", \"prisma\", \"mcp\"],\n },\n \"github-mcp-server\": {\n command: \"docker\",\n args: [\n \"run\",\n \"-i\",\n \"--rm\",\n \"-e\",\n \"GITHUB_PERSONAL_ACCESS_TOKEN\",\n \"ghcr.io/github/github-mcp-server\",\n ],\n env: {\n GITHUB_PERSONAL_ACCESS_TOKEN: \"\",\n },\n },\n context7: {\n command: \"npx\",\n args: [\"-y\", \"@upstash/context7-mcp\"],\n },\n redis: {\n command: \"npx\",\n args: [\"-y\", \"@modelcontextprotocol/server-redis\"],\n env: {\n REDIS_URL: \"\",\n },\n },\n};\n","export const RULE_CONTENT = `---\nalwaysApply: true\n---\n\n# Business-to-Data-Platform Orchestrator\n\nThis project uses the **business-to-data-platform** skill as the central orchestrator. When building any backend feature, database schema, or data-driven system:\n\n1. **Invoke \\`business-to-data-platform\\`** as the primary skill โ€” it drives architecture design, schema creation, ORM setup, testing, frontend scaffolding, and infra provisioning.\n2. **Never hardcode data in UI components.** All data must come from real DB queries or API endpoints.\n3. **Always use Datafy MCP** for database operations โ€” never raw psql in the terminal.\n4. **Follow the 12-step workflow** defined in the orchestrator skill โ€” from architecture design through infrastructure provisioning.\n\n## Required MCPs for the full stack\n- **datafy** โ€” DB operations, SQL execution, code generation\n- **prisma-mcp-server** โ€” migrations and DB exploration\n- **github-mcp-server** โ€” repo discovery, CI/CD setup\n- **context7** โ€” up-to-date docs and patterns for any library\n\n## Sibling skills that get invoked automatically\n- \\`cloud-solution-architect\\` โ†’ Docker/K8s architecture\n- \\`api-test-generator\\` โ†’ integration tests\n- \\`frontend-data-consumer\\` โ†’ Vite/Next.js UI components\n- \\`infrastructure-as-code-architect\\` โ†’ Dockerfiles, K8s manifests, GitHub Actions\n`;\n","import { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport ora from \"ora\";\n\nimport { log } from \"../utils/logger.js\";\nimport {\n type AgentName,\n ALL_AGENT_NAMES,\n AGENT_DISPLAY_NAMES,\n getAgent,\n} from \"../setup/agents.js\";\nimport { readJsonConfig } from \"../setup/mcp-writer.js\";\nimport { B2DP_MCP_SERVERS } from \"../setup/mcp-writer.js\";\n\nconst REQUIRED_MCPS = [\"datafy\", \"prisma-mcp-server\", \"github-mcp-server\", \"context7\"];\n\ninterface CheckOptions {\n project?: boolean;\n}\n\nexport function registerCheckCommand(program: Command): void {\n program\n .command(\"check\")\n .description(\n \"Verify that the b2dp MCP servers are configured for your AI coding agents\"\n )\n .option(\"-p, --project\", \"Check project-level config instead of global\")\n .action(async (options: CheckOptions) => {\n await checkCommand(options);\n });\n}\n\nasync function checkCommand(options: CheckOptions): Promise<void> {\n const scope: \"project\" | \"global\" = options.project ? \"project\" : \"global\";\n\n log.info(\n pc.bold(`Checking b2dp MCP configuration (${scope} scope)...`)\n );\n log.blank();\n\n let anyAgentFound = false;\n\n for (const agentName of ALL_AGENT_NAMES as AgentName[]) {\n const agent = getAgent(agentName);\n const mcpConfigPath = agent.mcpConfigPath(scope);\n\n const spinner = ora(\n `Reading ${agent.displayName} config...`\n ).start();\n\n let config: Record<string, unknown>;\n try {\n config = await readJsonConfig(mcpConfigPath);\n } catch {\n spinner.warn(`${agent.displayName}: config not readable (${mcpConfigPath})`);\n continue;\n }\n\n const servers = (\n config[agent.mcpConfigKey] as Record<string, unknown> | undefined\n ) ?? {};\n\n if (Object.keys(servers).length === 0 && Object.keys(config).length === 0) {\n spinner.warn(\n `${agent.displayName}: no config found at ${mcpConfigPath}`\n );\n continue;\n }\n\n spinner.stop();\n anyAgentFound = true;\n\n console.log(pc.bold(`\\n ${agent.displayName}`));\n console.log(pc.dim(` ${mcpConfigPath}`));\n\n for (const mcp of REQUIRED_MCPS) {\n const isPresent = mcp in servers;\n const entry = servers[mcp] as Record<string, unknown> | undefined;\n\n // Check for unfilled placeholders\n const hasPlaceholder =\n isPresent &&\n JSON.stringify(entry).includes(\"/path/to/your/dbhub.toml\");\n\n const icon = !isPresent\n ? pc.red(\"โœ–\")\n : hasPlaceholder\n ? pc.yellow(\"โš \")\n : pc.green(\"โœ”\");\n\n const note = !isPresent\n ? pc.dim(\" (missing โ€” run `b2dp setup` to add)\")\n : hasPlaceholder\n ? pc.yellow(\" (needs dbhub.toml path filled in)\")\n : pc.dim(\" (configured)\");\n\n console.log(` ${icon} ${mcp}${note}`);\n }\n\n // Check for optional MCPs from b2dp ecosystem\n const optionalMcps = Object.keys(B2DP_MCP_SERVERS).filter(\n (s) => !REQUIRED_MCPS.includes(s)\n );\n for (const mcp of optionalMcps) {\n const isPresent = mcp in servers;\n const icon = isPresent ? pc.green(\"โœ”\") : pc.dim(\"โ—‹\");\n const label = isPresent ? pc.dim(\" (configured)\") : pc.dim(\" (optional, not set)\");\n console.log(` ${icon} ${pc.dim(mcp)}${label}`);\n }\n }\n\n log.blank();\n\n if (!anyAgentFound) {\n log.warn(\n \"No agent configs found. Run `b2dp setup` to install the b2dp skill ecosystem.\"\n );\n } else {\n log.dim(\"Run `b2dp setup` to add any missing MCPs or reinstall skills.\");\n }\n}\n","import { Command } from \"commander\";\nimport pc from \"picocolors\";\n\nimport { log } from \"../utils/logger.js\";\nimport { ALL_SKILLS, SKILL_DESCRIPTIONS, ORCHESTRATOR_SKILL } from \"../constants.js\";\nimport {\n getAvailableSkills,\n readSkillFile,\n parseSkillDescription,\n} from \"../setup/skill-writer.js\";\n\nexport function registerSkillsCommand(program: Command): void {\n const skills = program\n .command(\"skills\")\n .description(\"Manage b2dp skills\");\n\n // b2dp skills list\n skills\n .command(\"list\")\n .description(\"List all available b2dp skills\")\n .action(async () => {\n await listSkillsCommand();\n });\n\n // b2dp skills info <name>\n skills\n .command(\"info <skillName>\")\n .description(\"Show details about a specific skill\")\n .action(async (skillName: string) => {\n await infoSkillCommand(skillName);\n });\n}\n\nasync function listSkillsCommand(): Promise<void> {\n log.blank();\n console.log(pc.bold(\"Available b2dp skills:\"));\n log.blank();\n\n const available = await getAvailableSkills();\n\n for (const { name, available: isAvailable } of available) {\n const isOrchestrator = name === ORCHESTRATOR_SKILL;\n const statusIcon = isAvailable ? pc.green(\"โœ”\") : pc.red(\"โœ–\");\n const label = isOrchestrator\n ? pc.bold(pc.cyan(name)) + pc.cyan(\" (orchestrator)\")\n : pc.white(name);\n const desc = pc.dim(SKILL_DESCRIPTIONS[name]);\n const missing = isAvailable ? \"\" : pc.red(\" [SKILL.md not found]\");\n\n console.log(` ${statusIcon} ${label}${missing}`);\n console.log(` ${desc}`);\n log.blank();\n }\n\n const availableCount = available.filter((s) => s.available).length;\n log.dim(\n `${availableCount}/${ALL_SKILLS.length} skills available locally.`\n );\n log.dim(\n \"Run `b2dp setup` to install all skills into your AI coding agent.\"\n );\n}\n\nasync function infoSkillCommand(skillName: string): Promise<void> {\n const validSkill = ALL_SKILLS.find((s) => s === skillName);\n if (!validSkill) {\n log.error(\n `Unknown skill: \"${skillName}\". Run \\`b2dp skills list\\` to see available skills.`\n );\n process.exit(1);\n }\n\n const content = await readSkillFile(validSkill);\n if (!content) {\n log.error(\n `SKILL.md not found for \"${validSkill}\". Ensure the skills/ directory is intact.`\n );\n process.exit(1);\n }\n\n const description = parseSkillDescription(content) ?? SKILL_DESCRIPTIONS[validSkill];\n const isOrchestrator = validSkill === ORCHESTRATOR_SKILL;\n\n log.blank();\n console.log(\n pc.bold(\n isOrchestrator ? pc.cyan(`${validSkill} (orchestrator)`) : pc.white(validSkill)\n )\n );\n log.blank();\n console.log(` ${pc.dim(description)}`);\n log.blank();\n\n // Count lines in the SKILL.md as a rough size indicator\n const lineCount = content.split(\"\\n\").length;\n log.dim(` ${lineCount} lines ยท SKILL.md`);\n log.blank();\n\n log.dim(\n `Install with: b2dp setup --yes (or select it during interactive setup)`\n );\n}\n"],"mappings":"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","names":["pc","pc","writeFile","join","join","join","readFile","writeFile","mkdir","dirname","program","agents","pc","join","writeFile","pc","ora","program","pc","ora","pc","program","pc","pc"]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@teckedd-code2save/b2dp",
3
+ "version": "1.0.0",
4
+ "description": "b2dp CLI โ€” One-command setup for the Business-to-Data-Platform skill ecosystem",
5
+ "type": "module",
6
+ "bin": {
7
+ "b2dp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "LICENSE",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch",
17
+ "typecheck": "tsc --noEmit",
18
+ "test": "vitest run",
19
+ "clean": "rm -rf dist node_modules"
20
+ },
21
+ "dependencies": {
22
+ "@inquirer/prompts": "^8.2.0",
23
+ "commander": "^13.1.0",
24
+ "figlet": "^1.9.4",
25
+ "ora": "^9.0.0",
26
+ "picocolors": "^1.1.1"
27
+ },
28
+ "devDependencies": {
29
+ "@types/figlet": "^1.7.0",
30
+ "@types/node": "^22",
31
+ "tsup": "^8.5.0",
32
+ "typescript": "^5.8.2",
33
+ "vitest": "^3.0.7"
34
+ },
35
+ "keywords": [
36
+ "b2dp",
37
+ "ai",
38
+ "skills",
39
+ "mcp",
40
+ "data-platform",
41
+ "cli"
42
+ ],
43
+ "author": "teckedd-code2save",
44
+ "license": "MIT",
45
+ "engines": {
46
+ "node": ">=18"
47
+ }
48
+ }