@teckedd-code2save/b2dp 1.0.3 → 1.1.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/README.md +43 -119
- package/dist/index.js +16 -9
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/skills/api-test-generator/SKILL.md +72 -0
- package/skills/business-to-data-platform/SKILL.md +206 -0
- package/skills/cloud-solution-architect/SKILL.md +317 -0
- package/skills/cloud-solution-architect/references/acceptance-criteria.md +436 -0
- package/skills/cloud-solution-architect/references/architecture-styles.md +365 -0
- package/skills/cloud-solution-architect/references/best-practices.md +311 -0
- package/skills/cloud-solution-architect/references/design-patterns.md +873 -0
- package/skills/cloud-solution-architect/references/design-principles.md +328 -0
- package/skills/cloud-solution-architect/references/mission-critical.md +285 -0
- package/skills/cloud-solution-architect/references/performance-antipatterns.md +242 -0
- package/skills/cloud-solution-architect/references/technology-choices.md +159 -0
- package/skills/context7-mcp/SKILL.md +53 -0
- package/skills/frontend-data-consumer/SKILL.md +75 -0
- package/skills/frontend-design-review/SKILL.md +138 -0
- package/skills/frontend-design-review/references/pattern-examples.md +21 -0
- package/skills/frontend-design-review/references/quick-checklist.md +38 -0
- package/skills/frontend-design-review/references/review-output-format.md +68 -0
- package/skills/frontend-design-review/references/review-type-modifiers.md +31 -0
- package/skills/infrastructure-as-code-architect/SKILL.md +56 -0
package/README.md
CHANGED
|
@@ -1,139 +1,63 @@
|
|
|
1
|
-
#
|
|
1
|
+
# b2dp CLI 🚀
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> One-command setup for the Business-to-Data-Platform (b2dp) skill ecosystem across your favorite AI coding agents.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## What is b2dp?
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**b2dp** is a powerful "Orchestrator" skill ecosystem designed for AI coding agents. It transforms high-level business requirements into production-grade technical implementations—from database schema and backend code to frontend components and Infrastructure as Code (IaC).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The `b2dp` CLI tool automates the installation and configuration of this entire ecosystem, ensuring your agents have the right skills and tools (MCP servers) to build complex software.
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
```
|
|
11
|
+
## Key Capabilities
|
|
23
12
|
|
|
24
|
-
|
|
13
|
+
- **Multi-Agent Support**: Automatically detects and configures [Antigravity](https://github.com), [Claude Code](https://claude.ai), [Cursor](https://cursor.com), [VS Code (Copilot)](https://code.visualstudio.com), and [Gemini CLI](https://github.com).
|
|
14
|
+
- **Skill Installation**: Provisions the core `business-to-data-platform` orchestrator along with mandatory sibling skills:
|
|
15
|
+
- `cloud-solution-architect`
|
|
16
|
+
- `api-test-generator`
|
|
17
|
+
- `frontend-data-consumer`
|
|
18
|
+
- `infrastructure-as-code-architect`
|
|
19
|
+
- **MCP Server Configuration**: Sets up necessary Model Context Protocol (MCP) servers like **Datafy** (database operations), **Context7** (documentation tracking), and **GitHub** (repository management).
|
|
20
|
+
- **Global & Project Scoping**: Install the ecosystem globally for personal use or scope it to a specific project repository.
|
|
21
|
+
- **Health Checks**: Quickly verify your configuration and missing dependencies across all supported agents.
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
## Quick Start
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
[sources.pet_market]
|
|
30
|
-
type = "postgres"
|
|
31
|
-
url = "postgres://user:pass@localhost:5432/pet_market"
|
|
32
|
-
description = "Pet market database"
|
|
25
|
+
### Installation
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
description = "Ride sharing database"
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @teckedd-code2save/b2dp
|
|
29
|
+
```
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
type = "redis"
|
|
41
|
-
url = "redis://localhost:6379"
|
|
42
|
-
description = "Redis for session storage"
|
|
31
|
+
### Full Ecosystem Setup
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
type = "elasticsearch"
|
|
46
|
-
host = "localhost"
|
|
47
|
-
port = 9200
|
|
48
|
-
lazy = true
|
|
49
|
-
description = "Elasticsearch for logs and analytics"
|
|
50
|
-
```
|
|
33
|
+
Run the setup command to interactively choose your agents and skills:
|
|
51
34
|
|
|
52
|
-
|
|
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
|
-
}
|
|
35
|
+
```bash
|
|
36
|
+
b2dp setup
|
|
113
37
|
```
|
|
114
38
|
|
|
115
|
-
|
|
39
|
+
Alternatively, use the auto-setup flag to install everything with detected defaults:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
b2dp setup --yes
|
|
43
|
+
```
|
|
116
44
|
|
|
117
|
-
|
|
45
|
+
## Commands
|
|
118
46
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
- **Antigravity**: Place skills in `~/.gemini/antigravity/skills/` or `.agent/workflows/` in your workspace.
|
|
47
|
+
### `setup`
|
|
48
|
+
Provision skills, configure MCP servers, and write agent rules.
|
|
49
|
+
- `--project`: Configure for the current directory only.
|
|
50
|
+
- `--claude`, `--cursor`, etc.: Force setup for a specific agent.
|
|
124
51
|
|
|
125
|
-
|
|
52
|
+
### `check`
|
|
53
|
+
Verify that your AI agents are correctly configured with b2dp skills and MCP servers.
|
|
54
|
+
- `--project`: Check project-level configuration.
|
|
126
55
|
|
|
127
|
-
|
|
56
|
+
### `skills`
|
|
57
|
+
Manage and explore the available b2dp skills.
|
|
58
|
+
- `skills list`: Show all available skills and their descriptions.
|
|
59
|
+
- `skills info <name>`: View details and content of a specific skill.
|
|
128
60
|
|
|
129
|
-
|
|
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.
|
|
61
|
+
## License
|
|
134
62
|
|
|
135
|
-
|
|
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.
|
|
63
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import figlet from "figlet";
|
|
|
6
6
|
import pc5 from "picocolors";
|
|
7
7
|
|
|
8
8
|
// src/constants.ts
|
|
9
|
-
var VERSION = "1.0
|
|
9
|
+
var VERSION = "1.1.0";
|
|
10
10
|
var ORCHESTRATOR_SKILL = "business-to-data-platform";
|
|
11
11
|
var ALL_SKILLS = [
|
|
12
12
|
"business-to-data-platform",
|
|
@@ -529,15 +529,22 @@ async function setupCommand(options) {
|
|
|
529
529
|
for (const agentName of selectedAgents) {
|
|
530
530
|
await setupAgent(agentName, scope, selectedSkills, selectedMcps, env);
|
|
531
531
|
}
|
|
532
|
-
log.success(pc2.bold("b2dp setup complete! \u{1F680}"));
|
|
532
|
+
log.success(pc2.bold(pc2.green("b2dp setup complete! \u{1F680}")));
|
|
533
533
|
log.blank();
|
|
534
|
-
log.
|
|
535
|
-
log.
|
|
536
|
-
log.
|
|
537
|
-
log.
|
|
538
|
-
log.
|
|
539
|
-
|
|
540
|
-
);
|
|
534
|
+
log.info(pc2.bold("NEXT STEPS (ACTION REQUIRED):"));
|
|
535
|
+
log.info(pc2.bold(" 1. Configure Datafy:"));
|
|
536
|
+
log.info(" Create or update your " + pc2.cyan("dbhub.toml") + " file to define your databases.");
|
|
537
|
+
log.info(" Then, find the " + pc2.yellow("datafy") + " entry in your agent's MCP config and update the ");
|
|
538
|
+
log.info(" " + pc2.cyan("--config") + " path to point to your " + pc2.cyan("dbhub.toml") + ".");
|
|
539
|
+
log.blank();
|
|
540
|
+
log.info(pc2.bold(" 2. Restart Your Agent:"));
|
|
541
|
+
log.info(" Restart your AI coding agent to pick up the new skills and MCP servers.");
|
|
542
|
+
log.blank();
|
|
543
|
+
log.info(pc2.bold(" 3. Start Building:"));
|
|
544
|
+
log.info(pc2.italic(' Try: "Build me a SaaS platform for team task management"'));
|
|
545
|
+
log.blank();
|
|
546
|
+
log.info(pc2.dim("Sample dbhub.toml can be found at:"));
|
|
547
|
+
log.info(pc2.blue("https://github.com/teckedd-code2save/ai-build-tools/blob/main/samples/dbhub.toml"));
|
|
541
548
|
log.blank();
|
|
542
549
|
log.dim("Run `b2dp check` to verify your MCP configuration at any time.");
|
|
543
550
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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.3\";\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 { existsSync } from \"node:fs\";\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\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve the package root by looking for package.json\n */\nfunction findPackageRoot(startDir: string): string {\n let current = startDir;\n while (current !== dirname(current)) {\n if (existsSync(join(current, \"package.json\"))) {\n return current;\n }\n current = dirname(current);\n }\n return startDir; // Fallback to startDir if not found\n}\n\nconst PACKAGE_ROOT = findPackageRoot(__dirname);\nconst SKILLS_REPO_DIR = resolve(PACKAGE_ROOT, \"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"]}
|
|
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.1.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(pc.green(\"b2dp setup complete! 🚀\")));\n log.blank();\n \n log.info(pc.bold(\"NEXT STEPS (ACTION REQUIRED):\"));\n log.info(pc.bold(\" 1. Configure Datafy:\"));\n log.info(\" Create or update your \" + pc.cyan(\"dbhub.toml\") + \" file to define your databases.\");\n log.info(\" Then, find the \" + pc.yellow(\"datafy\") + \" entry in your agent's MCP config and update the \");\n log.info(\" \" + pc.cyan(\"--config\") + \" path to point to your \" + pc.cyan(\"dbhub.toml\") + \".\");\n log.blank();\n log.info(pc.bold(\" 2. Restart Your Agent:\"));\n log.info(\" Restart your AI coding agent to pick up the new skills and MCP servers.\");\n log.blank();\n log.info(pc.bold(\" 3. Start Building:\"));\n log.info(pc.italic(\" Try: \\\"Build me a SaaS platform for team task management\\\"\"));\n log.blank();\n\n log.info(pc.dim(\"Sample dbhub.toml can be found at:\"));\n log.info(pc.blue(\"https://github.com/teckedd-code2save/ai-build-tools/blob/main/samples/dbhub.toml\"));\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 { existsSync } from \"node:fs\";\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\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n/**\n * Resolve the package root by looking for package.json\n */\nfunction findPackageRoot(startDir: string): string {\n let current = startDir;\n while (current !== dirname(current)) {\n if (existsSync(join(current, \"package.json\"))) {\n return current;\n }\n current = dirname(current);\n }\n return startDir; // Fallback to startDir if not found\n}\n\nconst PACKAGE_ROOT = findPackageRoot(__dirname);\nconst SKILLS_REPO_DIR = resolve(PACKAGE_ROOT, \"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\nexport const DBHUB_TOML_SAMPLE = `# Datafy DBHub Configuration (dbhub.toml)\n# This file connects Datafy to your database instances\n\n[databases.pet_market]\ntype = \"postgres\"\nurl = \"postgresql://user:pass@localhost:5432/pet_market\"\n\n[databases.ride_sharing]\ntype = \"postgres\"\nurl = \"postgresql://user:pass@localhost:5432/ride_sharing\"\n\n[databases.session_storage]\ntype = \"redis\"\nurl = \"redis://localhost:6379\"\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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teckedd-code2save/b2dp",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "b2dp CLI — One-command setup for the Business-to-Data-Platform skill ecosystem",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"dist/**/*",
|
|
15
|
+
"skills/**/*",
|
|
15
16
|
"LICENSE",
|
|
16
17
|
"README.md"
|
|
17
18
|
],
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-test-generator
|
|
3
|
+
description: Generates comprehensive API integration tests (using Jest/Supertest, Pytest, etc.) from existing backend repositories, ORM schemas, and router/controller files. Use when a user wants to test their newly provisioned data platform or backend services.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# API Test Generator
|
|
7
|
+
|
|
8
|
+
Generate production-grade integration tests for existing API endpoints and backend services. This skill ensures that newly provisioned schemas and controllers are fully validated against business requirements.
|
|
9
|
+
|
|
10
|
+
## 🎯 When to Use
|
|
11
|
+
- After using the `business-to-data-platform` skill to scaffold a new backend.
|
|
12
|
+
- When adding new endpoints to an existing service.
|
|
13
|
+
- When refactoring DTOs, Schemas, or ORM models.
|
|
14
|
+
- When generating Postman/Bruno collections for manual API testing.
|
|
15
|
+
|
|
16
|
+
## 🛠️ Step-by-Step Workflow
|
|
17
|
+
|
|
18
|
+
### 1. Analyze the Backend Structure
|
|
19
|
+
- Scan the existing codebase to identify the framework (e.g., Express + TS, FastAPI, Spring Boot, .NET).
|
|
20
|
+
- Identify the ORM being used (e.g., Prisma, SQLAlchemy, EF Core).
|
|
21
|
+
- Locate the router/controller files to extract the exact endpoint paths, HTTP methods, and expected payloads.
|
|
22
|
+
|
|
23
|
+
### 2. Set Up the Test Environment
|
|
24
|
+
- Generate the necessary test scaffolding (e.g., `jest.config.js` with `ts-jest` for TypeScript, or `conftest.py` for Pytest).
|
|
25
|
+
- Auto-generate test database setup, teardown, and seeding scripts using the existing ORM. Tests should run against an isolated test database (e.g., a local Docker container) and rollback state between tests.
|
|
26
|
+
- Provide instructions or a workflow snippet for running the tests (e.g., `npm run test:e2e`).
|
|
27
|
+
|
|
28
|
+
### 3. Generate Integration Tests
|
|
29
|
+
For every identified endpoint, generate tests that cover:
|
|
30
|
+
- **Happy Path:** 200 OK or 201 Created with valid payloads. Verify the response shape against the API contract.
|
|
31
|
+
- **Validation Errors:** 400 Bad Request triggers by sending missing required fields, incorrect types, or boundary violations.
|
|
32
|
+
- **Not Found / Edge Cases:** 404 Not Found handling for invalid IDs.
|
|
33
|
+
- **Auth / Permissions (if applicable):** 401 Unauthorized or 403 Forbidden checks.
|
|
34
|
+
|
|
35
|
+
#### Example TypeScript + Jest + Supertest:
|
|
36
|
+
```typescript
|
|
37
|
+
import request from 'supertest';
|
|
38
|
+
import { app } from '../src/app';
|
|
39
|
+
import { prisma } from '../src/db/prisma';
|
|
40
|
+
|
|
41
|
+
describe('POST /api/users', () => {
|
|
42
|
+
afterAll(async () => {
|
|
43
|
+
await prisma.user.deleteMany(); // cleanup
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should create a new user and return 201', async () => {
|
|
47
|
+
const res = await request(app)
|
|
48
|
+
.post('/api/users')
|
|
49
|
+
.send({ email: 'test@example.com', name: 'Test User' });
|
|
50
|
+
|
|
51
|
+
expect(res.status).toBe(201);
|
|
52
|
+
expect(res.body).toHaveProperty('id');
|
|
53
|
+
expect(res.body.email).toBe('test@example.com');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should return 400 if email is missing', async () => {
|
|
57
|
+
const res = await request(app)
|
|
58
|
+
.post('/api/users')
|
|
59
|
+
.send({ name: 'Test User' });
|
|
60
|
+
|
|
61
|
+
expect(res.status).toBe(400);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4. Provide API Collections (Optional)
|
|
67
|
+
If requested, output a JSON collection for Postman or Bruno. This allows the user to easily import the endpoints and play with them manually. Make sure to parameterize base URLs using environment variables like `{{baseUrl}}`.
|
|
68
|
+
|
|
69
|
+
## ⚙️ Best Practices
|
|
70
|
+
- **Never mock the database** for integration tests if it can be avoided. Tests should hit a real test database instance to ensure ORM queries behave exactly as they will in production.
|
|
71
|
+
- **Clear state between tests.** Use ORM capabilities to truncate tables or rollback transactions before every test case to prevent flakiness.
|
|
72
|
+
- **Use factories.** Leverage libraries like `faker.js` or `factory_boy` to create realistic test payloads instead of hardcoding `'test'` everywhere.
|