diff --git a/README.md b/README.md index 1f6f407..0ab2216 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ agora init my-nextjs-demo --template nextjs | Next.js video app | `agora init my-nextjs-demo --template nextjs` | A cloned Next.js quickstart, project binding, and `.env.local` | | Python voice agent | `agora init my-python-demo --template python` | A Python quickstart with Agora credentials written for the backend | | Go token service | `agora init my-go-demo --template go` | A Go server quickstart with project metadata and env wiring | +| Android conversational AI app | `agora quickstart create my-android-demo --template android` | A cloned Android quickstart on the `rest-api` branch | ## Install diff --git a/docs/llms.txt b/docs/llms.txt index d875887..27441b1 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -39,7 +39,7 @@ Check project health: agora project doctor --json - **Project Management**: Initialize, configure, and validate Agora projects - **JSON Output**: All commands support --json for automation and scripting (see Automation Notes below for one documented exception) - **Stable Exit Codes**: Consistent error codes for CI/CD integration -- **Template System**: Quick-start templates for Next.js, Python, and Go (see `agora init --help` for the current catalog) +- **Template System**: Quick-start templates for Next.js, Python, and Go (see `agora init --help` for the init-capable catalog); Android is clone-only for now and appears in `agora quickstart list` - **Cross-Platform**: macOS, Linux, Windows support - **Agentic discovery**: `agora introspect --json` and `agora --help --all --json` emit the same machine-readable command tree - **MCP server**: `agora mcp serve` exposes the CLI as Model Context Protocol tools for agents diff --git a/internal/cli/integration_quickstart_test.go b/internal/cli/integration_quickstart_test.go index 96dc52b..79f828f 100644 --- a/internal/cli/integration_quickstart_test.go +++ b/internal/cli/integration_quickstart_test.go @@ -53,11 +53,14 @@ func TestCLIQuickstartListAndCreate(t *testing.T) { if list.exitCode != 0 || !strings.Contains(list.stdout, `"id":"nextjs"`) || !strings.Contains(list.stdout, `"id":"python"`) || !strings.Contains(list.stdout, `"id":"go"`) { t.Fatalf("unexpected quickstart list result: %+v", list) } + if !strings.Contains(list.stdout, `"id":"android"`) { + t.Fatalf("expected android quickstart in list result: %+v", list) + } listAll := runCLI(t, []string{"quickstart", "list", "--show-all", "--json"}, cliRunOptions{env: map[string]string{ "XDG_CONFIG_HOME": configHome, "AGORA_LOG_LEVEL": "error", }}) - if listAll.exitCode != 0 || !strings.Contains(listAll.stdout, `"id":"go"`) { + if listAll.exitCode != 0 || !strings.Contains(listAll.stdout, `"id":"go"`) || !strings.Contains(listAll.stdout, `"id":"android"`) { t.Fatalf("unexpected quickstart list --show-all result: %+v", listAll) } diff --git a/internal/cli/quickstart.go b/internal/cli/quickstart.go index 1becb5e..bba6340 100644 --- a/internal/cli/quickstart.go +++ b/internal/cli/quickstart.go @@ -18,6 +18,7 @@ type quickstartTemplate struct { Description string Runtime string RepoURL string + Ref string DocsURL string DetectPaths []string EnvExamplePath string @@ -79,6 +80,21 @@ func quickstartTemplates() []quickstartTemplate { SupportsInit: true, Available: true, }, + { + ID: "android", + Title: "Conversational AI Android Quickstart", + Description: "Clone the official Android conversational AI quickstart.", + Runtime: "android", + RepoURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android", + Ref: "rest-api", + DocsURL: "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android/tree/rest-api", + DetectPaths: []string{"settings.gradle", "gradlew", "app/src/main/AndroidManifest.xml"}, + InstallCommand: "Open in Android Studio", + RunCommand: "Run from Android Studio or Gradle", + EnvDocsSummary: "Android quickstart template. Clone-only for now; env seeding is not yet wired into the CLI.", + SupportsInit: false, + Available: true, + }, } } @@ -292,11 +308,15 @@ func (a *App) quickstartCreate(template quickstartTemplate, targetDir, explicitP if err != nil { return nil, err } + effectiveRef := strings.TrimSpace(ref) + if effectiveRef == "" { + effectiveRef = strings.TrimSpace(template.Ref) + } if overrideKey != "" { progress.emit("clone:override", fmt.Sprintf("Using repo override from %s", overrideKey), map[string]any{"repoUrl": repoURL, "envVar": overrideKey}) } - progress.emit("clone:start", "Cloning quickstart repository", map[string]any{"repoUrl": repoURL, "targetPath": absTarget, "ref": ref}) - if err := cloneQuickstartRepo(repoURL, absTarget, ref); err != nil { + progress.emit("clone:start", "Cloning quickstart repository", map[string]any{"repoUrl": repoURL, "targetPath": absTarget, "ref": effectiveRef}) + if err := cloneQuickstartRepo(repoURL, absTarget, effectiveRef); err != nil { return nil, err } progress.emit("clone:complete", "Quickstart repository cloned", map[string]any{"targetPath": absTarget}) @@ -346,7 +366,7 @@ func (a *App) quickstartCreate(template quickstartTemplate, targetDir, explicitP "title": template.Title, "written": written, "nextSteps": initNextSteps(template, absTarget), - "ref": ref, + "ref": effectiveRef, } if boundProject != nil { result["projectId"] = boundProject.project.ProjectID diff --git a/internal/cli/quickstart_test.go b/internal/cli/quickstart_test.go index a4a82fa..53636d4 100644 --- a/internal/cli/quickstart_test.go +++ b/internal/cli/quickstart_test.go @@ -153,3 +153,27 @@ func TestQuickstartRepoURLOverride(t *testing.T) { } } } + +func TestQuickstartTemplatesIncludeAndroid(t *testing.T) { + var android quickstartTemplate + found := false + for _, tmpl := range quickstartTemplates() { + if tmpl.ID == "android" { + android = tmpl + found = true + break + } + } + if !found { + t.Fatal("expected android quickstart template to exist") + } + if android.RepoURL != "https://github.com/AgoraIO-Conversational-AI/agent-quickstart-android" { + t.Fatalf("unexpected android repo url: %q", android.RepoURL) + } + if android.Ref != "rest-api" { + t.Fatalf("unexpected android default ref: %q", android.Ref) + } + if !android.Available || android.SupportsInit { + t.Fatalf("unexpected android flags: available=%v supportsInit=%v", android.Available, android.SupportsInit) + } +}