root commited on
Commit ·
7fab749
1
Parent(s): dd7e956
feat: real WordPress/Android/React/FastAPI/ReactNative knowledge injection into LLM prompts
Browse files- backend/builder_api/service.py +5 -11
- backend/tools/knowledge/android.md +114 -0
- backend/tools/knowledge/fastapi.md +79 -0
- backend/tools/knowledge/react_native.md +64 -0
- backend/tools/knowledge/react_nextjs.md +97 -0
- backend/tools/knowledge/wordpress.md +73 -0
- backend/tools/knowledge_loader.py +59 -0
- workspace-template/backend/mcp/agents/definitions/builder.md +21 -5
backend/builder_api/service.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import Dict, Any, List
|
|
| 3 |
|
| 4 |
from backend.builder.engine import AutonomousCodeBuilder
|
| 5 |
from backend.models.gateway import model_gateway, DEFAULT_MODEL
|
|
|
|
| 6 |
|
| 7 |
|
| 8 |
|
|
@@ -109,12 +110,8 @@ uvicorn
|
|
| 109 |
description: str,
|
| 110 |
) -> Dict[str, Any]:
|
| 111 |
"""Generate a real FastAPI endpoint via LLM."""
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
f"Description: {description}. "
|
| 115 |
-
f"Assume `app = FastAPI()` already exists. "
|
| 116 |
-
f"Output ONLY the code, no explanation, no markdown fences."
|
| 117 |
-
)
|
| 118 |
|
| 119 |
result = await model_gateway.generate(DEFAULT_MODEL, prompt)
|
| 120 |
|
|
@@ -136,11 +133,8 @@ uvicorn
|
|
| 136 |
model_name = "".join(word.capitalize() for word in table_name.split("_"))
|
| 137 |
field_desc = ", ".join(f"{f['name']}: {f['type']}" for f in fields)
|
| 138 |
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
f"with fields: {field_desc}. "
|
| 142 |
-
f"Output ONLY the code, no explanation, no markdown fences."
|
| 143 |
-
)
|
| 144 |
|
| 145 |
result = await model_gateway.generate(DEFAULT_MODEL, prompt)
|
| 146 |
|
|
|
|
| 3 |
|
| 4 |
from backend.builder.engine import AutonomousCodeBuilder
|
| 5 |
from backend.models.gateway import model_gateway, DEFAULT_MODEL
|
| 6 |
+
from backend.tools.knowledge_loader import build_prompt_with_knowledge
|
| 7 |
|
| 8 |
|
| 9 |
|
|
|
|
| 110 |
description: str,
|
| 111 |
) -> Dict[str, Any]:
|
| 112 |
"""Generate a real FastAPI endpoint via LLM."""
|
| 113 |
+
base_prompt = f"Write a production FastAPI route for {method.upper()} {endpoint_path}. Description: {description}. Assume app = FastAPI() exists. Include all imports."
|
| 114 |
+
prompt = build_prompt_with_knowledge(base_prompt, f"fastapi {description}")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
|
| 116 |
result = await model_gateway.generate(DEFAULT_MODEL, prompt)
|
| 117 |
|
|
|
|
| 133 |
model_name = "".join(word.capitalize() for word in table_name.split("_"))
|
| 134 |
field_desc = ", ".join(f"{f['name']}: {f['type']}" for f in fields)
|
| 135 |
|
| 136 |
+
base_prompt = f"Write a production Pydantic BaseModel named {model_name}Base with fields: {field_desc}. Include SQLAlchemy model too."
|
| 137 |
+
prompt = build_prompt_with_knowledge(base_prompt, "fastapi pydantic sqlalchemy")
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
result = await model_gateway.generate(DEFAULT_MODEL, prompt)
|
| 140 |
|
backend/tools/knowledge/android.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Android Development Reference (Jetpack/Kotlin)
|
| 2 |
+
|
| 3 |
+
## build.gradle (app)
|
| 4 |
+
android {
|
| 5 |
+
compileSdk = 34
|
| 6 |
+
defaultConfig {
|
| 7 |
+
applicationId = "com.example.app"
|
| 8 |
+
minSdk = 24
|
| 9 |
+
targetSdk = 34
|
| 10 |
+
versionCode = 1
|
| 11 |
+
versionName = "1.0"
|
| 12 |
+
}
|
| 13 |
+
buildFeatures { viewBinding = true; compose = true }
|
| 14 |
+
composeOptions { kotlinCompilerExtensionVersion = "1.5.0" }
|
| 15 |
+
}
|
| 16 |
+
dependencies {
|
| 17 |
+
implementation("androidx.core:core-ktx:1.12.0")
|
| 18 |
+
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
|
| 19 |
+
implementation("androidx.navigation:navigation-fragment-ktx:2.7.5")
|
| 20 |
+
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
| 21 |
+
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
|
| 22 |
+
implementation(platform("androidx.compose:compose-bom:2024.01.00"))
|
| 23 |
+
implementation("androidx.compose.ui:ui")
|
| 24 |
+
implementation("androidx.compose.material3:material3")
|
| 25 |
+
implementation("androidx.activity:activity-compose:1.8.2")
|
| 26 |
+
implementation("io.coil-kt:coil-compose:2.5.0")
|
| 27 |
+
implementation("com.google.dagger:hilt-android:2.50")
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
## Jetpack Compose Screen
|
| 31 |
+
@Composable
|
| 32 |
+
fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) {
|
| 33 |
+
val uiState by viewModel.uiState.collectAsState()
|
| 34 |
+
Scaffold(
|
| 35 |
+
topBar = { TopAppBar(title = { Text("App Name") }) }
|
| 36 |
+
) { padding ->
|
| 37 |
+
LazyColumn(modifier = Modifier.padding(padding)) {
|
| 38 |
+
items(uiState.items) { item -> ItemCard(item = item) }
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
@Composable
|
| 44 |
+
fun ItemCard(item: Item) {
|
| 45 |
+
Card(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
|
| 46 |
+
Column(modifier = Modifier.padding(16.dp)) {
|
| 47 |
+
Text(item.title, style = MaterialTheme.typography.headlineSmall)
|
| 48 |
+
Text(item.description, style = MaterialTheme.typography.bodyMedium)
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
## ViewModel + StateFlow
|
| 54 |
+
@HiltViewModel
|
| 55 |
+
class HomeViewModel @Inject constructor(
|
| 56 |
+
private val repository: ItemRepository
|
| 57 |
+
) : ViewModel() {
|
| 58 |
+
private val _uiState = MutableStateFlow(HomeUiState())
|
| 59 |
+
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
|
| 60 |
+
|
| 61 |
+
init { loadItems() }
|
| 62 |
+
|
| 63 |
+
private fun loadItems() {
|
| 64 |
+
viewModelScope.launch {
|
| 65 |
+
_uiState.update { it.copy(isLoading = true) }
|
| 66 |
+
try {
|
| 67 |
+
val items = repository.getItems()
|
| 68 |
+
_uiState.update { it.copy(items = items, isLoading = false) }
|
| 69 |
+
} catch (e: Exception) {
|
| 70 |
+
_uiState.update { it.copy(error = e.message, isLoading = false) }
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
data class HomeUiState(val items: List<Item> = emptyList(), val isLoading: Boolean = false, val error: String? = null)
|
| 76 |
+
|
| 77 |
+
## Retrofit API Service
|
| 78 |
+
interface ApiService {
|
| 79 |
+
@GET("posts") suspend fun getPosts(): List<Post>
|
| 80 |
+
@POST("posts") suspend fun createPost(@Body post: CreatePostRequest): Post
|
| 81 |
+
@GET("posts/{id}") suspend fun getPost(@Path("id") id: Int): Post
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
@Module @InstallIn(SingletonComponent::class)
|
| 85 |
+
object NetworkModule {
|
| 86 |
+
@Provides @Singleton
|
| 87 |
+
fun provideRetrofit(): Retrofit = Retrofit.Builder()
|
| 88 |
+
.baseUrl("https://api.example.com/")
|
| 89 |
+
.addConverterFactory(GsonConverterFactory.create())
|
| 90 |
+
.build()
|
| 91 |
+
|
| 92 |
+
@Provides @Singleton
|
| 93 |
+
fun provideApiService(retrofit: Retrofit): ApiService = retrofit.create(ApiService::class.java)
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
## Room Database
|
| 97 |
+
@Entity(tableName = "items")
|
| 98 |
+
data class ItemEntity(@PrimaryKey(autoGenerate = true) val id: Int = 0, val title: String, val description: String)
|
| 99 |
+
|
| 100 |
+
@Dao
|
| 101 |
+
interface ItemDao {
|
| 102 |
+
@Query("SELECT * FROM items") fun getAll(): Flow<List<ItemEntity>>
|
| 103 |
+
@Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(item: ItemEntity)
|
| 104 |
+
@Delete suspend fun delete(item: ItemEntity)
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
@Database(entities = [ItemEntity::class], version = 1)
|
| 108 |
+
abstract class AppDatabase : RoomDatabase() { abstract fun itemDao(): ItemDao }
|
| 109 |
+
|
| 110 |
+
## AndroidManifest Permissions
|
| 111 |
+
android.permission.INTERNET
|
| 112 |
+
android.permission.ACCESS_NETWORK_STATE
|
| 113 |
+
android.permission.CAMERA
|
| 114 |
+
android.permission.READ_EXTERNAL_STORAGE
|
backend/tools/knowledge/fastapi.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# FastAPI Production Reference
|
| 2 |
+
|
| 3 |
+
## Project Structure
|
| 4 |
+
backend/app/main.py: FastAPI app, middleware, routers
|
| 5 |
+
backend/app/database.py: SQLAlchemy engine, session
|
| 6 |
+
backend/app/models.py: SQLAlchemy ORM models
|
| 7 |
+
backend/app/schemas.py: Pydantic request/response models
|
| 8 |
+
backend/app/auth.py: JWT auth logic
|
| 9 |
+
backend/app/routers/: Route modules
|
| 10 |
+
|
| 11 |
+
## main.py
|
| 12 |
+
from fastapi import FastAPI
|
| 13 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 14 |
+
from .database import engine, Base
|
| 15 |
+
|
| 16 |
+
Base.metadata.create_all(bind=engine)
|
| 17 |
+
app = FastAPI(title="API", version="1.0.0")
|
| 18 |
+
app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:3000"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
|
| 19 |
+
|
| 20 |
+
@app.get("/health")
|
| 21 |
+
def health(): return {"status": "ok"}
|
| 22 |
+
|
| 23 |
+
## database.py
|
| 24 |
+
from sqlalchemy import create_engine
|
| 25 |
+
from sqlalchemy.ext.declarative import declarative_base
|
| 26 |
+
from sqlalchemy.orm import sessionmaker
|
| 27 |
+
import os
|
| 28 |
+
|
| 29 |
+
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./app.db")
|
| 30 |
+
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
| 31 |
+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
| 32 |
+
Base = declarative_base()
|
| 33 |
+
|
| 34 |
+
def get_db():
|
| 35 |
+
db = SessionLocal()
|
| 36 |
+
try: yield db
|
| 37 |
+
finally: db.close()
|
| 38 |
+
|
| 39 |
+
## JWT Auth
|
| 40 |
+
from datetime import datetime, timedelta
|
| 41 |
+
from jose import jwt
|
| 42 |
+
from passlib.context import CryptContext
|
| 43 |
+
from fastapi.security import OAuth2PasswordBearer
|
| 44 |
+
|
| 45 |
+
SECRET_KEY = os.getenv("SECRET_KEY", "changeme")
|
| 46 |
+
ALGORITHM = "HS256"
|
| 47 |
+
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
| 48 |
+
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")
|
| 49 |
+
|
| 50 |
+
def create_access_token(data: dict):
|
| 51 |
+
expire = datetime.utcnow() + timedelta(minutes=30)
|
| 52 |
+
return jwt.encode({**data, "exp": expire}, SECRET_KEY, algorithm=ALGORITHM)
|
| 53 |
+
|
| 54 |
+
def verify_password(plain: str, hashed: str) -> bool:
|
| 55 |
+
return pwd_context.verify(plain, hashed)
|
| 56 |
+
|
| 57 |
+
## CRUD Router
|
| 58 |
+
from fastapi import APIRouter, Depends, HTTPException
|
| 59 |
+
from sqlalchemy.orm import Session
|
| 60 |
+
|
| 61 |
+
router = APIRouter()
|
| 62 |
+
|
| 63 |
+
@router.get("/", response_model=list[schemas.Item])
|
| 64 |
+
def list_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
| 65 |
+
return db.query(models.Item).offset(skip).limit(limit).all()
|
| 66 |
+
|
| 67 |
+
@router.post("/", response_model=schemas.Item, status_code=201)
|
| 68 |
+
def create_item(item: schemas.ItemCreate, db: Session = Depends(get_db)):
|
| 69 |
+
db_item = models.Item(**item.dict())
|
| 70 |
+
db.add(db_item)
|
| 71 |
+
db.commit()
|
| 72 |
+
db.refresh(db_item)
|
| 73 |
+
return db_item
|
| 74 |
+
|
| 75 |
+
@router.get("/{item_id}", response_model=schemas.Item)
|
| 76 |
+
def get_item(item_id: int, db: Session = Depends(get_db)):
|
| 77 |
+
item = db.query(models.Item).filter(models.Item.id == item_id).first()
|
| 78 |
+
if not item: raise HTTPException(status_code=404, detail="Not found")
|
| 79 |
+
return item
|
backend/tools/knowledge/react_native.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# React Native Production Reference (Expo)
|
| 2 |
+
|
| 3 |
+
## Setup
|
| 4 |
+
npx create-expo-app MyApp --template blank-typescript
|
| 5 |
+
npx expo install expo-router react-native-safe-area-context react-native-screens
|
| 6 |
+
|
| 7 |
+
## App Structure
|
| 8 |
+
app/(tabs)/index.tsx: Home tab
|
| 9 |
+
app/(tabs)/explore.tsx: Explore tab
|
| 10 |
+
app/(tabs)/_layout.tsx: Tab navigator
|
| 11 |
+
app/_layout.tsx: Root layout
|
| 12 |
+
components/: Shared components
|
| 13 |
+
|
| 14 |
+
## Core Screen
|
| 15 |
+
import { View, Text, StyleSheet, TouchableOpacity, FlatList, TextInput, ScrollView } from 'react-native'
|
| 16 |
+
import { SafeAreaView } from 'react-native-safe-area-context'
|
| 17 |
+
|
| 18 |
+
export default function HomeScreen() {
|
| 19 |
+
const [text, setText] = useState('')
|
| 20 |
+
const [items, setItems] = useState([])
|
| 21 |
+
|
| 22 |
+
return (
|
| 23 |
+
<SafeAreaView style={styles.container}>
|
| 24 |
+
<ScrollView>
|
| 25 |
+
<Text style={styles.title}>Home</Text>
|
| 26 |
+
<TextInput style={styles.input} value={text} onChangeText={setText} placeholder="Search..." />
|
| 27 |
+
<FlatList
|
| 28 |
+
data={items}
|
| 29 |
+
keyExtractor={(item) => item.id.toString()}
|
| 30 |
+
renderItem={({ item }) => (
|
| 31 |
+
<TouchableOpacity style={styles.card} onPress={() => {}}>
|
| 32 |
+
<Text style={styles.cardTitle}>{item.title}</Text>
|
| 33 |
+
</TouchableOpacity>
|
| 34 |
+
)}
|
| 35 |
+
/>
|
| 36 |
+
</ScrollView>
|
| 37 |
+
</SafeAreaView>
|
| 38 |
+
)
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
const styles = StyleSheet.create({
|
| 42 |
+
container: { flex: 1, backgroundColor: '#fff' },
|
| 43 |
+
title: { fontSize: 24, fontWeight: 'bold', padding: 16 },
|
| 44 |
+
input: { borderWidth: 1, borderColor: '#ddd', borderRadius: 8, padding: 12, margin: 16 },
|
| 45 |
+
card: { backgroundColor: '#f5f5f5', padding: 16, marginHorizontal: 16, marginBottom: 8, borderRadius: 8 },
|
| 46 |
+
cardTitle: { fontSize: 16, fontWeight: '600' },
|
| 47 |
+
})
|
| 48 |
+
|
| 49 |
+
## Navigation
|
| 50 |
+
import { Link, useRouter } from 'expo-router'
|
| 51 |
+
<Link href="/detail/123">Go to detail</Link>
|
| 52 |
+
const router = useRouter()
|
| 53 |
+
router.push('/detail/123')
|
| 54 |
+
router.replace('/home')
|
| 55 |
+
router.back()
|
| 56 |
+
|
| 57 |
+
## Axios API Client
|
| 58 |
+
import axios from 'axios'
|
| 59 |
+
const api = axios.create({ baseURL: 'https://api.example.com', timeout: 10000 })
|
| 60 |
+
api.interceptors.request.use(async (config) => {
|
| 61 |
+
const token = await AsyncStorage.getItem('token')
|
| 62 |
+
if (token) config.headers.Authorization = 'Bearer ' + token
|
| 63 |
+
return config
|
| 64 |
+
})
|
backend/tools/knowledge/react_nextjs.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# React / Next.js 14 Production Reference
|
| 2 |
+
|
| 3 |
+
## App Router Structure
|
| 4 |
+
app/layout.tsx: Root layout with metadata
|
| 5 |
+
app/page.tsx: Home page
|
| 6 |
+
app/[slug]/page.tsx: Dynamic route
|
| 7 |
+
app/api/route.ts: API route handler
|
| 8 |
+
app/loading.tsx: Loading UI
|
| 9 |
+
app/error.tsx: Error boundary
|
| 10 |
+
components/: Shared components
|
| 11 |
+
lib/: Utilities, db clients
|
| 12 |
+
|
| 13 |
+
## layout.tsx
|
| 14 |
+
import type { Metadata } from 'next'
|
| 15 |
+
import { Inter } from 'next/font/google'
|
| 16 |
+
import './globals.css'
|
| 17 |
+
|
| 18 |
+
const inter = Inter({ subsets: ['latin'] })
|
| 19 |
+
|
| 20 |
+
export const metadata: Metadata = {
|
| 21 |
+
title: 'App Name',
|
| 22 |
+
description: 'App description',
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
| 26 |
+
return (
|
| 27 |
+
<html lang="en">
|
| 28 |
+
<body className={inter.className}>{children}</body>
|
| 29 |
+
</html>
|
| 30 |
+
)
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
## Server Component
|
| 34 |
+
async function getData() {
|
| 35 |
+
const res = await fetch('https://api.example.com/data', { next: { revalidate: 3600 } })
|
| 36 |
+
if (!res.ok) throw new Error('Failed to fetch')
|
| 37 |
+
return res.json()
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
export default async function Page() {
|
| 41 |
+
const data = await getData()
|
| 42 |
+
return (
|
| 43 |
+
<main>
|
| 44 |
+
<h1>{data.title}</h1>
|
| 45 |
+
{data.items.map((item: any) => <div key={item.id}>{item.name}</div>)}
|
| 46 |
+
</main>
|
| 47 |
+
)
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
## Client Component
|
| 51 |
+
'use client'
|
| 52 |
+
import { useState, useEffect } from 'react'
|
| 53 |
+
|
| 54 |
+
export default function Counter() {
|
| 55 |
+
const [count, setCount] = useState(0)
|
| 56 |
+
useEffect(() => { document.title = 'Count: ' + count }, [count])
|
| 57 |
+
return <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
## API Route Handler
|
| 61 |
+
import { NextRequest, NextResponse } from 'next/server'
|
| 62 |
+
|
| 63 |
+
export async function GET(request: NextRequest) {
|
| 64 |
+
const { searchParams } = new URL(request.url)
|
| 65 |
+
const id = searchParams.get('id')
|
| 66 |
+
return NextResponse.json({ id, data: 'example' })
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
export async function POST(request: NextRequest) {
|
| 70 |
+
const body = await request.json()
|
| 71 |
+
return NextResponse.json({ success: true }, { status: 201 })
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
## Tailwind Card Grid
|
| 75 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
| 76 |
+
{items.map(item => (
|
| 77 |
+
<div key={item.id} className="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
|
| 78 |
+
<h3 className="text-xl font-semibold text-gray-900">{item.title}</h3>
|
| 79 |
+
<p className="text-gray-600 mt-2">{item.description}</p>
|
| 80 |
+
<button className="mt-4 bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700">Learn More</button>
|
| 81 |
+
</div>
|
| 82 |
+
))}
|
| 83 |
+
</div>
|
| 84 |
+
|
| 85 |
+
## Prisma ORM
|
| 86 |
+
import { PrismaClient } from '@prisma/client'
|
| 87 |
+
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }
|
| 88 |
+
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
|
| 89 |
+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
|
| 90 |
+
|
| 91 |
+
## Zustand State
|
| 92 |
+
import { create } from 'zustand'
|
| 93 |
+
interface AppStore { user: User | null; setUser: (user: User | null) => void }
|
| 94 |
+
export const useAppStore = create<AppStore>((set) => ({
|
| 95 |
+
user: null,
|
| 96 |
+
setUser: (user) => set({ user }),
|
| 97 |
+
}))
|
backend/tools/knowledge/wordpress.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# WordPress Development Reference
|
| 2 |
+
|
| 3 |
+
## Theme Structure
|
| 4 |
+
- style.css: Theme header with Name, Version, Description
|
| 5 |
+
- index.php: Main template fallback
|
| 6 |
+
- functions.php: Theme setup, enqueue scripts/styles
|
| 7 |
+
- header.php / footer.php: Site header and footer
|
| 8 |
+
- single.php: Single post template
|
| 9 |
+
- page.php: Static page template
|
| 10 |
+
- archive.php: Archive/category/tag template
|
| 11 |
+
- 404.php: Not found page
|
| 12 |
+
- sidebar.php: Widget area
|
| 13 |
+
|
| 14 |
+
## functions.php Essentials
|
| 15 |
+
function theme_setup() {
|
| 16 |
+
add_theme_support('title-tag');
|
| 17 |
+
add_theme_support('post-thumbnails');
|
| 18 |
+
add_theme_support('html5', ['search-form','comment-form','comment-list','gallery','caption']);
|
| 19 |
+
register_nav_menus(['primary' => 'Primary Menu']);
|
| 20 |
+
}
|
| 21 |
+
add_action('after_setup_theme', 'theme_setup');
|
| 22 |
+
|
| 23 |
+
function theme_scripts() {
|
| 24 |
+
wp_enqueue_style('theme-style', get_stylesheet_uri(), [], '1.0.0');
|
| 25 |
+
wp_enqueue_script('theme-script', get_template_directory_uri() . '/assets/js/main.js', ['jquery'], '1.0.0', true);
|
| 26 |
+
}
|
| 27 |
+
add_action('wp_enqueue_scripts', 'theme_scripts');
|
| 28 |
+
|
| 29 |
+
## WordPress REST API
|
| 30 |
+
Base URL: /wp-json/wp/v2/
|
| 31 |
+
Posts: GET /wp-json/wp/v2/posts
|
| 32 |
+
Pages: GET /wp-json/wp/v2/pages
|
| 33 |
+
|
| 34 |
+
Custom endpoint:
|
| 35 |
+
add_action('rest_api_init', function() {
|
| 36 |
+
register_rest_route('mytheme/v1', '/data', [
|
| 37 |
+
'methods' => 'GET',
|
| 38 |
+
'callback' => 'my_rest_callback',
|
| 39 |
+
'permission_callback' => '__return_true',
|
| 40 |
+
]);
|
| 41 |
+
});
|
| 42 |
+
|
| 43 |
+
## Custom Post Types
|
| 44 |
+
function register_portfolio_cpt() {
|
| 45 |
+
register_post_type('portfolio', [
|
| 46 |
+
'labels' => ['name' => 'Portfolio', 'singular_name' => 'Project'],
|
| 47 |
+
'public' => true,
|
| 48 |
+
'has_archive' => true,
|
| 49 |
+
'supports' => ['title', 'editor', 'thumbnail', 'excerpt'],
|
| 50 |
+
'show_in_rest' => true,
|
| 51 |
+
'rewrite' => ['slug' => 'portfolio'],
|
| 52 |
+
]);
|
| 53 |
+
}
|
| 54 |
+
add_action('init', 'register_portfolio_cpt');
|
| 55 |
+
|
| 56 |
+
## WooCommerce Integration
|
| 57 |
+
- Check active: function_exists('WC')
|
| 58 |
+
- Get products: wc_get_products(['limit' => 10, 'status' => 'publish'])
|
| 59 |
+
- Product price: $product->get_price()
|
| 60 |
+
- Cart URL: wc_get_cart_url()
|
| 61 |
+
- Shop URL: get_permalink(wc_get_page_id('shop'))
|
| 62 |
+
|
| 63 |
+
## ACF Fields
|
| 64 |
+
- Get field: get_field('field_name')
|
| 65 |
+
- Display: the_field('field_name')
|
| 66 |
+
- Repeater: while(have_rows('repeater')): the_row(); get_sub_field('sub'); endwhile;
|
| 67 |
+
|
| 68 |
+
## Security
|
| 69 |
+
- Escape output: esc_html(), esc_url(), esc_attr()
|
| 70 |
+
- Sanitize input: sanitize_text_field(), wp_kses_post()
|
| 71 |
+
- Nonce: wp_nonce_field(), check_admin_referer()
|
| 72 |
+
- Prepare SQL: $wpdb->prepare()
|
| 73 |
+
- Capabilities: current_user_can('edit_posts')
|
backend/tools/knowledge_loader.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Knowledge loader for MCP builder tools.
|
| 3 |
+
Loads relevant documentation into LLM prompts based on build target.
|
| 4 |
+
"""
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
KNOWLEDGE_DIR = Path(__file__).parent / "knowledge"
|
| 8 |
+
|
| 9 |
+
KNOWLEDGE_MAP = {
|
| 10 |
+
"wordpress": "wordpress.md",
|
| 11 |
+
"wp": "wordpress.md",
|
| 12 |
+
"woocommerce": "wordpress.md",
|
| 13 |
+
"android": "android.md",
|
| 14 |
+
"kotlin": "android.md",
|
| 15 |
+
"jetpack": "android.md",
|
| 16 |
+
"react": "react_nextjs.md",
|
| 17 |
+
"nextjs": "react_nextjs.md",
|
| 18 |
+
"next.js": "react_nextjs.md",
|
| 19 |
+
"next": "react_nextjs.md",
|
| 20 |
+
"tailwind": "react_nextjs.md",
|
| 21 |
+
"fastapi": "fastapi.md",
|
| 22 |
+
"python": "fastapi.md",
|
| 23 |
+
"sqlalchemy": "fastapi.md",
|
| 24 |
+
"pydantic": "fastapi.md",
|
| 25 |
+
"react-native": "react_native.md",
|
| 26 |
+
"react_native": "react_native.md",
|
| 27 |
+
"expo": "react_native.md",
|
| 28 |
+
"mobile": "react_native.md",
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def load_knowledge(context: str) -> str:
|
| 33 |
+
context_lower = context.lower()
|
| 34 |
+
matched_file = None
|
| 35 |
+
for keyword, filename in KNOWLEDGE_MAP.items():
|
| 36 |
+
if keyword in context_lower:
|
| 37 |
+
matched_file = filename
|
| 38 |
+
break
|
| 39 |
+
if not matched_file:
|
| 40 |
+
return ""
|
| 41 |
+
knowledge_path = KNOWLEDGE_DIR / matched_file
|
| 42 |
+
if not knowledge_path.exists():
|
| 43 |
+
return ""
|
| 44 |
+
return knowledge_path.read_text(encoding="utf-8")
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def build_prompt_with_knowledge(base_prompt: str, context: str) -> str:
|
| 48 |
+
knowledge = load_knowledge(context)
|
| 49 |
+
if not knowledge:
|
| 50 |
+
return base_prompt + "\n\nOutput ONLY production-ready code. No placeholders, no TODOs, no mock data."
|
| 51 |
+
return f"""You are a senior software engineer. Use this reference documentation:
|
| 52 |
+
|
| 53 |
+
{knowledge}
|
| 54 |
+
|
| 55 |
+
---
|
| 56 |
+
|
| 57 |
+
{base_prompt}
|
| 58 |
+
|
| 59 |
+
Output ONLY production-ready code. No placeholders, no TODOs, no mock data, no comments saying 'add logic here'."""
|
workspace-template/backend/mcp/agents/definitions/builder.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
| 1 |
-
# Builder
|
| 2 |
|
| 3 |
-
|
| 4 |
-
-
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Builder Agent
|
| 2 |
|
| 3 |
+
## Role
|
| 4 |
+
Senior full-stack software engineer. Build real, production-ready applications.
|
| 5 |
+
|
| 6 |
+
## Rules
|
| 7 |
+
- NEVER output placeholder code, mock data, TODOs, or "add logic here" comments
|
| 8 |
+
- NEVER output incomplete functions or stubs
|
| 9 |
+
- ALWAYS output working, runnable code with all imports included
|
| 10 |
+
- Use the reference documentation provided in the prompt exactly
|
| 11 |
+
- Follow framework best practices as shown in the reference docs
|
| 12 |
+
|
| 13 |
+
## Capabilities
|
| 14 |
+
- WordPress themes, plugins, WooCommerce, REST API, ACF, CPTs
|
| 15 |
+
- Android with Kotlin, Jetpack Compose, ViewModel, Room, Retrofit, Hilt
|
| 16 |
+
- React / Next.js 14 App Router, TypeScript, Tailwind CSS, Prisma, Zustand
|
| 17 |
+
- FastAPI backends with SQLAlchemy, JWT auth, Pydantic, CORS, CRUD routers
|
| 18 |
+
- React Native / Expo with navigation, API integration, AsyncStorage
|
| 19 |
+
- Docker, docker-compose, deployment configs
|
| 20 |
+
|
| 21 |
+
## Output
|
| 22 |
+
Return ONLY the requested code. No explanations unless asked.
|