Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,27 @@

## ⚡ Quick Start (Community Edition)

Get PROMPTC running in your local environment in under 60 seconds. Our *Plug & Play* installer automatically configures the engine and connects it with **Claude Desktop**.
Get PROMPTC running in your local environment in under 60 seconds. Our *Plug & Play* installer compiles the engine and registers it as a local MCP server for **Codex**.

**Prerequisites:**
* macOS (M1/M2/M3) or Linux.
* Claude Desktop installed.
* A [Google AI Studio API Key](https://aistudio.google.com/app/apikey).
* Codex CLI or Codex app installed.
* An [OpenAI API key](https://platform.openai.com/api-keys) for cloud fallback.
* Optional: a [Google AI Studio API Key](https://aistudio.google.com/app/apikey) as secondary fallback.

Run in your terminal:

```bash
curl -sSL https://raw.githubusercontent.com/andesdevroot/promptc/master/install.sh | bash
```

The installer now:
* compiles `promptc` into `~/.promptc/promptc`
* registers `PROMPTC` in Codex through `codex mcp add`
* injects `PROMPTC_MACMINI_IP`, `PROMPTC_MCP_CLIENT=codex-desktop`, `OPENAI_API_KEY`, optional `OPENAI_MODEL`, and optional `GEMINI_API_KEY`

If Codex is not available yet, the installer leaves a ready-to-run helper script at `~/.promptc/codex-mcp-setup.sh`.

---

## 🏗️ Architecture: Private-First AI (Dual-Tier)
Expand All @@ -50,7 +58,8 @@ In regulated sectors like **Mining, Banking, and Legal**, business logic is a cr

### 1. Community Mode (Agile Development)
* **Orchestration:** Ultra-lightweight local binary execution.
* **Inference:** Routes optimization to **Gemini 1.5 Pro** transparently.
* **Client:** Designed for **Codex** as the MCP-native operator surface.
* **Inference:** Routes optimization to **OpenAI** transparently when local sovereignty is unavailable, with optional **Gemini** secondary fallback.

### 2. Enterprise Mode (Air-Gapped / Full Sovereignty)
* **Orchestration:** Local interceptor for all outgoing prompts.
Expand All @@ -73,6 +82,8 @@ In regulated sectors like **Mining, Banking, and Legal**, business logic is a cr
* **Static Go Binary**: Zero dependencies, runtime-free, and high performance (RAM < 15MB).
* **Prompt-as-Code (PaC)**: Manage templates through versioned, pre-certified components.
* **Deterministic Compilation**: Transforms ambiguous language into structured Markdown (Role, Context, Task, Constraints).
* **Codex-Ready Local Ops**: Works as a stdio MCP server that Codex can register and call locally.
* **OpenAI Responses API Fallback**: Uses the official `/v1/responses` API for cloud optimization when the local node is unavailable.

---

Expand Down
25 changes: 21 additions & 4 deletions cmd/promptc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ var configCmd = &cobra.Command{
reader := bufio.NewReader(os.Stdin)

fmt.Println(cli.ColorCyan + "¿Qué proveedor de IA deseas usar?" + cli.ColorReset)
fmt.Println("1) Google Gemini")
fmt.Println("1) OpenAI")
fmt.Println("2) Google Gemini")
fmt.Print(cli.ColorYellow + "> " + cli.ColorReset)

providerOption, _ := reader.ReadString('\n')
Expand All @@ -30,9 +31,11 @@ var configCmd = &cobra.Command{
var provider string
switch providerOption {
case "1":
provider = "openai"
case "2":
provider = "gemini"
default:
provider = "gemini"
provider = "openai"
}

fmt.Printf("\n🔑 Ingresa tu API Key:\n")
Expand All @@ -41,9 +44,23 @@ var configCmd = &cobra.Command{
apiKey, _ := reader.ReadString('\n')
apiKey = strings.TrimSpace(apiKey)

openAIModel := ""
if provider == "openai" {
fmt.Printf("\n🧠 Modelo OpenAI (Enter para usar gpt-5.4-mini):\n")
fmt.Print(cli.ColorYellow + "> " + cli.ColorReset)
openAIModel, _ = reader.ReadString('\n')
openAIModel = strings.TrimSpace(openAIModel)
}

cfg := config.AppConfig{
Provider: provider,
APIKey: apiKey,
Provider: provider,
APIKey: apiKey,
OpenAIModel: openAIModel,
}
if provider == "openai" {
cfg.OpenAIAPIKey = apiKey
} else {
cfg.GeminiAPIKey = apiKey
}

config.Save(cfg)
Expand Down
56 changes: 34 additions & 22 deletions cmd/promptc/fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"os"
"strings"

"github.com/andesdevroot/promptc/internal/cli"
"github.com/andesdevroot/promptc/internal/config"
Expand All @@ -18,51 +19,62 @@ var fixCmd = &cobra.Command{
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
cli.PrintBanner()
cfg, _ := config.Load()
cfg, err := config.Load()
if err != nil {
fmt.Println("Error cargando configuración:", err)
os.Exit(1)
}

p, err := parser.ParseFile(args[0])
if err != nil {
fmt.Println("Error:", err)
os.Exit(1)
}

ctx := context.Background()
promptcSDK, _ := sdk.NewSDK(ctx, cfg.APIKey, "")
openAIKey := strings.TrimSpace(cfg.OpenAIAPIKey)
geminiKey := strings.TrimSpace(cfg.GeminiAPIKey)

analysis := promptcSDK.Analyze(p)

analysisResult, ok := analysis.(map[string]interface{})
if !ok {
fmt.Println("Error: invalid analysis result type")
os.Exit(1)
switch cfg.Provider {
case "openai":
if openAIKey == "" {
openAIKey = strings.TrimSpace(cfg.APIKey)
}
case "gemini":
if geminiKey == "" {
geminiKey = strings.TrimSpace(cfg.APIKey)
}
}

score, ok := analysisResult["Score"].(float64)
if !ok {
fmt.Println("Error: Score field not found or invalid type")
os.Exit(1)
if envKey := strings.TrimSpace(os.Getenv("OPENAI_API_KEY")); envKey != "" {
openAIKey = envKey
}
if envKey := strings.TrimSpace(os.Getenv("GEMINI_API_KEY")); envKey != "" {
geminiKey = envKey
}
fmt.Printf("Score: %d/100\n", int(score))

analysisResult, ok = analysis.(map[string]interface{})
if !ok {
fmt.Println("Error: invalid analysis result type")
os.Exit(1)
openAIModel := strings.TrimSpace(cfg.OpenAIModel)
if envModel := strings.TrimSpace(os.Getenv("OPENAI_MODEL")); envModel != "" {
openAIModel = envModel
}

isReliable, ok := analysisResult["IsReliable"].(bool)
if !ok {
fmt.Println("Error: IsReliable field not found or invalid type")
promptcSDK, err := sdk.NewSDK(ctx, openAIKey, openAIModel, geminiKey, os.Getenv("PROMPTC_MACMINI_IP"))
if err != nil {
fmt.Println("Error inicializando SDK:", err)
os.Exit(1)
}

if !isReliable {
analysis := promptcSDK.Analyze(p)
fmt.Printf("Score: %d/100\n", analysis.Score)

if !analysis.IsReliable {
optimized, err := promptcSDK.Optimize(ctx, p)
if err != nil {
fmt.Printf("\n❌ Error Crítico: %v\n", err)
os.Exit(1)
}
cli.PrintSuccess("\n✨ Prompt Optimizado:")
fmt.Println("\n" + fmt.Sprint(optimized))
fmt.Println("\n" + optimized)
} else {
output, _ := promptcSDK.Engine.Compile(p)
fmt.Println("\n" + output)
Expand Down
Loading