mirror of
https://github.com/NodeOps-app/skills.git
synced 2026-09-19 07:25:09 +08:00
createos-fullstack-skills (#4)
This commit is contained in:
+1077
-180
File diff suppressed because it is too large
Load Diff
@@ -92,13 +92,10 @@ For image/upload projects:
|
||||
"type": "vcs",
|
||||
"status": "active",
|
||||
"createdAt": "2025-01-15T10:30:00Z",
|
||||
"url": "https://my-app.nodeops.app"
|
||||
"url": "https://my-app.createos.io"
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Always use the `url` field from the response. Never construct URLs manually - the domain may vary.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### List Projects
|
||||
@@ -259,10 +256,8 @@ For image/upload projects:
|
||||
"image": "nginx:latest",
|
||||
"createdAt": "2025-01-15T10:30:00Z",
|
||||
"deployedAt": "2025-01-15T10:35:00Z",
|
||||
"url": "https://my-app.nodeops.app"
|
||||
"url": "https://deployment-hash.createos.io"
|
||||
}
|
||||
|
||||
**Important:** The `url` field contains the actual live URL. Always share this exact URL with users - never construct URLs manually.
|
||||
```
|
||||
|
||||
**Deployment Statuses:** `queued`, `building`, `deploying`, `deployed`, `failed`, `sleeping`
|
||||
|
||||
@@ -1,428 +0,0 @@
|
||||
# CreateOS Core Skills Reference
|
||||
|
||||
Detailed documentation for all CreateOS operations. Load this reference when you need specifics beyond the quick start.
|
||||
|
||||
---
|
||||
|
||||
## Project Management
|
||||
|
||||
### Create Projects
|
||||
|
||||
#### Project Types
|
||||
|
||||
| Type | Description | Best For |
|
||||
|------|-------------|----------|
|
||||
| `vcs` | GitHub-connected repository | Production apps with CI/CD |
|
||||
| `image` | Docker container deployment | Pre-built images, complex deps |
|
||||
| `upload` | Direct file upload | Quick prototypes, static sites |
|
||||
|
||||
#### VCS Project
|
||||
|
||||
```json
|
||||
CreateProject({
|
||||
"uniqueName": "my-nextjs-app",
|
||||
"displayName": "My Next.js Application",
|
||||
"type": "vcs",
|
||||
"source": {
|
||||
"vcsName": "github",
|
||||
"vcsInstallationId": "12345678",
|
||||
"vcsRepoId": "98765432"
|
||||
},
|
||||
"settings": {
|
||||
"framework": "nextjs",
|
||||
"runtime": "node:20",
|
||||
"port": 3000,
|
||||
"directoryPath": ".",
|
||||
"installCommand": "npm install",
|
||||
"buildCommand": "npm run build",
|
||||
"runCommand": "npm start",
|
||||
"buildVars": {"NODE_ENV": "production"},
|
||||
"runEnvs": {"DATABASE_URL": "postgresql://..."},
|
||||
"ignoreBranches": ["develop", "feature/*"],
|
||||
"hasDockerfile": false,
|
||||
"useBuildAI": false
|
||||
},
|
||||
"appId": "optional-app-uuid",
|
||||
"enabledSecurityScan": true
|
||||
})
|
||||
```
|
||||
|
||||
**Prerequisites**: GitHub account connected, repository access granted
|
||||
|
||||
**Pitfalls**: Incorrect `vcsRepoId` causes failures; missing `port` fails health checks; `buildVars` (build-time) vs `runEnvs` (runtime)
|
||||
|
||||
#### Image Project
|
||||
|
||||
```json
|
||||
CreateProject({
|
||||
"uniqueName": "my-api-service",
|
||||
"displayName": "My API Service",
|
||||
"type": "image",
|
||||
"source": {},
|
||||
"settings": {
|
||||
"port": 8080,
|
||||
"runEnvs": {"API_KEY": "secret", "LOG_LEVEL": "info"}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Upload Project
|
||||
|
||||
```json
|
||||
CreateProject({
|
||||
"uniqueName": "quick-prototype",
|
||||
"displayName": "Quick Prototype",
|
||||
"type": "upload",
|
||||
"source": {},
|
||||
"settings": {
|
||||
"framework": "express",
|
||||
"runtime": "node:20",
|
||||
"port": 3000,
|
||||
"installCommand": "npm install",
|
||||
"buildCommand": "npm run build",
|
||||
"buildDir": "dist",
|
||||
"useBuildAI": true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Update Project Settings
|
||||
|
||||
```json
|
||||
UpdateProjectSettings(project_id, {
|
||||
"framework": "nextjs",
|
||||
"runtime": "node:22",
|
||||
"port": 3000,
|
||||
"installCommand": "npm ci",
|
||||
"buildCommand": "npm run build",
|
||||
"runCommand": "npm start",
|
||||
"buildDir": ".next",
|
||||
"buildVars": {"NODE_ENV": "production"},
|
||||
"runEnvs": {"NEW_VAR": "value"},
|
||||
"ignoreBranches": ["wip/*"],
|
||||
"hasDockerfile": false,
|
||||
"useBuildAI": false
|
||||
})
|
||||
```
|
||||
|
||||
**Edge cases**: Changing `runtime` triggers rebuild; changing `port` requires redeployment; `ignoreBranches` affects future pushes only
|
||||
|
||||
### Project Lifecycle
|
||||
|
||||
| Operation | Tool |
|
||||
|-----------|------|
|
||||
| List | `ListProjects(limit?, offset?, name?, type?, status?, app?)` |
|
||||
| Get | `GetProject(project_id)` |
|
||||
| Update | `UpdateProject(project_id, {displayName, description?, enabledSecurityScan?})` |
|
||||
| Delete | `DeleteProject(project_id)` — async |
|
||||
| Check name | `CheckProjectUniqueName({uniqueName})` |
|
||||
|
||||
### Project Transfer
|
||||
|
||||
```
|
||||
1. Owner: GetProjectTransferUri(project_id) → {uri, token} (valid 6 hours)
|
||||
2. Owner: Share URI with recipient
|
||||
3. Recipient: TransferProject(project_id, token)
|
||||
4. Audit: ListProjectTransferHistory(project_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployments
|
||||
|
||||
### Trigger Deployments
|
||||
|
||||
**VCS Projects** — Push to GitHub (auto) or manual:
|
||||
```json
|
||||
TriggerLatestDeployment(project_id, branch?)
|
||||
```
|
||||
|
||||
**Image Projects**:
|
||||
```json
|
||||
CreateDeployment(project_id, {"image": "nginx:latest"})
|
||||
```
|
||||
|
||||
**Upload Projects**:
|
||||
```json
|
||||
// Text files
|
||||
UploadDeploymentFiles(project_id, {
|
||||
"files": [
|
||||
{"path": "package.json", "content": "{...}"},
|
||||
{"path": "index.js", "content": "..."}
|
||||
]
|
||||
})
|
||||
|
||||
// Binary files (base64)
|
||||
UploadDeploymentBase64Files(project_id, {
|
||||
"files": [{"path": "logo.png", "content": "iVBORw0KGgo..."}]
|
||||
})
|
||||
|
||||
// ZIP archive
|
||||
UploadDeploymentZip(project_id, {file: zipBinaryData})
|
||||
```
|
||||
|
||||
**Limits**: Max 100 files per upload; use ZIP for larger projects
|
||||
|
||||
### Deployment States
|
||||
|
||||
```
|
||||
queued → building → deploying → deployed
|
||||
↓ ↓
|
||||
failed sleeping
|
||||
```
|
||||
|
||||
| State | Actions |
|
||||
|-------|---------|
|
||||
| `queued` | Cancel |
|
||||
| `building` | Cancel, View logs |
|
||||
| `deploying` | Wait |
|
||||
| `deployed` | Assign to env |
|
||||
| `failed` | Retry, View logs |
|
||||
| `sleeping` | Wake up |
|
||||
|
||||
### Deployment Operations
|
||||
|
||||
| Operation | Tool |
|
||||
|-----------|------|
|
||||
| List | `ListDeployments(project_id, limit?, offset?)` |
|
||||
| Get | `GetDeployment(project_id, deployment_id)` |
|
||||
| Retry | `RetriggerDeployment(project_id, deployment_id, settings?)` |
|
||||
| Cancel | `CancelDeployment(project_id, deployment_id)` |
|
||||
| Delete | `DeleteDeployment(project_id, deployment_id)` |
|
||||
| Wake | `WakeupDeployment(project_id, deployment_id)` |
|
||||
| Download | `DownloadDeployment(project_id, deployment_id)` — upload only |
|
||||
|
||||
### Debug with Logs
|
||||
|
||||
```json
|
||||
// Build logs
|
||||
GetBuildLogs(project_id, deployment_id, skip?)
|
||||
|
||||
// Runtime logs
|
||||
GetDeploymentLogs(project_id, deployment_id, since-seconds?)
|
||||
|
||||
// Environment logs
|
||||
GetProjectEnvironmentLogs(project_id, environment_id, since-seconds?)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environments
|
||||
|
||||
### Create Environment
|
||||
|
||||
**VCS Project** (branch required):
|
||||
```json
|
||||
CreateProjectEnvironment(project_id, {
|
||||
"displayName": "Production",
|
||||
"uniqueName": "production",
|
||||
"description": "Live production environment",
|
||||
"branch": "main",
|
||||
"isAutoPromoteEnabled": true,
|
||||
"resources": {"cpu": 500, "memory": 1024, "replicas": 2},
|
||||
"settings": {
|
||||
"runEnvs": {
|
||||
"NODE_ENV": "production",
|
||||
"DATABASE_URL": "postgresql://..."
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Image Project** (no branch):
|
||||
```json
|
||||
CreateProjectEnvironment(project_id, {
|
||||
"displayName": "Production",
|
||||
"uniqueName": "production",
|
||||
"resources": {"cpu": 500, "memory": 1024, "replicas": 2},
|
||||
"settings": {"runEnvs": {"NODE_ENV": "production"}}
|
||||
})
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
| Resource | Min | Max | Unit |
|
||||
|----------|-----|-----|------|
|
||||
| CPU | 200 | 500 | millicores |
|
||||
| Memory | 500 | 1024 | MB |
|
||||
| Replicas | 1 | 3 | instances |
|
||||
|
||||
```json
|
||||
UpdateProjectEnvironmentResources(project_id, environment_id, {
|
||||
"cpu": 500, "memory": 1024, "replicas": 3
|
||||
})
|
||||
```
|
||||
|
||||
**Notes**: Replicas > 1 requires stateless app; memory exceeded = OOM kill; CPU exceeded = throttle
|
||||
|
||||
### Environment Variables
|
||||
|
||||
```json
|
||||
UpdateProjectEnvironmentEnvironmentVariables(project_id, environment_id, {
|
||||
"runEnvs": {
|
||||
"DATABASE_URL": "postgresql://...",
|
||||
"API_KEY": "secret"
|
||||
},
|
||||
"port": 8080 // Image projects only
|
||||
})
|
||||
```
|
||||
|
||||
### Deployment Assignment
|
||||
|
||||
```json
|
||||
AssignDeploymentToProjectEnvironment(project_id, environment_id, {
|
||||
"deploymentId": "deployment-uuid"
|
||||
})
|
||||
```
|
||||
|
||||
Use for: rollbacks, blue-green switching, canary releases
|
||||
|
||||
---
|
||||
|
||||
## Domains
|
||||
|
||||
### Add Custom Domain
|
||||
|
||||
```json
|
||||
CreateDomain(project_id, {
|
||||
"name": "api.mycompany.com",
|
||||
"environmentId": "optional-env-uuid"
|
||||
})
|
||||
```
|
||||
|
||||
Response includes CNAME target for DNS configuration.
|
||||
|
||||
### Verification Flow
|
||||
|
||||
1. `CreateDomain` → Status: pending
|
||||
2. Configure DNS CNAME at registrar
|
||||
3. Wait for propagation (up to 48h)
|
||||
4. `RefreshDomain(project_id, domain_id)` → Status: active
|
||||
|
||||
### Domain Operations
|
||||
|
||||
| Operation | Tool |
|
||||
|-----------|------|
|
||||
| List | `ListDomains(project_id)` |
|
||||
| Verify | `RefreshDomain(project_id, domain_id)` |
|
||||
| Assign | `UpdateDomainEnvironment(project_id, domain_id, {environmentId})` |
|
||||
| Delete | `DeleteDomain(project_id, domain_id)` |
|
||||
|
||||
---
|
||||
|
||||
## GitHub Integration
|
||||
|
||||
### Connect Account
|
||||
|
||||
```json
|
||||
InstallGithubApp({
|
||||
"installationId": 12345678,
|
||||
"code": "oauth-code-from-github"
|
||||
})
|
||||
```
|
||||
|
||||
### Repository Discovery
|
||||
|
||||
```json
|
||||
ListConnectedGithubAccounts()
|
||||
ListGithubRepositories(installation_id)
|
||||
ListGithubRepositoryBranches(installation_id, "owner/repo")
|
||||
GetGithubRepositoryContent(installation_id, {"repository": "owner/repo", "branch": "main"})
|
||||
```
|
||||
|
||||
### Auto-Deploy Config
|
||||
|
||||
```json
|
||||
// Ignore branches
|
||||
UpdateProjectSettings(project_id, {"ignoreBranches": ["develop", "feature/*"]})
|
||||
|
||||
// Auto-promote to environment
|
||||
CreateProjectEnvironment(project_id, {"branch": "main", "isAutoPromoteEnabled": true, ...})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Analytics
|
||||
|
||||
### Comprehensive Analytics
|
||||
|
||||
```json
|
||||
GetProjectEnvironmentAnalytics(project_id, environment_id, {
|
||||
"start": 1704067200, "end": 1704070800
|
||||
})
|
||||
```
|
||||
|
||||
### Individual Metrics
|
||||
|
||||
| Metric | Tool |
|
||||
|--------|------|
|
||||
| Overall | `GetProjectEnvironmentAnalyticsOverallRequests` |
|
||||
| RPM | `GetProjectEnvironmentAnalyticsRPM` |
|
||||
| Success % | `GetProjectEnvironmentAnalyticsSuccessPercentage` |
|
||||
| Time series | `GetProjectEnvironmentAnalyticsRequestsOverTime` |
|
||||
| Top paths | `GetProjectEnvironmentAnalyticsTopHitPaths` |
|
||||
| Errors | `GetProjectEnvironmentAnalyticsTopErrorPaths` |
|
||||
| Distribution | `GetEnvAnalyticsReqDistribution` |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Vulnerability Scanning
|
||||
|
||||
```json
|
||||
// Enable
|
||||
UpdateProject(project_id, {"enabledSecurityScan": true})
|
||||
|
||||
// Trigger
|
||||
TriggerSecurityScan(project_id, deployment_id)
|
||||
|
||||
// View
|
||||
GetSecurityScan(project_id, deployment_id)
|
||||
|
||||
// Download report
|
||||
GetSecurityScanDownloadUri(project_id, deployment_id)
|
||||
|
||||
// Retry
|
||||
RetriggerSecurityScan(project_id, deployment_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Apps (Organization)
|
||||
|
||||
### Group Projects
|
||||
|
||||
```json
|
||||
CreateApp({"name": "E-Commerce Platform", "description": "...", "color": "#3B82F6"})
|
||||
AddProjectsToApp(app_id, {"projectIds": ["uuid1", "uuid2"]})
|
||||
RemoveProjectsFromApp(app_id, {"projectIds": ["uuid1"]})
|
||||
ListProjectsByApp(app_id)
|
||||
```
|
||||
|
||||
Deleting app unassigns projects (doesn't delete them).
|
||||
|
||||
---
|
||||
|
||||
## API Keys
|
||||
|
||||
```json
|
||||
// Create (key shown only once!)
|
||||
CreateAPIKey({"name": "prod-key", "description": "...", "expiryAt": "2025-12-31T23:59:59Z"})
|
||||
|
||||
// List
|
||||
ListAPIKeys()
|
||||
|
||||
// Revoke
|
||||
RevokeAPIKey(api_key_id)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## User & Quotas
|
||||
|
||||
```json
|
||||
GetCurrentUser()
|
||||
GetQuotas() // {projects: {used, limit}, ...}
|
||||
GetSupportedProjectTypes()
|
||||
```
|
||||
@@ -128,7 +128,7 @@ app.get("/mcp", async (req, res) => {
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
**MCP Endpoint:** Fetch project URL from `GetProject(project_id)` response, then append `/mcp` (e.g., if `url` is `https://mcp-server.nodeops.app`, MCP endpoint is `https://mcp-server.nodeops.app/mcp`)
|
||||
**MCP Endpoint:** `https://{uniqueName}.createos.io/mcp`
|
||||
|
||||
---
|
||||
|
||||
@@ -341,7 +341,7 @@ app.listen(3000);
|
||||
"runCommand": "python bot.py",
|
||||
"runEnvs": {
|
||||
"TELEGRAM_TOKEN": "${TELEGRAM_TOKEN}",
|
||||
"WEBHOOK_URL": "${DEPLOYMENT_URL}/webhook" // Set after deployment - get URL from GetProject response
|
||||
"WEBHOOK_URL": "https://telegram-bot.createos.io/webhook"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
# CreateOS Troubleshooting Guide
|
||||
|
||||
Quick reference for diagnosing and resolving common issues.
|
||||
|
||||
---
|
||||
|
||||
## Common Errors
|
||||
|
||||
| Error | Diagnosis | Solution |
|
||||
|-------|-----------|----------|
|
||||
| Build failed | `GetBuildLogs` | Fix code errors, check dependencies |
|
||||
| Runtime crash | `GetDeploymentLogs` | Check startup errors, missing env vars |
|
||||
| Health check fail | App not responding on port | Verify `port` setting matches app |
|
||||
| 502 Bad Gateway | App crashed after deploy | Check logs, increase memory if OOM |
|
||||
| Domain pending | DNS not propagated | Wait 24-48h, verify CNAME record |
|
||||
| Quota exceeded | `GetQuotas` | Upgrade plan or delete unused |
|
||||
| Deployment sleeping | Idle timeout | `WakeupDeployment` or add keep-alive |
|
||||
|
||||
---
|
||||
|
||||
## Debugging Workflow
|
||||
|
||||
1. **Check deployment status**:
|
||||
```json
|
||||
GetDeployment(project_id, deployment_id)
|
||||
```
|
||||
|
||||
2. **If `building` or `failed`** — check build logs:
|
||||
```json
|
||||
GetBuildLogs(project_id, deployment_id)
|
||||
```
|
||||
|
||||
3. **If `deployed` but errors** — check runtime logs:
|
||||
```json
|
||||
GetDeploymentLogs(project_id, deployment_id, 300)
|
||||
```
|
||||
|
||||
4. **If 502/503 errors** — check resources:
|
||||
- Memory too low → OOM kills → increase memory
|
||||
- CPU throttled → slow responses → increase CPU
|
||||
|
||||
---
|
||||
|
||||
## Edge Cases
|
||||
|
||||
### High-Load Scenarios
|
||||
|
||||
- Max 3 replicas per environment
|
||||
- Consider external load balancer for higher scale
|
||||
- Monitor RPM via `GetProjectEnvironmentAnalyticsRPM`
|
||||
|
||||
### Monorepo Projects
|
||||
|
||||
- Set `directoryPath` to subdirectory in settings
|
||||
- Use `GetGithubRepositoryContent` to explore structure
|
||||
|
||||
### Private npm/pip Packages
|
||||
|
||||
- Add auth tokens to `buildVars`:
|
||||
```json
|
||||
"buildVars": {"NPM_TOKEN": "..."}
|
||||
```
|
||||
- Include `.npmrc` or `pip.conf` in repo
|
||||
|
||||
### Long-Running Builds
|
||||
|
||||
- Build timeout: 15 minutes
|
||||
- Use `hasDockerfile: true` for complex builds
|
||||
- Pre-build images and use image project type
|
||||
|
||||
### Sleeping Deployments
|
||||
|
||||
Deployments sleep after idle timeout. Options:
|
||||
1. `WakeupDeployment(project_id, deployment_id)` — manual wake
|
||||
2. Add health check endpoint that gets pinged regularly
|
||||
3. Use external uptime monitor to keep alive
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Security
|
||||
|
||||
1. Never hardcode secrets — use `runEnvs`
|
||||
2. Enable security scanning on projects
|
||||
3. Rotate API keys with reasonable expiry
|
||||
4. Use environment isolation for different secrets
|
||||
|
||||
### Performance
|
||||
|
||||
1. Start small, scale based on metrics
|
||||
2. Use 2+ replicas for production availability
|
||||
3. Monitor analytics for error spikes
|
||||
4. Use `npm ci` over `npm install` for faster builds
|
||||
|
||||
### Reliability
|
||||
|
||||
1. Test auto-promote in staging first
|
||||
2. Keep previous deployments for rollbacks
|
||||
3. Ensure `port` matches app's actual listen port
|
||||
4. Handle sleeping deployments appropriately
|
||||
|
||||
### Organization
|
||||
|
||||
1. Group related projects with Apps
|
||||
2. Use naming convention: `{app}-{service}-{env}`
|
||||
3. Document environments clearly
|
||||
4. Clean up unused projects and deployments
|
||||
@@ -86,12 +86,10 @@ EOF
|
||||
local result
|
||||
result=$(api_check POST "/v1/projects" "$payload")
|
||||
local project_id
|
||||
project_id=$(echo "$result" | jq -r '.data.id // .id // empty')
|
||||
local project_url
|
||||
project_url=$(echo "$result" | jq -r '.data.url // .url // empty')
|
||||
|
||||
project_id=$(echo "$result" | jq -r '.data.id // empty')
|
||||
|
||||
success "Agent deployed! Project ID: $project_id"
|
||||
[ -n "$project_url" ] && echo "URL: $project_url" || echo "URL: Fetch via GetProject($project_id)"
|
||||
echo "URL: https://$name.createos.io"
|
||||
}
|
||||
|
||||
# Deploy MCP Server
|
||||
@@ -137,12 +135,10 @@ EOF
|
||||
local result
|
||||
result=$(api_check POST "/v1/projects" "$payload")
|
||||
local project_id
|
||||
project_id=$(echo "$result" | jq -r '.data.id // .id // empty')
|
||||
local project_url
|
||||
project_url=$(echo "$result" | jq -r '.data.url // .url // empty')
|
||||
|
||||
project_id=$(echo "$result" | jq -r '.data.id // empty')
|
||||
|
||||
success "MCP Server deployed! Project ID: $project_id"
|
||||
[ -n "$project_url" ] && echo "MCP Endpoint: ${project_url}/sse" || echo "MCP Endpoint: Fetch via GetProject($project_id), append /sse"
|
||||
echo "MCP Endpoint: https://$name.createos.io/sse"
|
||||
}
|
||||
|
||||
# Deploy FastAPI Service
|
||||
@@ -183,13 +179,7 @@ EOF
|
||||
|
||||
local result
|
||||
result=$(api_check POST "/v1/projects" "$payload")
|
||||
local project_id
|
||||
project_id=$(echo "$result" | jq -r '.data.id // .id // empty')
|
||||
local project_url
|
||||
project_url=$(echo "$result" | jq -r '.data.url // .url // empty')
|
||||
|
||||
success "API deployed! Project ID: $project_id"
|
||||
[ -n "$project_url" ] && echo "URL: $project_url" || echo "URL: Fetch via GetProject($project_id)"
|
||||
success "API deployed! URL: https://$name.createos.io"
|
||||
}
|
||||
|
||||
# Deploy Bot (Docker image)
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Skill Validation Script for CreateOS
|
||||
|
||||
set -e
|
||||
|
||||
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ERRORS=0
|
||||
|
||||
echo "=== CreateOS Skill Validation ==="
|
||||
echo ""
|
||||
|
||||
# 1. Check SKILL.md exists and has required fields
|
||||
echo "1. Checking SKILL.md structure..."
|
||||
if [ ! -f "$SKILL_DIR/SKILL.md" ]; then
|
||||
echo " ❌ SKILL.md not found"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
# Check frontmatter
|
||||
if ! head -20 "$SKILL_DIR/SKILL.md" | grep -q "^name:"; then
|
||||
echo " ❌ Missing 'name' in frontmatter"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo " ✅ name field present"
|
||||
fi
|
||||
|
||||
if ! head -20 "$SKILL_DIR/SKILL.md" | grep -q "^description:"; then
|
||||
echo " ❌ Missing 'description' in frontmatter"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo " ✅ description field present"
|
||||
fi
|
||||
|
||||
if ! head -20 "$SKILL_DIR/SKILL.md" | grep -q "^allowed-tools:"; then
|
||||
echo " ⚠️ Missing 'allowed-tools' (optional but recommended)"
|
||||
else
|
||||
echo " ✅ allowed-tools field present"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2. Check line count
|
||||
echo ""
|
||||
echo "2. Checking SKILL.md length..."
|
||||
LINES=$(wc -l < "$SKILL_DIR/SKILL.md")
|
||||
if [ "$LINES" -gt 500 ]; then
|
||||
echo " ❌ SKILL.md is $LINES lines (should be <500)"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo " ✅ SKILL.md is $LINES lines (under 500 limit)"
|
||||
fi
|
||||
|
||||
# 3. Check description length
|
||||
echo ""
|
||||
echo "3. Checking description length..."
|
||||
DESC_LEN=$(grep "^description:" "$SKILL_DIR/SKILL.md" | head -1 | wc -c)
|
||||
if [ "$DESC_LEN" -gt 1024 ]; then
|
||||
echo " ❌ Description is $DESC_LEN chars (max 1024)"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo " ✅ Description is $DESC_LEN chars (under 1024 limit)"
|
||||
fi
|
||||
|
||||
# 4. Check for hardcoded URLs
|
||||
echo ""
|
||||
echo "4. Checking for hardcoded createos.io URLs..."
|
||||
HARDCODED=$(grep -r "createos\.io" "$SKILL_DIR" --include="*.md" --include="*.json" --include="*.sh" --include="*.py" 2>/dev/null | grep -v "NEVER construct" | grep -v "like \`https" | grep -v "tests/validate" || true)
|
||||
if [ -n "$HARDCODED" ]; then
|
||||
echo " ❌ Found hardcoded URLs:"
|
||||
echo "$HARDCODED" | head -5
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo " ✅ No hardcoded createos.io URLs found"
|
||||
fi
|
||||
|
||||
# 5. Check references exist
|
||||
echo ""
|
||||
echo "5. Checking reference files..."
|
||||
for ref in "core-skills.md" "deployment-patterns.md" "api-reference.md" "troubleshooting.md"; do
|
||||
if [ -f "$SKILL_DIR/references/$ref" ]; then
|
||||
echo " ✅ references/$ref exists"
|
||||
else
|
||||
echo " ❌ references/$ref missing"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# 6. Check config exists
|
||||
echo ""
|
||||
echo "6. Checking config..."
|
||||
if [ -f "$SKILL_DIR/config/config.json" ]; then
|
||||
if python3 -c "import json; json.load(open('$SKILL_DIR/config/config.json'))" 2>/dev/null; then
|
||||
echo " ✅ config/config.json is valid JSON"
|
||||
else
|
||||
echo " ❌ config/config.json is invalid JSON"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
else
|
||||
echo " ❌ config/config.json missing"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# 7. Check scripts are executable
|
||||
echo ""
|
||||
echo "7. Checking scripts..."
|
||||
for script in "deploy.sh" "quick-deploy.sh"; do
|
||||
if [ -f "$SKILL_DIR/scripts/$script" ]; then
|
||||
if [ -x "$SKILL_DIR/scripts/$script" ] || head -1 "$SKILL_DIR/scripts/$script" | grep -q "^#!/"; then
|
||||
echo " ✅ scripts/$script exists"
|
||||
else
|
||||
echo " ⚠️ scripts/$script missing shebang"
|
||||
fi
|
||||
else
|
||||
echo " ❌ scripts/$script missing"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# 8. Check no duplicate content
|
||||
echo ""
|
||||
echo "8. Checking for README.md (should not exist)..."
|
||||
if [ -f "$SKILL_DIR/README.md" ]; then
|
||||
echo " ⚠️ README.md exists (not needed for skills)"
|
||||
else
|
||||
echo " ✅ No README.md (correct for skills)"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "=== Validation Summary ==="
|
||||
if [ "$ERRORS" -eq 0 ]; then
|
||||
echo "✅ All checks passed! Skill is ready."
|
||||
exit 0
|
||||
else
|
||||
echo "❌ $ERRORS error(s) found. Please fix before using."
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user