projecta-rrr 1.6.0 → 1.6.1
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 +57 -0
- package/bin/install.js +20 -1
- package/commands/rrr/help.md +35 -0
- package/commands/rrr/new-project.md +19 -3
- package/mcp.registry.json +128 -0
- package/package.json +9 -2
- package/projecta.defaults.json +32 -0
- package/rrr/presets/saas-dashboard.yml +7 -1
- package/scripts/mcp-setup.sh +224 -0
- package/scripts/pushpa-mode.sh +639 -0
package/README.md
CHANGED
|
@@ -219,6 +219,63 @@ If you choose a non-default provider, RRR asks for a reason and records it in De
|
|
|
219
219
|
|
|
220
220
|
These are allowed but require explicit justification: Firebase, Supabase, Auth0, Vercel, PlanetScale.
|
|
221
221
|
|
|
222
|
+
### MCP Auto-Setup
|
|
223
|
+
|
|
224
|
+
RRR includes an MCP registry that maps your selected providers to their MCP servers. After `/rrr:new-project` generates your `MVP_FEATURES.yml`:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm run mcp:setup
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
This reads your feature selections and outputs the MCP configuration for Claude Code. Only MCPs you actually need get installed.
|
|
231
|
+
|
|
232
|
+
| Feature | MCP Server |
|
|
233
|
+
|---------|------------|
|
|
234
|
+
| Database (Neon) | `@neondatabase/mcp-server-neon` |
|
|
235
|
+
| Payments (Stripe) | `@stripe/mcp` |
|
|
236
|
+
| Analytics (PostHog) | `@anthropic/mcp-posthog` |
|
|
237
|
+
| Voice (Deepgram) | `@deepgram/mcp-server` |
|
|
238
|
+
| Browser (Browserbase) | `@anthropic/mcp-browserbase` |
|
|
239
|
+
| Sandbox (E2B) | `@e2b/mcp-server` |
|
|
240
|
+
| Storage (R2) | `@cloudflare/mcp-server-r2` |
|
|
241
|
+
|
|
242
|
+
**Always included:** Context7 (docs), GitHub, Filesystem, Sequential Thinking.
|
|
243
|
+
|
|
244
|
+
### Pushpa Mode (Autopilot)
|
|
245
|
+
|
|
246
|
+
Run phases overnight while you sleep. Pushpa Mode is an unattended runner that plans and executes phases sequentially, skipping any that require human verification.
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
bash scripts/pushpa-mode.sh
|
|
250
|
+
# or
|
|
251
|
+
npm run pushpa
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**What it does:**
|
|
255
|
+
1. Preflights required API keys based on your `MVP_FEATURES.yml`
|
|
256
|
+
2. Iterates through phases in order
|
|
257
|
+
3. Plans any phase that doesn't have a plan yet
|
|
258
|
+
4. Executes phases automatically
|
|
259
|
+
5. **Skips** phases marked with `HITL_REQUIRED: true` (human verification needed)
|
|
260
|
+
6. Generates a morning report at `.planning/PUSHPA_REPORT.md`
|
|
261
|
+
|
|
262
|
+
**Prerequisites:**
|
|
263
|
+
- Run `/rrr:new-project` first (project must be initialized)
|
|
264
|
+
- Set all required API keys (script will check and warn)
|
|
265
|
+
- Recommend enabling YOLO mode in `.planning/config.json`
|
|
266
|
+
|
|
267
|
+
**Where outputs live:**
|
|
268
|
+
- Report: `.planning/PUSHPA_REPORT.md`
|
|
269
|
+
- Logs: `.planning/logs/pushpa_*.log`
|
|
270
|
+
|
|
271
|
+
**HITL Convention:**
|
|
272
|
+
Plans that require human verification should include one of these markers:
|
|
273
|
+
- `HITL_REQUIRED: true` (canonical)
|
|
274
|
+
- `HUMAN_VERIFICATION_REQUIRED`
|
|
275
|
+
- `MANUAL_VERIFICATION`
|
|
276
|
+
|
|
277
|
+
Pushpa Mode will skip these phases and record them in the report for manual follow-up.
|
|
278
|
+
|
|
222
279
|
---
|
|
223
280
|
|
|
224
281
|
## How It Works
|
package/bin/install.js
CHANGED
|
@@ -301,7 +301,26 @@ function finishInstall(settingsPath, settings, statuslineCommand, notifyCommand,
|
|
|
301
301
|
writeSettings(settingsPath, settings);
|
|
302
302
|
|
|
303
303
|
console.log(`
|
|
304
|
-
${green}Done!${reset}
|
|
304
|
+
${green}Done!${reset}
|
|
305
|
+
|
|
306
|
+
${yellow}If you installed from inside Claude Code:${reset}
|
|
307
|
+
Type ${cyan}exit${reset} and restart ${cyan}claude${reset} so it reloads commands.
|
|
308
|
+
|
|
309
|
+
${yellow}Pick your start command:${reset}
|
|
310
|
+
|
|
311
|
+
${cyan}New/empty folder (greenfield)${reset}
|
|
312
|
+
/rrr:new-project
|
|
313
|
+
(bootstraps Next.js/TS baseline if folder is empty)
|
|
314
|
+
|
|
315
|
+
${cyan}Existing repo (brownfield)${reset}
|
|
316
|
+
/rrr:new-project
|
|
317
|
+
(brownfield-safe; won't overwrite or restructure your repo)
|
|
318
|
+
|
|
319
|
+
${cyan}RRR already initialized${reset}
|
|
320
|
+
/rrr:progress
|
|
321
|
+
(if .planning/STATE.md exists)
|
|
322
|
+
|
|
323
|
+
Run ${cyan}/rrr:help${reset} anytime to see all commands.
|
|
305
324
|
`);
|
|
306
325
|
}
|
|
307
326
|
|
package/commands/rrr/help.md
CHANGED
|
@@ -19,6 +19,20 @@ Output ONLY the reference content below. Do NOT add:
|
|
|
19
19
|
|
|
20
20
|
**RRR** creates hierarchical project plans optimized for solo agentic development with Claude Code. Built by [Projecta.ai](https://projecta.ai).
|
|
21
21
|
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
**After install/update:** If you installed from inside Claude Code, type `exit` and restart `claude` so it reloads commands.
|
|
25
|
+
|
|
26
|
+
**Pick your start command:**
|
|
27
|
+
|
|
28
|
+
| Scenario | Command |
|
|
29
|
+
|----------|---------|
|
|
30
|
+
| New/empty folder (greenfield) | `/rrr:new-project` — bootstraps Next.js/TS baseline if folder is empty |
|
|
31
|
+
| Existing repo (brownfield) | `/rrr:new-project` — brownfield-safe; won't overwrite your repo |
|
|
32
|
+
| RRR already initialized | `/rrr:progress` — if `.planning/STATE.md` exists |
|
|
33
|
+
|
|
34
|
+
**MVP Definition of Done at Projecta:** local demo runs + tests pass.
|
|
35
|
+
|
|
22
36
|
## Quick Start
|
|
23
37
|
|
|
24
38
|
1. `/rrr:new-project` - Initialize project (includes research, requirements, roadmap)
|
|
@@ -381,6 +395,27 @@ Change anytime by editing `.planning/config.json`
|
|
|
381
395
|
|
|
382
396
|
That's it! `/rrr:new-project` handles everything from bootstrap to roadmap.
|
|
383
397
|
|
|
398
|
+
**Overnight mode: Pushpa Mode**
|
|
399
|
+
|
|
400
|
+
Run phases unattended while you sleep:
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
bash scripts/pushpa-mode.sh
|
|
404
|
+
# or
|
|
405
|
+
npm run pushpa
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Prerequisites:
|
|
409
|
+
1. Run `/rrr:new-project` first
|
|
410
|
+
2. Set all required API keys (based on your MVP_FEATURES.yml)
|
|
411
|
+
3. Recommend enabling YOLO mode in config.json
|
|
412
|
+
|
|
413
|
+
Pushpa Mode will:
|
|
414
|
+
- Plan and execute phases sequentially
|
|
415
|
+
- Skip phases marked with `HITL_REQUIRED: true`
|
|
416
|
+
- Generate report at `.planning/PUSHPA_REPORT.md`
|
|
417
|
+
- Log everything to `.planning/logs/`
|
|
418
|
+
|
|
384
419
|
**Bootstrap only (no planning):**
|
|
385
420
|
|
|
386
421
|
```
|
|
@@ -350,15 +350,17 @@ Use AskUserQuestion:
|
|
|
350
350
|
- header: "Preset"
|
|
351
351
|
- question: "Start with a use-case preset? This pre-configures capabilities."
|
|
352
352
|
- options:
|
|
353
|
+
- "saas-dashboard (Recommended)" — SaaS with Clerk + Neon + optional Stripe (DEFAULT)
|
|
353
354
|
- "landing-waitlist" — Landing page + email capture (no auth, no db)
|
|
354
|
-
- "saas-dashboard" — SaaS with Clerk + Neon + optional Stripe
|
|
355
355
|
- "api-admin" — API backend + admin panel (Neon, optional auth)
|
|
356
356
|
- "voice-agent" — Voice AI agent (Deepgram + full agent stack)
|
|
357
|
-
- "
|
|
357
|
+
- "Custom" — Configure capabilities manually
|
|
358
358
|
|
|
359
359
|
**If preset selected:** Load preset defaults, skip to Step 3 (confirm/override).
|
|
360
360
|
|
|
361
|
-
**If "
|
|
361
|
+
**If "Custom":** Continue to Step 2.
|
|
362
|
+
|
|
363
|
+
**DEFAULT BEHAVIOR:** If user selects nothing or skips, assume `saas-dashboard` preset (Neon + Clerk + Render + optional Stripe).
|
|
362
364
|
|
|
363
365
|
### Step 2: Capability Questionnaire
|
|
364
366
|
|
|
@@ -496,6 +498,10 @@ Deviations: [count or "None"]
|
|
|
496
498
|
|
|
497
499
|
File: .planning/MVP_FEATURES.yml
|
|
498
500
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
501
|
+
|
|
502
|
+
💡 To configure MCP servers for your stack:
|
|
503
|
+
npm run mcp:setup
|
|
504
|
+
(Reads MVP_FEATURES.yml and installs required MCPs)
|
|
499
505
|
```
|
|
500
506
|
|
|
501
507
|
Continue to Phase 3.
|
|
@@ -1272,6 +1278,16 @@ Present completion with next steps:
|
|
|
1272
1278
|
|
|
1273
1279
|
<sub>`/clear` first → fresh context window</sub>
|
|
1274
1280
|
|
|
1281
|
+
───────────────────────────────────────────────────────────────
|
|
1282
|
+
|
|
1283
|
+
## 🔌 MCP Setup (Optional)
|
|
1284
|
+
|
|
1285
|
+
Configure Claude Code with MCP servers for your selected stack:
|
|
1286
|
+
|
|
1287
|
+
`npm run mcp:setup`
|
|
1288
|
+
|
|
1289
|
+
<sub>Reads MVP_FEATURES.yml and outputs MCP configuration</sub>
|
|
1290
|
+
|
|
1275
1291
|
───────────────────────────────────────────────────────────────
|
|
1276
1292
|
```
|
|
1277
1293
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"description": "MCP server registry - maps providers to their Claude MCP installation commands",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
|
|
6
|
+
"servers": {
|
|
7
|
+
"neon": {
|
|
8
|
+
"name": "Neon MCP Server",
|
|
9
|
+
"description": "PostgreSQL database operations via Neon",
|
|
10
|
+
"command": "npx -y @neondatabase/mcp-server-neon",
|
|
11
|
+
"envRequired": ["NEON_API_KEY"],
|
|
12
|
+
"featureKeys": ["db:neon", "database"],
|
|
13
|
+
"docs": "https://neon.tech/docs/ai/mcp"
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
"stripe": {
|
|
17
|
+
"name": "Stripe MCP Server",
|
|
18
|
+
"description": "Payment processing and subscription management",
|
|
19
|
+
"command": "npx -y @stripe/mcp",
|
|
20
|
+
"envRequired": ["STRIPE_SECRET_KEY"],
|
|
21
|
+
"featureKeys": ["payments:stripe", "payments"],
|
|
22
|
+
"docs": "https://docs.stripe.com/mcp"
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
"posthog": {
|
|
26
|
+
"name": "PostHog MCP Server",
|
|
27
|
+
"description": "Product analytics and feature flags",
|
|
28
|
+
"command": "npx -y @anthropic/mcp-posthog",
|
|
29
|
+
"envRequired": ["POSTHOG_API_KEY", "POSTHOG_PROJECT_ID"],
|
|
30
|
+
"featureKeys": ["analytics:posthog", "analytics"],
|
|
31
|
+
"docs": "https://posthog.com/docs/mcp"
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
"deepgram": {
|
|
35
|
+
"name": "Deepgram MCP Server",
|
|
36
|
+
"description": "Voice-to-text and text-to-speech",
|
|
37
|
+
"command": "npx -y @deepgram/mcp-server",
|
|
38
|
+
"envRequired": ["DEEPGRAM_API_KEY"],
|
|
39
|
+
"featureKeys": ["voice:deepgram", "voice"],
|
|
40
|
+
"docs": "https://developers.deepgram.com/docs/mcp"
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
"browserbase": {
|
|
44
|
+
"name": "Browserbase MCP Server",
|
|
45
|
+
"description": "Headless browser automation",
|
|
46
|
+
"command": "npx -y @anthropic/mcp-browserbase",
|
|
47
|
+
"envRequired": ["BROWSERBASE_API_KEY", "BROWSERBASE_PROJECT_ID"],
|
|
48
|
+
"featureKeys": ["browserAutomation:browserbase", "browser"],
|
|
49
|
+
"docs": "https://docs.browserbase.com/mcp"
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
"e2b": {
|
|
53
|
+
"name": "E2B MCP Server",
|
|
54
|
+
"description": "Code execution sandbox",
|
|
55
|
+
"command": "npx -y @e2b/mcp-server",
|
|
56
|
+
"envRequired": ["E2B_API_KEY"],
|
|
57
|
+
"featureKeys": ["sandbox:e2b", "sandbox"],
|
|
58
|
+
"docs": "https://e2b.dev/docs/mcp"
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
"cloudflare-r2": {
|
|
62
|
+
"name": "Cloudflare R2 MCP Server",
|
|
63
|
+
"description": "S3-compatible object storage",
|
|
64
|
+
"command": "npx -y @cloudflare/mcp-server-r2",
|
|
65
|
+
"envRequired": ["CLOUDFLARE_ACCOUNT_ID", "R2_ACCESS_KEY_ID", "R2_SECRET_ACCESS_KEY"],
|
|
66
|
+
"featureKeys": ["objectStorage:r2", "storage"],
|
|
67
|
+
"docs": "https://developers.cloudflare.com/r2/mcp"
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
"context7": {
|
|
71
|
+
"name": "Context7 MCP Server",
|
|
72
|
+
"description": "Documentation lookup and library references",
|
|
73
|
+
"command": "npx -y @upstash/context7-mcp",
|
|
74
|
+
"envRequired": [],
|
|
75
|
+
"featureKeys": ["docs", "context"],
|
|
76
|
+
"docs": "https://context7.dev",
|
|
77
|
+
"alwaysInclude": true
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
"resend": {
|
|
81
|
+
"name": "Resend MCP Server",
|
|
82
|
+
"description": "Transactional email sending",
|
|
83
|
+
"command": "npx -y @resend/mcp-server",
|
|
84
|
+
"envRequired": ["RESEND_API_KEY"],
|
|
85
|
+
"featureKeys": ["email:resend", "email"],
|
|
86
|
+
"docs": "https://resend.com/docs/mcp"
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
"github": {
|
|
90
|
+
"name": "GitHub MCP Server",
|
|
91
|
+
"description": "GitHub repository operations",
|
|
92
|
+
"command": "npx -y @modelcontextprotocol/server-github",
|
|
93
|
+
"envRequired": ["GITHUB_PERSONAL_ACCESS_TOKEN"],
|
|
94
|
+
"featureKeys": ["github", "vcs"],
|
|
95
|
+
"docs": "https://modelcontextprotocol.io/docs/servers/github",
|
|
96
|
+
"alwaysInclude": true
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
"filesystem": {
|
|
100
|
+
"name": "Filesystem MCP Server",
|
|
101
|
+
"description": "Local filesystem operations",
|
|
102
|
+
"command": "npx -y @modelcontextprotocol/server-filesystem",
|
|
103
|
+
"envRequired": [],
|
|
104
|
+
"featureKeys": ["filesystem"],
|
|
105
|
+
"docs": "https://modelcontextprotocol.io/docs/servers/filesystem",
|
|
106
|
+
"alwaysInclude": true
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
"sequential-thinking": {
|
|
110
|
+
"name": "Sequential Thinking MCP Server",
|
|
111
|
+
"description": "Enhanced reasoning and chain-of-thought",
|
|
112
|
+
"command": "npx -y @modelcontextprotocol/server-sequential-thinking",
|
|
113
|
+
"envRequired": [],
|
|
114
|
+
"featureKeys": ["reasoning"],
|
|
115
|
+
"docs": "https://modelcontextprotocol.io/docs/servers/sequential-thinking",
|
|
116
|
+
"alwaysInclude": true
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
"presetMappings": {
|
|
121
|
+
"saas-dashboard": ["neon", "context7", "github", "filesystem", "sequential-thinking"],
|
|
122
|
+
"landing-waitlist": ["context7", "github", "filesystem", "sequential-thinking"],
|
|
123
|
+
"api-admin": ["neon", "context7", "github", "filesystem", "sequential-thinking"],
|
|
124
|
+
"voice-agent": ["neon", "deepgram", "e2b", "context7", "github", "filesystem", "sequential-thinking"]
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
"defaultServers": ["context7", "github", "filesystem", "sequential-thinking"]
|
|
128
|
+
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projecta-rrr",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code by Projecta.ai",
|
|
5
5
|
"bin": {
|
|
6
6
|
"projecta-rrr": "bin/install.js"
|
|
7
7
|
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"mcp:setup": "bash scripts/mcp-setup.sh",
|
|
10
|
+
"pushpa": "bash scripts/pushpa-mode.sh"
|
|
11
|
+
},
|
|
8
12
|
"files": [
|
|
9
13
|
"bin",
|
|
10
14
|
"commands",
|
|
11
15
|
"rrr",
|
|
12
16
|
"agents",
|
|
13
|
-
"hooks"
|
|
17
|
+
"hooks",
|
|
18
|
+
"scripts",
|
|
19
|
+
"mcp.registry.json",
|
|
20
|
+
"projecta.defaults.json"
|
|
14
21
|
],
|
|
15
22
|
"keywords": [
|
|
16
23
|
"claude",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"framework": "nextjs-app-router",
|
|
3
|
+
"language": "typescript",
|
|
4
|
+
"packageManager": "npm",
|
|
5
|
+
"ui": {
|
|
6
|
+
"styling": "tailwind",
|
|
7
|
+
"componentSystem": "shadcn-ui"
|
|
8
|
+
},
|
|
9
|
+
"testing": {
|
|
10
|
+
"unit": "vitest",
|
|
11
|
+
"e2e": "playwright"
|
|
12
|
+
},
|
|
13
|
+
"preferredProviders": {
|
|
14
|
+
"db": ["neon"],
|
|
15
|
+
"auth": ["clerk", "neon-auth"],
|
|
16
|
+
"authDefault": "clerk",
|
|
17
|
+
"deploy": ["render"],
|
|
18
|
+
"payments": ["stripe"],
|
|
19
|
+
"objectStorage": ["r2"],
|
|
20
|
+
"analytics": ["posthog"],
|
|
21
|
+
"voice": ["deepgram"],
|
|
22
|
+
"agents": ["mastra"],
|
|
23
|
+
"agentAuth": ["authdev"],
|
|
24
|
+
"agentMail": ["agentmail"],
|
|
25
|
+
"sandbox": ["e2b"],
|
|
26
|
+
"browserAutomation": ["browserbase"]
|
|
27
|
+
},
|
|
28
|
+
"discouragedProviders": {
|
|
29
|
+
"list": ["firebase", "supabase", "auth0", "vercel", "planetscale"],
|
|
30
|
+
"note": "These providers are discouraged but allowed with explicit reason recorded in Deviation Notes"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# SaaS Dashboard Preset
|
|
1
|
+
# SaaS Dashboard Preset (DEFAULT)
|
|
2
2
|
# Auth + database required, payments optional
|
|
3
|
+
# This is the Projecta default preset - used when user skips selection
|
|
3
4
|
|
|
4
5
|
name: saas-dashboard
|
|
6
|
+
default: true
|
|
5
7
|
description: SaaS application with authentication, database, and optional payments
|
|
6
8
|
|
|
7
9
|
stack:
|
|
@@ -70,6 +72,10 @@ database:
|
|
|
70
72
|
- users (synced from Clerk)
|
|
71
73
|
- user_data (application-specific)
|
|
72
74
|
|
|
75
|
+
# MCP servers auto-installed based on integrations
|
|
76
|
+
mcp_servers:
|
|
77
|
+
- neon # Required for database
|
|
78
|
+
|
|
73
79
|
done_criteria:
|
|
74
80
|
- User can sign up and sign in
|
|
75
81
|
- Dashboard only accessible when authenticated
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# MCP Setup Script for Projecta RRR
|
|
4
|
+
# Reads MVP_FEATURES.yml and generates MCP server configuration
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
# Colors
|
|
10
|
+
RED='\033[0;31m'
|
|
11
|
+
GREEN='\033[0;32m'
|
|
12
|
+
YELLOW='\033[1;33m'
|
|
13
|
+
CYAN='\033[0;36m'
|
|
14
|
+
NC='\033[0m' # No Color
|
|
15
|
+
|
|
16
|
+
echo ""
|
|
17
|
+
echo -e "${CYAN}╔══════════════════════════════════════════╗${NC}"
|
|
18
|
+
echo -e "${CYAN}║ Projecta RRR - MCP Setup ║${NC}"
|
|
19
|
+
echo -e "${CYAN}╚══════════════════════════════════════════╝${NC}"
|
|
20
|
+
echo ""
|
|
21
|
+
|
|
22
|
+
# Find the RRR installation directory
|
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
24
|
+
RRR_DIR="$(dirname "$SCRIPT_DIR")"
|
|
25
|
+
|
|
26
|
+
# Check if we're in a project with .planning
|
|
27
|
+
PROJECT_DIR="$(pwd)"
|
|
28
|
+
FEATURES_FILE="$PROJECT_DIR/.planning/MVP_FEATURES.yml"
|
|
29
|
+
REGISTRY_FILE="$RRR_DIR/mcp.registry.json"
|
|
30
|
+
|
|
31
|
+
# Verify registry exists
|
|
32
|
+
if [ ! -f "$REGISTRY_FILE" ]; then
|
|
33
|
+
echo -e "${RED}Error: MCP registry not found at $REGISTRY_FILE${NC}"
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Determine which MCPs to include
|
|
38
|
+
MCP_SERVERS=()
|
|
39
|
+
|
|
40
|
+
# Always include default servers
|
|
41
|
+
DEFAULT_SERVERS=$(cat "$REGISTRY_FILE" | grep -A1000 '"defaultServers"' | grep -o '"[^"]*"' | tr -d '"' | head -10)
|
|
42
|
+
for server in $DEFAULT_SERVERS; do
|
|
43
|
+
if [ "$server" != "defaultServers" ] && [ "$server" != "[" ] && [ "$server" != "]" ]; then
|
|
44
|
+
MCP_SERVERS+=("$server")
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
# Check if MVP_FEATURES.yml exists
|
|
49
|
+
if [ -f "$FEATURES_FILE" ]; then
|
|
50
|
+
echo -e "${GREEN}Found MVP_FEATURES.yml${NC}"
|
|
51
|
+
echo ""
|
|
52
|
+
|
|
53
|
+
# Extract preset if specified
|
|
54
|
+
PRESET=$(grep "^preset:" "$FEATURES_FILE" 2>/dev/null | cut -d: -f2 | tr -d ' ' || echo "")
|
|
55
|
+
|
|
56
|
+
if [ -n "$PRESET" ]; then
|
|
57
|
+
echo -e "Preset: ${CYAN}$PRESET${NC}"
|
|
58
|
+
|
|
59
|
+
# Get servers for this preset from registry
|
|
60
|
+
PRESET_SERVERS=$(cat "$REGISTRY_FILE" | grep -A10 "\"$PRESET\":" | grep -o '"[^"]*"' | tr -d '"' | grep -v "$PRESET" | head -10)
|
|
61
|
+
for server in $PRESET_SERVERS; do
|
|
62
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " ${server} " ]]; then
|
|
63
|
+
MCP_SERVERS+=("$server")
|
|
64
|
+
fi
|
|
65
|
+
done
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Check for specific feature selections
|
|
69
|
+
# Database
|
|
70
|
+
if grep -q "db:" "$FEATURES_FILE" 2>/dev/null; then
|
|
71
|
+
DB=$(grep "db:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
72
|
+
if [ "$DB" = "neon" ]; then
|
|
73
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " neon " ]]; then
|
|
74
|
+
MCP_SERVERS+=("neon")
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
# Payments
|
|
80
|
+
if grep -q "payments:" "$FEATURES_FILE" 2>/dev/null; then
|
|
81
|
+
PAYMENTS=$(grep "payments:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
82
|
+
if [ "$PAYMENTS" = "stripe" ]; then
|
|
83
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " stripe " ]]; then
|
|
84
|
+
MCP_SERVERS+=("stripe")
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
# Voice
|
|
90
|
+
if grep -q "voice:" "$FEATURES_FILE" 2>/dev/null; then
|
|
91
|
+
VOICE=$(grep "voice:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
92
|
+
if [ "$VOICE" = "deepgram" ]; then
|
|
93
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " deepgram " ]]; then
|
|
94
|
+
MCP_SERVERS+=("deepgram")
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# Analytics
|
|
100
|
+
if grep -q "analytics:" "$FEATURES_FILE" 2>/dev/null; then
|
|
101
|
+
ANALYTICS=$(grep "analytics:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
102
|
+
if [ "$ANALYTICS" = "posthog" ]; then
|
|
103
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " posthog " ]]; then
|
|
104
|
+
MCP_SERVERS+=("posthog")
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# Browser automation
|
|
110
|
+
if grep -q "browserAutomation:" "$FEATURES_FILE" 2>/dev/null; then
|
|
111
|
+
BROWSER=$(grep "browserAutomation:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
112
|
+
if [ "$BROWSER" = "browserbase" ]; then
|
|
113
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " browserbase " ]]; then
|
|
114
|
+
MCP_SERVERS+=("browserbase")
|
|
115
|
+
fi
|
|
116
|
+
fi
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Sandbox
|
|
120
|
+
if grep -q "sandbox:" "$FEATURES_FILE" 2>/dev/null; then
|
|
121
|
+
SANDBOX=$(grep "sandbox:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
122
|
+
if [ "$SANDBOX" = "e2b" ]; then
|
|
123
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " e2b " ]]; then
|
|
124
|
+
MCP_SERVERS+=("e2b")
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Object storage
|
|
130
|
+
if grep -q "objectStorage:" "$FEATURES_FILE" 2>/dev/null; then
|
|
131
|
+
STORAGE=$(grep "objectStorage:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
132
|
+
if [ "$STORAGE" = "r2" ]; then
|
|
133
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " cloudflare-r2 " ]]; then
|
|
134
|
+
MCP_SERVERS+=("cloudflare-r2")
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# Email
|
|
140
|
+
if grep -q "email:" "$FEATURES_FILE" 2>/dev/null; then
|
|
141
|
+
EMAIL=$(grep "email:" "$FEATURES_FILE" | cut -d: -f2 | tr -d ' ')
|
|
142
|
+
if [ "$EMAIL" = "resend" ]; then
|
|
143
|
+
if [[ ! " ${MCP_SERVERS[*]} " =~ " resend " ]]; then
|
|
144
|
+
MCP_SERVERS+=("resend")
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
fi
|
|
148
|
+
else
|
|
149
|
+
echo -e "${YELLOW}No MVP_FEATURES.yml found - using SaaS default preset${NC}"
|
|
150
|
+
echo ""
|
|
151
|
+
# Add neon for SaaS default
|
|
152
|
+
MCP_SERVERS+=("neon")
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
echo ""
|
|
156
|
+
echo -e "${GREEN}MCP Servers for your stack:${NC}"
|
|
157
|
+
echo "─────────────────────────────"
|
|
158
|
+
|
|
159
|
+
# Output the MCP configuration
|
|
160
|
+
for server in "${MCP_SERVERS[@]}"; do
|
|
161
|
+
# Get server details from registry
|
|
162
|
+
SERVER_NAME=$(cat "$REGISTRY_FILE" | grep -A5 "\"$server\":" | grep '"name"' | cut -d'"' -f4)
|
|
163
|
+
SERVER_CMD=$(cat "$REGISTRY_FILE" | grep -A5 "\"$server\":" | grep '"command"' | cut -d'"' -f4)
|
|
164
|
+
|
|
165
|
+
if [ -n "$SERVER_CMD" ]; then
|
|
166
|
+
echo -e " ${CYAN}$server${NC}: $SERVER_NAME"
|
|
167
|
+
echo -e " Command: ${YELLOW}$SERVER_CMD${NC}"
|
|
168
|
+
fi
|
|
169
|
+
done
|
|
170
|
+
|
|
171
|
+
echo ""
|
|
172
|
+
echo "─────────────────────────────"
|
|
173
|
+
echo ""
|
|
174
|
+
echo -e "${GREEN}To configure Claude Code with these MCPs:${NC}"
|
|
175
|
+
echo ""
|
|
176
|
+
echo "1. Open Claude Code settings:"
|
|
177
|
+
echo -e " ${CYAN}claude config${NC}"
|
|
178
|
+
echo ""
|
|
179
|
+
echo "2. Add MCP servers manually, or add to your ~/.claude/settings.json:"
|
|
180
|
+
echo ""
|
|
181
|
+
|
|
182
|
+
# Generate JSON snippet
|
|
183
|
+
echo -e "${YELLOW}{"
|
|
184
|
+
echo ' "mcpServers": {'
|
|
185
|
+
|
|
186
|
+
FIRST=true
|
|
187
|
+
for server in "${MCP_SERVERS[@]}"; do
|
|
188
|
+
SERVER_CMD=$(cat "$REGISTRY_FILE" | grep -A5 "\"$server\":" | grep '"command"' | cut -d'"' -f4)
|
|
189
|
+
if [ -n "$SERVER_CMD" ]; then
|
|
190
|
+
if [ "$FIRST" = true ]; then
|
|
191
|
+
FIRST=false
|
|
192
|
+
else
|
|
193
|
+
echo ","
|
|
194
|
+
fi
|
|
195
|
+
# Extract the package name from the npx command
|
|
196
|
+
PKG_NAME=$(echo "$SERVER_CMD" | sed 's/npx -y //')
|
|
197
|
+
echo -n " \"$server\": {"
|
|
198
|
+
echo -n "\"command\": \"npx\", \"args\": [\"-y\", \"$PKG_NAME\"]"
|
|
199
|
+
echo -n "}"
|
|
200
|
+
fi
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
echo ""
|
|
204
|
+
echo ' }'
|
|
205
|
+
echo -e "}${NC}"
|
|
206
|
+
echo ""
|
|
207
|
+
|
|
208
|
+
# List required environment variables
|
|
209
|
+
echo -e "${GREEN}Required environment variables:${NC}"
|
|
210
|
+
echo "─────────────────────────────"
|
|
211
|
+
|
|
212
|
+
for server in "${MCP_SERVERS[@]}"; do
|
|
213
|
+
ENV_VARS=$(cat "$REGISTRY_FILE" | grep -A10 "\"$server\":" | grep -A5 '"envRequired"' | grep -o '"[A-Z_]*"' | tr -d '"')
|
|
214
|
+
if [ -n "$ENV_VARS" ]; then
|
|
215
|
+
echo -e " ${CYAN}$server${NC}:"
|
|
216
|
+
for var in $ENV_VARS; do
|
|
217
|
+
echo " - $var"
|
|
218
|
+
done
|
|
219
|
+
fi
|
|
220
|
+
done
|
|
221
|
+
|
|
222
|
+
echo ""
|
|
223
|
+
echo -e "${GREEN}Done!${NC} Add the environment variables to your .env file."
|
|
224
|
+
echo ""
|
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Pushpa Mode - Unattended Overnight Runner for RRR
|
|
4
|
+
# Runs plan+execute phases sequentially, skipping HITL-marked phases
|
|
5
|
+
#
|
|
6
|
+
# Usage: bash scripts/pushpa-mode.sh
|
|
7
|
+
#
|
|
8
|
+
# Requirements:
|
|
9
|
+
# - MVP_FEATURES.yml must exist (.planning/MVP_FEATURES.yml)
|
|
10
|
+
# - Required API keys must be set based on your feature selections
|
|
11
|
+
# - Claude Code must be installed and configured
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
17
|
+
# Configuration
|
|
18
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
19
|
+
|
|
20
|
+
PLANNING_DIR=".planning"
|
|
21
|
+
PHASES_DIR="$PLANNING_DIR/phases"
|
|
22
|
+
LOGS_DIR="$PLANNING_DIR/logs"
|
|
23
|
+
FEATURES_FILE="$PLANNING_DIR/MVP_FEATURES.yml"
|
|
24
|
+
STATE_FILE="$PLANNING_DIR/STATE.md"
|
|
25
|
+
ROADMAP_FILE="$PLANNING_DIR/ROADMAP.md"
|
|
26
|
+
REPORT_FILE="$PLANNING_DIR/PUSHPA_REPORT.md"
|
|
27
|
+
|
|
28
|
+
# HITL markers that indicate human verification required
|
|
29
|
+
HITL_MARKERS=("HITL_REQUIRED: true" "HUMAN_VERIFICATION_REQUIRED" "MANUAL_VERIFICATION")
|
|
30
|
+
|
|
31
|
+
# Polling configuration
|
|
32
|
+
POLL_INTERVAL=10 # seconds between checks
|
|
33
|
+
MAX_POLL_ATTEMPTS=360 # 1 hour max wait (360 * 10s)
|
|
34
|
+
|
|
35
|
+
# Colors
|
|
36
|
+
RED='\033[0;31m'
|
|
37
|
+
GREEN='\033[0;32m'
|
|
38
|
+
YELLOW='\033[1;33m'
|
|
39
|
+
CYAN='\033[0;36m'
|
|
40
|
+
BOLD='\033[1m'
|
|
41
|
+
NC='\033[0m'
|
|
42
|
+
|
|
43
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
44
|
+
# Logging
|
|
45
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
46
|
+
|
|
47
|
+
LOG_FILE=""
|
|
48
|
+
START_TIME=""
|
|
49
|
+
|
|
50
|
+
setup_logging() {
|
|
51
|
+
mkdir -p "$LOGS_DIR"
|
|
52
|
+
START_TIME=$(date +%Y%m%d_%H%M%S)
|
|
53
|
+
LOG_FILE="$LOGS_DIR/pushpa_${START_TIME}.log"
|
|
54
|
+
touch "$LOG_FILE"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
log() {
|
|
58
|
+
local level="$1"
|
|
59
|
+
local message="$2"
|
|
60
|
+
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
61
|
+
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
|
|
62
|
+
|
|
63
|
+
case "$level" in
|
|
64
|
+
INFO) echo -e "${CYAN}[$level]${NC} $message" ;;
|
|
65
|
+
OK) echo -e "${GREEN}[$level]${NC} $message" ;;
|
|
66
|
+
WARN) echo -e "${YELLOW}[$level]${NC} $message" ;;
|
|
67
|
+
ERROR) echo -e "${RED}[$level]${NC} $message" ;;
|
|
68
|
+
SKIP) echo -e "${YELLOW}[SKIP]${NC} $message" ;;
|
|
69
|
+
*) echo "[$level] $message" ;;
|
|
70
|
+
esac
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
74
|
+
# Banner
|
|
75
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
76
|
+
|
|
77
|
+
print_banner() {
|
|
78
|
+
echo ""
|
|
79
|
+
echo -e "${BOLD}${CYAN}"
|
|
80
|
+
echo "╔═══════════════════════════════════════════════════════════════╗"
|
|
81
|
+
echo "║ ║"
|
|
82
|
+
echo "║ ██████╗ ██╗ ██╗███████╗██╗ ██╗██████╗ █████╗ ║"
|
|
83
|
+
echo "║ ██╔══██╗██║ ██║██╔════╝██║ ██║██╔══██╗██╔══██╗ ║"
|
|
84
|
+
echo "║ ██████╔╝██║ ██║███████╗███████║██████╔╝███████║ ║"
|
|
85
|
+
echo "║ ██╔═══╝ ██║ ██║╚════██║██╔══██║██╔═══╝ ██╔══██║ ║"
|
|
86
|
+
echo "║ ██║ ╚██████╔╝███████║██║ ██║██║ ██║ ██║ ║"
|
|
87
|
+
echo "║ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ║"
|
|
88
|
+
echo "║ ║"
|
|
89
|
+
echo "║ RRR Overnight Autopilot Runner ║"
|
|
90
|
+
echo "║ ║"
|
|
91
|
+
echo "╚═══════════════════════════════════════════════════════════════╝"
|
|
92
|
+
echo -e "${NC}"
|
|
93
|
+
echo ""
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
97
|
+
# Preflight Checks
|
|
98
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
99
|
+
|
|
100
|
+
check_claude_installed() {
|
|
101
|
+
if ! command -v claude &> /dev/null; then
|
|
102
|
+
log ERROR "Claude Code CLI not found. Install it first: https://claude.ai/code"
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
log OK "Claude Code CLI found"
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
check_mvp_features() {
|
|
109
|
+
if [ ! -f "$FEATURES_FILE" ]; then
|
|
110
|
+
echo ""
|
|
111
|
+
echo -e "${RED}═══════════════════════════════════════════════════════════════${NC}"
|
|
112
|
+
echo -e "${RED} ERROR: MVP_FEATURES.yml not found${NC}"
|
|
113
|
+
echo -e "${RED}═══════════════════════════════════════════════════════════════${NC}"
|
|
114
|
+
echo ""
|
|
115
|
+
echo "Pushpa Mode requires a configured project with feature selections."
|
|
116
|
+
echo ""
|
|
117
|
+
echo "To get started:"
|
|
118
|
+
echo " 1. Run: /rrr:new-project"
|
|
119
|
+
echo " 2. Complete the questionnaire and capability selection"
|
|
120
|
+
echo " 3. Set required API keys (see below)"
|
|
121
|
+
echo " 4. Run: bash scripts/pushpa-mode.sh"
|
|
122
|
+
echo ""
|
|
123
|
+
exit 1
|
|
124
|
+
fi
|
|
125
|
+
log OK "MVP_FEATURES.yml found"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
check_planning_exists() {
|
|
129
|
+
if [ ! -d "$PLANNING_DIR" ]; then
|
|
130
|
+
log WARN "No .planning directory found"
|
|
131
|
+
return 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
if [ ! -f "$ROADMAP_FILE" ]; then
|
|
135
|
+
log WARN "No ROADMAP.md found"
|
|
136
|
+
return 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
log OK "Planning directory and roadmap found"
|
|
140
|
+
return 0
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# Check required environment variables based on MVP_FEATURES.yml
|
|
144
|
+
check_env_vars() {
|
|
145
|
+
local missing_vars=()
|
|
146
|
+
local features_content=$(cat "$FEATURES_FILE" 2>/dev/null || echo "")
|
|
147
|
+
|
|
148
|
+
log INFO "Checking required environment variables..."
|
|
149
|
+
|
|
150
|
+
# Neon (database)
|
|
151
|
+
if echo "$features_content" | grep -q "db:.*neon\|db: neon"; then
|
|
152
|
+
[ -z "${NEON_API_KEY:-}" ] && missing_vars+=("NEON_API_KEY")
|
|
153
|
+
[ -z "${DATABASE_URL:-}" ] && missing_vars+=("DATABASE_URL")
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Clerk (auth)
|
|
157
|
+
if echo "$features_content" | grep -q "auth:.*clerk\|auth: clerk"; then
|
|
158
|
+
[ -z "${CLERK_SECRET_KEY:-}" ] && missing_vars+=("CLERK_SECRET_KEY")
|
|
159
|
+
[ -z "${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:-}" ] && missing_vars+=("NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY")
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
# Stripe (payments)
|
|
163
|
+
if echo "$features_content" | grep -q "payments:.*stripe\|payments: stripe"; then
|
|
164
|
+
[ -z "${STRIPE_SECRET_KEY:-}" ] && missing_vars+=("STRIPE_SECRET_KEY")
|
|
165
|
+
[ -z "${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:-}" ] && missing_vars+=("NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY (optional)")
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Cloudflare R2 (storage)
|
|
169
|
+
if echo "$features_content" | grep -q "object_storage:.*r2\|objectStorage:.*r2"; then
|
|
170
|
+
[ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ] && missing_vars+=("CLOUDFLARE_ACCOUNT_ID")
|
|
171
|
+
[ -z "${R2_ACCESS_KEY_ID:-}" ] && missing_vars+=("R2_ACCESS_KEY_ID")
|
|
172
|
+
[ -z "${R2_SECRET_ACCESS_KEY:-}" ] && missing_vars+=("R2_SECRET_ACCESS_KEY")
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Deepgram (voice)
|
|
176
|
+
if echo "$features_content" | grep -q "voice:.*deepgram\|voice: deepgram"; then
|
|
177
|
+
[ -z "${DEEPGRAM_API_KEY:-}" ] && missing_vars+=("DEEPGRAM_API_KEY")
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# PostHog (analytics)
|
|
181
|
+
if echo "$features_content" | grep -q "analytics:.*posthog\|analytics: posthog"; then
|
|
182
|
+
[ -z "${NEXT_PUBLIC_POSTHOG_KEY:-}" ] && missing_vars+=("NEXT_PUBLIC_POSTHOG_KEY")
|
|
183
|
+
[ -z "${NEXT_PUBLIC_POSTHOG_HOST:-}" ] && missing_vars+=("NEXT_PUBLIC_POSTHOG_HOST")
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# E2B (sandbox)
|
|
187
|
+
if echo "$features_content" | grep -q "sandbox:.*e2b\|sandbox: e2b"; then
|
|
188
|
+
[ -z "${E2B_API_KEY:-}" ] && missing_vars+=("E2B_API_KEY")
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Browserbase
|
|
192
|
+
if echo "$features_content" | grep -q "browser.*browserbase\|browserbase"; then
|
|
193
|
+
[ -z "${BROWSERBASE_API_KEY:-}" ] && missing_vars+=("BROWSERBASE_API_KEY")
|
|
194
|
+
[ -z "${BROWSERBASE_PROJECT_ID:-}" ] && missing_vars+=("BROWSERBASE_PROJECT_ID")
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
if [ ${#missing_vars[@]} -gt 0 ]; then
|
|
198
|
+
echo ""
|
|
199
|
+
echo -e "${YELLOW}═══════════════════════════════════════════════════════════════${NC}"
|
|
200
|
+
echo -e "${YELLOW} WARNING: Missing environment variables${NC}"
|
|
201
|
+
echo -e "${YELLOW}═══════════════════════════════════════════════════════════════${NC}"
|
|
202
|
+
echo ""
|
|
203
|
+
echo "Based on your MVP_FEATURES.yml, these variables should be set:"
|
|
204
|
+
echo ""
|
|
205
|
+
for var in "${missing_vars[@]}"; do
|
|
206
|
+
echo -e " ${RED}✗${NC} $var"
|
|
207
|
+
done
|
|
208
|
+
echo ""
|
|
209
|
+
echo "Set them in your environment or .env file before running Pushpa Mode."
|
|
210
|
+
echo ""
|
|
211
|
+
echo "Example:"
|
|
212
|
+
echo " export NEON_API_KEY=your_key_here"
|
|
213
|
+
echo " # or"
|
|
214
|
+
echo " source .env"
|
|
215
|
+
echo ""
|
|
216
|
+
|
|
217
|
+
read -p "Continue anyway? (y/N) " -n 1 -r
|
|
218
|
+
echo
|
|
219
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
220
|
+
log ERROR "Aborted due to missing environment variables"
|
|
221
|
+
exit 1
|
|
222
|
+
fi
|
|
223
|
+
log WARN "Continuing with missing environment variables (user override)"
|
|
224
|
+
else
|
|
225
|
+
log OK "All required environment variables are set"
|
|
226
|
+
fi
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
230
|
+
# Phase Discovery
|
|
231
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
232
|
+
|
|
233
|
+
# Get list of phases from ROADMAP.md
|
|
234
|
+
get_phases() {
|
|
235
|
+
if [ ! -f "$ROADMAP_FILE" ]; then
|
|
236
|
+
echo ""
|
|
237
|
+
return
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
# Extract phase numbers from ROADMAP.md
|
|
241
|
+
# Looks for patterns like "## Phase 1:" or "### Phase 1:" or "| 1 |"
|
|
242
|
+
grep -oE "Phase [0-9]+(\.[0-9]+)?" "$ROADMAP_FILE" 2>/dev/null | \
|
|
243
|
+
grep -oE "[0-9]+(\.[0-9]+)?" | \
|
|
244
|
+
sort -t. -k1,1n -k2,2n | \
|
|
245
|
+
uniq
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# Check if phase has a plan
|
|
249
|
+
phase_has_plan() {
|
|
250
|
+
local phase="$1"
|
|
251
|
+
local phase_padded=$(printf "%02d" "${phase%%.*}")
|
|
252
|
+
|
|
253
|
+
# Look for plan files in the phase directory
|
|
254
|
+
local plan_files=$(find "$PHASES_DIR" -path "*/${phase_padded}*/*-PLAN.md" -type f 2>/dev/null | head -1)
|
|
255
|
+
|
|
256
|
+
if [ -n "$plan_files" ]; then
|
|
257
|
+
return 0
|
|
258
|
+
fi
|
|
259
|
+
return 1
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
# Get plan files for a phase
|
|
263
|
+
get_phase_plans() {
|
|
264
|
+
local phase="$1"
|
|
265
|
+
local phase_padded=$(printf "%02d" "${phase%%.*}")
|
|
266
|
+
|
|
267
|
+
find "$PHASES_DIR" -path "*/${phase_padded}*/*-PLAN.md" -type f 2>/dev/null | sort
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
# Check if phase plan contains HITL marker
|
|
271
|
+
phase_requires_hitl() {
|
|
272
|
+
local phase="$1"
|
|
273
|
+
local plan_files=$(get_phase_plans "$phase")
|
|
274
|
+
|
|
275
|
+
if [ -z "$plan_files" ]; then
|
|
276
|
+
return 1 # No plan, no HITL
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
for plan_file in $plan_files; do
|
|
280
|
+
for marker in "${HITL_MARKERS[@]}"; do
|
|
281
|
+
if grep -q "$marker" "$plan_file" 2>/dev/null; then
|
|
282
|
+
log INFO "Found HITL marker '$marker' in $plan_file"
|
|
283
|
+
return 0
|
|
284
|
+
fi
|
|
285
|
+
done
|
|
286
|
+
done
|
|
287
|
+
|
|
288
|
+
return 1
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
# Check if milestone is complete
|
|
292
|
+
is_milestone_complete() {
|
|
293
|
+
local completion_markers=("MVP COMPLETE" "MILESTONE COMPLETE" "ALL PHASES COMPLETE" "100%")
|
|
294
|
+
|
|
295
|
+
for marker in "${completion_markers[@]}"; do
|
|
296
|
+
if grep -qi "$marker" "$STATE_FILE" 2>/dev/null; then
|
|
297
|
+
return 0
|
|
298
|
+
fi
|
|
299
|
+
if grep -qi "$marker" "$ROADMAP_FILE" 2>/dev/null; then
|
|
300
|
+
return 0
|
|
301
|
+
fi
|
|
302
|
+
done
|
|
303
|
+
|
|
304
|
+
return 1
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
308
|
+
# Execution
|
|
309
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
310
|
+
|
|
311
|
+
# Run a Claude Code command
|
|
312
|
+
run_claude_command() {
|
|
313
|
+
local command="$1"
|
|
314
|
+
local description="$2"
|
|
315
|
+
|
|
316
|
+
log INFO "Running: $command"
|
|
317
|
+
log INFO "Description: $description"
|
|
318
|
+
|
|
319
|
+
# Run claude with the command, capturing output
|
|
320
|
+
local output_file="$LOGS_DIR/claude_${START_TIME}_$(date +%s).log"
|
|
321
|
+
|
|
322
|
+
if claude -p "$command" >> "$output_file" 2>&1; then
|
|
323
|
+
log OK "Command completed successfully"
|
|
324
|
+
return 0
|
|
325
|
+
else
|
|
326
|
+
log ERROR "Command failed. Check $output_file for details"
|
|
327
|
+
return 1
|
|
328
|
+
fi
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# Wait for plan files to appear
|
|
332
|
+
wait_for_plan() {
|
|
333
|
+
local phase="$1"
|
|
334
|
+
local attempts=0
|
|
335
|
+
|
|
336
|
+
log INFO "Waiting for plan files for Phase $phase..."
|
|
337
|
+
|
|
338
|
+
while [ $attempts -lt $MAX_POLL_ATTEMPTS ]; do
|
|
339
|
+
if phase_has_plan "$phase"; then
|
|
340
|
+
log OK "Plan files found for Phase $phase"
|
|
341
|
+
return 0
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
sleep $POLL_INTERVAL
|
|
345
|
+
((attempts++))
|
|
346
|
+
|
|
347
|
+
if [ $((attempts % 6)) -eq 0 ]; then
|
|
348
|
+
log INFO "Still waiting for plan files... (${attempts}/${MAX_POLL_ATTEMPTS})"
|
|
349
|
+
fi
|
|
350
|
+
done
|
|
351
|
+
|
|
352
|
+
log ERROR "Timeout waiting for plan files for Phase $phase"
|
|
353
|
+
return 1
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
# Plan a phase
|
|
357
|
+
plan_phase() {
|
|
358
|
+
local phase="$1"
|
|
359
|
+
|
|
360
|
+
log INFO "Planning Phase $phase..."
|
|
361
|
+
|
|
362
|
+
if ! run_claude_command "/rrr:plan-phase $phase" "Create plan for Phase $phase"; then
|
|
363
|
+
log ERROR "Failed to plan Phase $phase"
|
|
364
|
+
return 1
|
|
365
|
+
fi
|
|
366
|
+
|
|
367
|
+
# Wait for plan files to appear
|
|
368
|
+
if ! wait_for_plan "$phase"; then
|
|
369
|
+
return 1
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
return 0
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
# Execute a phase
|
|
376
|
+
execute_phase() {
|
|
377
|
+
local phase="$1"
|
|
378
|
+
|
|
379
|
+
log INFO "Executing Phase $phase..."
|
|
380
|
+
|
|
381
|
+
if ! run_claude_command "/rrr:execute-phase $phase" "Execute Phase $phase"; then
|
|
382
|
+
log ERROR "Failed to execute Phase $phase"
|
|
383
|
+
return 1
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
log OK "Phase $phase execution complete"
|
|
387
|
+
return 0
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
391
|
+
# Report Generation
|
|
392
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
393
|
+
|
|
394
|
+
# Phase tracking arrays
|
|
395
|
+
declare -a PLANNED_PHASES=()
|
|
396
|
+
declare -a EXECUTED_PHASES=()
|
|
397
|
+
declare -a SKIPPED_PHASES=()
|
|
398
|
+
declare -a FAILED_PHASES=()
|
|
399
|
+
|
|
400
|
+
init_report() {
|
|
401
|
+
cat > "$REPORT_FILE" << EOF
|
|
402
|
+
# Pushpa Mode Report
|
|
403
|
+
|
|
404
|
+
**Started:** $(date '+%Y-%m-%d %H:%M:%S')
|
|
405
|
+
**Log File:** $LOG_FILE
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Summary
|
|
410
|
+
|
|
411
|
+
| Metric | Count |
|
|
412
|
+
|--------|-------|
|
|
413
|
+
| Phases Planned | — |
|
|
414
|
+
| Phases Executed | — |
|
|
415
|
+
| Phases Skipped (HITL) | — |
|
|
416
|
+
| Phases Failed | — |
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## Phase Details
|
|
421
|
+
|
|
422
|
+
EOF
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
update_report() {
|
|
426
|
+
local end_time=$(date '+%Y-%m-%d %H:%M:%S')
|
|
427
|
+
|
|
428
|
+
cat > "$REPORT_FILE" << EOF
|
|
429
|
+
# Pushpa Mode Report
|
|
430
|
+
|
|
431
|
+
**Started:** $(date -d "@$(($(date +%s) - SECONDS))" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo "$(date '+%Y-%m-%d %H:%M:%S')")
|
|
432
|
+
**Completed:** $end_time
|
|
433
|
+
**Duration:** $((SECONDS / 60)) minutes
|
|
434
|
+
**Log File:** $LOG_FILE
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Summary
|
|
439
|
+
|
|
440
|
+
| Metric | Count |
|
|
441
|
+
|--------|-------|
|
|
442
|
+
| Phases Planned | ${#PLANNED_PHASES[@]} |
|
|
443
|
+
| Phases Executed | ${#EXECUTED_PHASES[@]} |
|
|
444
|
+
| Phases Skipped (HITL) | ${#SKIPPED_PHASES[@]} |
|
|
445
|
+
| Phases Failed | ${#FAILED_PHASES[@]} |
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Phase Details
|
|
450
|
+
|
|
451
|
+
EOF
|
|
452
|
+
|
|
453
|
+
if [ ${#EXECUTED_PHASES[@]} -gt 0 ]; then
|
|
454
|
+
echo "### Executed" >> "$REPORT_FILE"
|
|
455
|
+
for phase in "${EXECUTED_PHASES[@]}"; do
|
|
456
|
+
echo "- Phase $phase ✓" >> "$REPORT_FILE"
|
|
457
|
+
done
|
|
458
|
+
echo "" >> "$REPORT_FILE"
|
|
459
|
+
fi
|
|
460
|
+
|
|
461
|
+
if [ ${#SKIPPED_PHASES[@]} -gt 0 ]; then
|
|
462
|
+
echo "### Skipped (HITL Required)" >> "$REPORT_FILE"
|
|
463
|
+
echo "" >> "$REPORT_FILE"
|
|
464
|
+
echo "These phases require human verification and were skipped:" >> "$REPORT_FILE"
|
|
465
|
+
echo "" >> "$REPORT_FILE"
|
|
466
|
+
for phase in "${SKIPPED_PHASES[@]}"; do
|
|
467
|
+
echo "- Phase $phase — **HITL_REQUIRED**" >> "$REPORT_FILE"
|
|
468
|
+
done
|
|
469
|
+
echo "" >> "$REPORT_FILE"
|
|
470
|
+
echo "> Run these phases manually with \`/rrr:execute-phase N\` after verification." >> "$REPORT_FILE"
|
|
471
|
+
echo "" >> "$REPORT_FILE"
|
|
472
|
+
fi
|
|
473
|
+
|
|
474
|
+
if [ ${#FAILED_PHASES[@]} -gt 0 ]; then
|
|
475
|
+
echo "### Failed" >> "$REPORT_FILE"
|
|
476
|
+
echo "" >> "$REPORT_FILE"
|
|
477
|
+
echo "These phases encountered errors:" >> "$REPORT_FILE"
|
|
478
|
+
echo "" >> "$REPORT_FILE"
|
|
479
|
+
for phase in "${FAILED_PHASES[@]}"; do
|
|
480
|
+
echo "- Phase $phase ✗" >> "$REPORT_FILE"
|
|
481
|
+
done
|
|
482
|
+
echo "" >> "$REPORT_FILE"
|
|
483
|
+
echo "> Check logs at \`$LOGS_DIR/\` for details." >> "$REPORT_FILE"
|
|
484
|
+
echo "" >> "$REPORT_FILE"
|
|
485
|
+
fi
|
|
486
|
+
|
|
487
|
+
echo "---" >> "$REPORT_FILE"
|
|
488
|
+
echo "" >> "$REPORT_FILE"
|
|
489
|
+
echo "*Generated by Pushpa Mode*" >> "$REPORT_FILE"
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
493
|
+
# Main
|
|
494
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
495
|
+
|
|
496
|
+
main() {
|
|
497
|
+
print_banner
|
|
498
|
+
setup_logging
|
|
499
|
+
|
|
500
|
+
log INFO "Pushpa Mode starting..."
|
|
501
|
+
log INFO "Working directory: $(pwd)"
|
|
502
|
+
|
|
503
|
+
# Preflight checks
|
|
504
|
+
echo -e "${BOLD}Preflight Checks${NC}"
|
|
505
|
+
echo "─────────────────────────────────────────"
|
|
506
|
+
|
|
507
|
+
check_claude_installed
|
|
508
|
+
check_mvp_features
|
|
509
|
+
check_env_vars
|
|
510
|
+
|
|
511
|
+
# Check if planning exists, run new-project if not
|
|
512
|
+
if ! check_planning_exists; then
|
|
513
|
+
log WARN "Planning not initialized. Running /rrr:new-project..."
|
|
514
|
+
echo ""
|
|
515
|
+
echo -e "${YELLOW}Planning not found. Please run /rrr:new-project first.${NC}"
|
|
516
|
+
echo ""
|
|
517
|
+
echo "Pushpa Mode requires:"
|
|
518
|
+
echo " 1. .planning/ROADMAP.md (phases defined)"
|
|
519
|
+
echo " 2. .planning/MVP_FEATURES.yml (feature selections)"
|
|
520
|
+
echo ""
|
|
521
|
+
echo "Run /rrr:new-project interactively to set up your project,"
|
|
522
|
+
echo "then run Pushpa Mode again."
|
|
523
|
+
exit 1
|
|
524
|
+
fi
|
|
525
|
+
|
|
526
|
+
echo ""
|
|
527
|
+
echo -e "${GREEN}All preflight checks passed!${NC}"
|
|
528
|
+
echo ""
|
|
529
|
+
|
|
530
|
+
# Check if already complete
|
|
531
|
+
if is_milestone_complete; then
|
|
532
|
+
log OK "Milestone is already complete!"
|
|
533
|
+
echo -e "${GREEN}Milestone is already complete. Nothing to do.${NC}"
|
|
534
|
+
exit 0
|
|
535
|
+
fi
|
|
536
|
+
|
|
537
|
+
# Get phases
|
|
538
|
+
local phases=$(get_phases)
|
|
539
|
+
|
|
540
|
+
if [ -z "$phases" ]; then
|
|
541
|
+
log ERROR "No phases found in ROADMAP.md"
|
|
542
|
+
exit 1
|
|
543
|
+
fi
|
|
544
|
+
|
|
545
|
+
log INFO "Found phases: $(echo $phases | tr '\n' ' ')"
|
|
546
|
+
|
|
547
|
+
# Initialize report
|
|
548
|
+
init_report
|
|
549
|
+
|
|
550
|
+
echo -e "${BOLD}Starting Execution${NC}"
|
|
551
|
+
echo "─────────────────────────────────────────"
|
|
552
|
+
echo ""
|
|
553
|
+
|
|
554
|
+
# Process each phase
|
|
555
|
+
for phase in $phases; do
|
|
556
|
+
echo ""
|
|
557
|
+
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
558
|
+
echo -e "${CYAN} Phase $phase${NC}"
|
|
559
|
+
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
560
|
+
|
|
561
|
+
# Check if plan exists
|
|
562
|
+
if ! phase_has_plan "$phase"; then
|
|
563
|
+
log INFO "No plan found for Phase $phase, creating..."
|
|
564
|
+
|
|
565
|
+
if plan_phase "$phase"; then
|
|
566
|
+
PLANNED_PHASES+=("$phase")
|
|
567
|
+
else
|
|
568
|
+
log ERROR "Failed to create plan for Phase $phase"
|
|
569
|
+
FAILED_PHASES+=("$phase")
|
|
570
|
+
continue
|
|
571
|
+
fi
|
|
572
|
+
else
|
|
573
|
+
log OK "Plan already exists for Phase $phase"
|
|
574
|
+
fi
|
|
575
|
+
|
|
576
|
+
# Check for HITL marker
|
|
577
|
+
if phase_requires_hitl "$phase"; then
|
|
578
|
+
log SKIP "Phase $phase requires human verification (HITL_REQUIRED)"
|
|
579
|
+
echo -e "${YELLOW} ⚠ Skipping - Human verification required${NC}"
|
|
580
|
+
SKIPPED_PHASES+=("$phase")
|
|
581
|
+
continue
|
|
582
|
+
fi
|
|
583
|
+
|
|
584
|
+
# Execute phase
|
|
585
|
+
if execute_phase "$phase"; then
|
|
586
|
+
EXECUTED_PHASES+=("$phase")
|
|
587
|
+
echo -e "${GREEN} ✓ Phase $phase complete${NC}"
|
|
588
|
+
else
|
|
589
|
+
FAILED_PHASES+=("$phase")
|
|
590
|
+
echo -e "${RED} ✗ Phase $phase failed${NC}"
|
|
591
|
+
fi
|
|
592
|
+
|
|
593
|
+
# Check if milestone became complete
|
|
594
|
+
if is_milestone_complete; then
|
|
595
|
+
log OK "Milestone complete!"
|
|
596
|
+
break
|
|
597
|
+
fi
|
|
598
|
+
done
|
|
599
|
+
|
|
600
|
+
# Generate final report
|
|
601
|
+
update_report
|
|
602
|
+
|
|
603
|
+
echo ""
|
|
604
|
+
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
|
|
605
|
+
echo -e "${BOLD} Pushpa Mode Complete${NC}"
|
|
606
|
+
echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
|
|
607
|
+
echo ""
|
|
608
|
+
echo "Results:"
|
|
609
|
+
echo " Planned: ${#PLANNED_PHASES[@]} phases"
|
|
610
|
+
echo " Executed: ${#EXECUTED_PHASES[@]} phases"
|
|
611
|
+
echo " Skipped: ${#SKIPPED_PHASES[@]} phases (HITL required)"
|
|
612
|
+
echo " Failed: ${#FAILED_PHASES[@]} phases"
|
|
613
|
+
echo ""
|
|
614
|
+
echo "Report: $REPORT_FILE"
|
|
615
|
+
echo "Logs: $LOG_FILE"
|
|
616
|
+
echo ""
|
|
617
|
+
|
|
618
|
+
if [ ${#SKIPPED_PHASES[@]} -gt 0 ]; then
|
|
619
|
+
echo -e "${YELLOW}HITL phases to complete manually:${NC}"
|
|
620
|
+
for phase in "${SKIPPED_PHASES[@]}"; do
|
|
621
|
+
echo " /rrr:execute-phase $phase"
|
|
622
|
+
done
|
|
623
|
+
echo ""
|
|
624
|
+
fi
|
|
625
|
+
|
|
626
|
+
if [ ${#FAILED_PHASES[@]} -gt 0 ]; then
|
|
627
|
+
echo -e "${RED}Failed phases to investigate:${NC}"
|
|
628
|
+
for phase in "${FAILED_PHASES[@]}"; do
|
|
629
|
+
echo " Phase $phase"
|
|
630
|
+
done
|
|
631
|
+
echo ""
|
|
632
|
+
exit 1
|
|
633
|
+
fi
|
|
634
|
+
|
|
635
|
+
log OK "Pushpa Mode finished successfully"
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
# Run main
|
|
639
|
+
main "$@"
|