-
Notifications
You must be signed in to change notification settings - Fork 858
Spike: Skill-driven aspire init with unified agent skill #15918
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
maddymontaquila
wants to merge
49
commits into
main
Choose a base branch
from
spike/skill-driven-init
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
49 commits
Select commit
Hold shift + click to select a range
cb5b280
Add aspire-init-typescript and aspire-init-csharp skills
maddymontaquila 4fd6956
Register aspire-init skills in CLI infrastructure
maddymontaquila 5094840
Gut InitCommand into skill-driven skeleton launcher
maddymontaquila 670a322
Merge init skills into single unified aspire-init skill
maddymontaquila 0353f29
Enhance init skill: docker-compose parsing, certs as troubleshooting
maddymontaquila 3177984
Add comprehensive AppHost wiring reference to init skill
maddymontaquila 6099136
Add guiding principles: minimize user code changes, surface tradeoffs
maddymontaquila 50680af
Focus init skill on local dev, surface external service dependencies
maddymontaquila 58db00e
Add .env file migration guidance to init skill
maddymontaquila ce6116a
Add aspirify-eval playground apps for init skill benchmarking
maddymontaquila ab440b1
Flesh out eval app READMEs with full before-state docs
maddymontaquila 7d7da27
Move eval rubric out of app dirs so agents can't cheat
maddymontaquila 2cee73d
Update aspire-init skill based on eval findings
maddymontaquila 64eff44
Add tiered integration preference to aspire-init skill
maddymontaquila 5ef224a
Replace AddProject with AddCsharpApp, preserve .env files, strengthen…
maddymontaquila 9c71ba6
Use aspire run for TS scripts, be package-manager agnostic
maddymontaquila efcedf6
Fix dev.localhost value prop, replace AddNpmApp, add CLI validation, …
maddymontaquila 6e8b0e2
Prefer HTTPS over HTTP, add WithHttpsDeveloperCertificate guidance
maddymontaquila 1462ebb
Update src/Aspire.Cli/Agents/SkillDefinition.cs
maddymontaquila c584bb1
Fix AddCSharpApp casing (capital S, capital H)
maddymontaquila 93cecec
Note aspire list integrations requires MCP server
maddymontaquila 0d6331d
Emphasize iterate until aspire start works without errors
maddymontaquila 3928b19
Fix aspire init writing 'already exists' on fresh repos
maddymontaquila 2b622f5
Improve skill based on excalidraw real-world test
maddymontaquila b5d173b
Fix init flow, profiles, ASPNETCORE_URLS, and CI issues
maddymontaquila 89fb461
Add Step 2 smoke test: verify skeleton boots before wiring
maddymontaquila 3165e26
Fix dev.localhost guidance: use aspire.config.json profiles, not with…
maddymontaquila 9ebfdef
Fix aspire.config.json message: always say 'Created' during init
maddymontaquila cf4a676
Improve init closing message with one-shot agent commands
maddymontaquila 003af46
Add summary output to skill validation step
maddymontaquila a10cced
Leave AppHost running after init completes
maddymontaquila fcb5e09
Strengthen withUrlForEndpoint and hardcoded URL prohibitions in skill
maddymontaquila b313c8d
Fix init skill follow-up commands
maddymontaquila 7d63fe0
Strengthen init skill port guidance
maddymontaquila c693ab7
Clarify mixed SDK AppHost guidance
maddymontaquila e332ba9
Fix solution-aware init scaffold
maddymontaquila d1d239c
Modularize init solution guidance
maddymontaquila af2381f
Clarify init validation states
maddymontaquila 3749481
Improve aspire-init skill for large/complex repos
maddymontaquila bab9105
Add critical rules: no workloads, no SDK changes, no TFM changes
maddymontaquila de34384
Clarify config files per AppHost mode, fix ServiceDefaults placement
maddymontaquila 06a0c2c
Fix issues from bitwarden eval run
maddymontaquila 966377b
Use aspire-apphost template for full-solution init
maddymontaquila 7c58b65
Make core loop expansion explicit — don't stop early
maddymontaquila 4dbde16
Add Redis auto-TLS, stable SDK, and ServiceDefaults dedup guidance
maddymontaquila e98cb15
Optimize aspire-init skill: deduplicate, split references, trim
maddymontaquila b0c9c24
Merge branch 'main' into spike/skill-driven-init
maddymontaquila 1e04e6e
Fix bad merge: remove stale code and restore fallback defaults
maddymontaquila 31c864e
Warn about stale persistent volumes causing auth failures
maddymontaquila File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
369 changes: 369 additions & 0 deletions
369
.agents/skills/aspire-init/references/apphost-wiring.md
Large diffs are not rendered by default.
Oops, something went wrong.
214 changes: 214 additions & 0 deletions
214
.agents/skills/aspire-init/references/docker-compose.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| # Docker Compose migration | ||
|
|
||
| Use this reference when the repo has a `docker-compose.yml` or `compose.yml` file. Docker Compose files are one of the most valuable discovery sources — they document the infrastructure the app actually needs to run locally. | ||
|
|
||
| ## When to load this reference | ||
|
|
||
| - A `docker-compose.yml`, `compose.yml`, or `docker-compose.override.yml` exists anywhere in the repo | ||
| - The repo has setup scripts that call `docker compose up` as part of the dev workflow | ||
|
|
||
| ## Profiles | ||
|
|
||
| Docker Compose files can use `profiles:` to organize services into named groups. Not all services run at once — the developer chooses which profiles to activate. | ||
|
|
||
| Example from a real repo: | ||
|
|
||
| ```yaml | ||
| services: | ||
| mssql: | ||
| image: mcr.microsoft.com/mssql/server:2022-latest | ||
| profiles: [cloud, mssql] | ||
| postgres: | ||
| image: postgres:14 | ||
| profiles: [postgres, ef] | ||
| redis: | ||
| image: redis:alpine | ||
| profiles: [redis, cloud] | ||
| storage: | ||
| image: mcr.microsoft.com/azure-storage/azurite:latest | ||
| profiles: [storage, cloud] | ||
| mail: | ||
| image: sj26/mailcatcher:latest | ||
| profiles: [mail] | ||
| ``` | ||
|
|
||
| When profiles are present: | ||
|
|
||
| 1. **List them clearly** — show the user which profiles exist and what services each activates | ||
| 2. **Ask which to target** — *"Your docker-compose has profiles: cloud, mssql, postgres, storage, redis, mail. Which ones represent your typical local dev stack?"* | ||
| 3. **Model only selected profiles** — services in unselected profiles are skipped entirely | ||
| 4. **Services without profiles always run** — if a service has no `profiles:` key, include it regardless of profile selection | ||
| 5. **Profile-specific infrastructure implies choices** — `mssql` vs `postgres` profiles often mean the repo supports multiple database backends. Ask which one to model in the AppHost. | ||
|
|
||
| ## Image-to-integration mapping | ||
|
|
||
| Prefer typed Aspire integrations over raw `AddContainer()`. Use `aspire docs search <technology>` to check for available integrations. | ||
|
|
||
| Common mappings: | ||
|
|
||
| | Compose image | Aspire integration | Method | | ||
| |---------------|-------------------|--------| | ||
| | `postgres:*` | `Aspire.Hosting.PostgreSQL` | `AddPostgres()` | | ||
| | `mcr.microsoft.com/mssql/server:*` | `Aspire.Hosting.SqlServer` | `AddSqlServer()` | | ||
| | `mysql:*` / `mariadb:*` | `Aspire.Hosting.MySql` | `AddMySql()` | | ||
| | `redis:*` | `Aspire.Hosting.Redis` | `AddRedis()` | | ||
| | `rabbitmq:*` | `Aspire.Hosting.RabbitMQ` | `AddRabbitMQ()` | | ||
| | `mongo:*` | `Aspire.Hosting.MongoDB` | `AddMongoDB()` | | ||
| | `mcr.microsoft.com/azure-storage/azurite:*` | `Aspire.Hosting.Azure.Storage` | `AddAzureStorage().RunAsEmulator()` | | ||
| | `kafka`, `confluentinc/cp-kafka:*` | `Aspire.Hosting.Kafka` | `AddKafka()` | | ||
| | `nats:*` | `Aspire.Hosting.Nats` | `AddNats()` | | ||
| | `mcr.microsoft.com/azure-messaging/servicebus-emulator:*` | `Aspire.Hosting.Azure.ServiceBus` | `AddAzureServiceBus().RunAsEmulator()` | | ||
|
|
||
| For images not in this list, use `aspire docs search` to check, then fall back to `AddContainer()`. | ||
|
|
||
| ## Environment variable interpolation | ||
|
|
||
| Compose files use `${VAR}` syntax to reference variables from `.env` files: | ||
|
|
||
| ```yaml | ||
| environment: | ||
| MSSQL_SA_PASSWORD: ${MSSQL_PASSWORD} | ||
| POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} | ||
| ``` | ||
|
|
||
| **⚠️ CRITICAL: Do not model passwords for typed Aspire integrations.** | ||
|
|
||
| `AddPostgres()`, `AddSqlServer()`, `AddRedis()`, `AddMySql()`, `AddRabbitMQ()`, and other typed integrations **auto-generate secure passwords**. The compose file needed `POSTGRES_PASSWORD` because Compose doesn't manage credentials — Aspire does. If you see a compose password variable that maps to a typed integration, **skip it entirely**. Do not create an `AddParameter` for it. | ||
|
|
||
| ```csharp | ||
| // ❌ WRONG — don't model passwords that Aspire auto-generates | ||
| var pgPassword = builder.AddParameter("postgres-password", secret: true); | ||
| var postgres = builder.AddPostgres("postgres", password: pgPassword); | ||
|
|
||
| // ✅ RIGHT — let Aspire handle the password | ||
| var postgres = builder.AddPostgres("postgres"); | ||
| ``` | ||
|
|
||
| Use `aspire docs get <integration-slug>` to check what each typed integration manages automatically. Look for the "Connection properties" section — if it lists `Password`, the integration handles it. | ||
|
|
||
| When you see a `${VAR}` pattern in compose: | ||
|
|
||
| 1. **Check if it maps to a typed integration** — if `POSTGRES_PASSWORD`, `MSSQL_SA_PASSWORD`, `MYSQL_ROOT_PASSWORD`, `RABBITMQ_DEFAULT_PASS`, etc. are used by a typed Aspire integration, **skip them** — Aspire manages these | ||
| 2. **Trace non-integration variables** — find them in the `.env` or `.env.example` file | ||
| 3. **Classify** — is it a secret (API key, token) or plain config? | ||
| 4. **Model it** — secrets become `AddParameter(name, secret: true)`, plain config becomes `AddParameter(name)` with a default or `WithEnvironment()` directly | ||
|
|
||
| ## Volume mapping | ||
|
|
||
| | Compose volume type | Aspire equivalent | Notes | | ||
| |--------------------|--------------------|-------| | ||
| | Named volume (`mssql_data:/var/opt/mssql`) | `WithDataVolume()` | Preferred — Aspire manages lifecycle | | ||
| | Named volume (custom name) | `WithDataVolume(name: "custom")` | Preserves the name for familiarity | | ||
| | Bind mount (`./data:/app/data`) | `WithBindMount("./data", "/app/data")` | Use for config files, scripts, or shared data | | ||
| | Bind mount for config (`./config.json:/etc/config.json`) | `WithBindMount(...)` | Preserve for config injection | | ||
|
|
||
| **Tip:** If the compose file mounts migration scripts or SQL files into a database container, those are likely init scripts. See "Init and setup scripts" below. | ||
|
|
||
| ## Dependency ordering | ||
|
|
||
| Compose `depends_on` maps to Aspire's `WaitFor()`: | ||
|
|
||
| ```yaml | ||
| # Compose | ||
| services: | ||
| api: | ||
| depends_on: | ||
| - mssql | ||
| - redis | ||
| ``` | ||
|
|
||
| ```csharp | ||
| // Aspire | ||
| var api = builder.AddProject<Projects.Api>("api") | ||
| .WithReference(mssql) | ||
| .WithReference(redis) | ||
| .WaitFor(mssql) | ||
| .WaitFor(redis); | ||
| ``` | ||
|
|
||
| `WithReference()` establishes the connection. `WaitFor()` ensures the dependency is healthy before the consuming service starts. Use both together. | ||
|
|
||
| For `depends_on` with `condition: service_healthy`, the `WaitFor()` mapping is especially important — it replicates the same behavior. | ||
|
|
||
| ## Build contexts | ||
|
|
||
| Compose services with `build:` are built from source, not pulled as images: | ||
|
|
||
| ```yaml | ||
| services: | ||
| worker: | ||
| build: | ||
| context: ./worker | ||
| dockerfile: Dockerfile | ||
| ``` | ||
|
|
||
| Map these to `AddDockerfile()`: | ||
|
|
||
| ```csharp | ||
| var worker = builder.AddDockerfile("worker", "../worker") | ||
| .WithHttpEndpoint(targetPort: 8080); | ||
| ``` | ||
|
|
||
| However, **prefer native Aspire project hosting over Dockerfiles when possible**. If the build context contains a `.csproj`, use `AddProject<T>()`. If it's a Node.js app, use `AddNodeApp()` or `AddViteApp()`. Dockerfiles are a last resort for Aspire modeling — they lose service discovery, health checks, and hot reload. | ||
|
|
||
| ## Init and setup scripts | ||
|
|
||
| Repos often have setup scripts alongside their compose files: | ||
|
|
||
| - `setup_azurite.ps1` — initializes storage emulator containers and queues | ||
| - `migrate.ps1` / `ef_migrate.ps1` — runs database migrations | ||
| - `setup_secrets.ps1` — configures .NET user secrets | ||
| - `create_certificates_*.sh` — generates dev certificates | ||
|
|
||
| **Present the user with options for how to handle these:** | ||
|
|
||
| 1. **Model as a lifecycle command on the relevant resource** — for example, a database migration script can be a startup command on the database resource. This runs automatically when the resource starts. | ||
| → *"Your repo has a migrate.ps1 that runs SQL migrations against the database. I can model this as a startup lifecycle hook on the database resource so migrations run automatically when you `aspire start`. Want that?"* | ||
|
|
||
| 2. **Model as a standalone executable resource** — for scripts that don't map cleanly to a single resource, use `AddExecutable()` with `WaitForCompletion()` so dependent services wait for the script to finish. | ||
|
|
||
| 3. **Leave as manual** — some setup scripts are one-time-only (like certificate generation) and don't need to run every time. Note them in the AppHost as a comment and move on. | ||
|
|
||
| The right choice depends on the script. Present the tradeoff and let the user decide. | ||
|
|
||
| ## Putting it together | ||
|
|
||
| A compose file like: | ||
|
|
||
| ```yaml | ||
| services: | ||
| mssql: | ||
| image: mcr.microsoft.com/mssql/server:2022-latest | ||
| environment: | ||
| MSSQL_SA_PASSWORD: ${MSSQL_PASSWORD} | ||
| volumes: | ||
| - mssql_data:/var/opt/mssql | ||
| ports: | ||
| - "1433:1433" | ||
| redis: | ||
| image: redis:alpine | ||
| volumes: | ||
| - redis_data:/data | ||
| ports: | ||
| - "6379:6379" | ||
| ``` | ||
|
|
||
| Becomes: | ||
|
|
||
| ```csharp | ||
| // ✅ Let Aspire auto-generate the SA password — don't model MSSQL_PASSWORD | ||
| var mssql = builder.AddSqlServer("mssql") | ||
| .WithDataVolume(); | ||
|
|
||
| var redis = builder.AddRedis("redis") | ||
| .WithDataVolume(); | ||
| ``` | ||
|
|
||
| Note: for typed integrations like `AddSqlServer()` and `AddRedis()`, you don't need to map ports or passwords — Aspire handles both. You also don't need to model `redis_data` as a named volume — `WithDataVolume()` handles persistence. The `${MSSQL_PASSWORD}` from the compose file is skipped entirely because `AddSqlServer()` auto-generates a secure SA password. | ||
|
|
||
| ## Common pitfalls | ||
|
|
||
| - **Don't model every compose service** — some are dev-only tools (mailcatcher, reverse proxies, SAML IdPs for testing). Ask the user which are essential vs nice-to-have. | ||
| - **Don't preserve hardcoded ports from compose** — Aspire manages ports dynamically. Only preserve a port if the user confirms it's required for external reasons (OAuth callbacks, etc.). | ||
| - **Don't duplicate compose's `.env` interpolation** — Aspire parameters replace this pattern. Trace each `${VAR}` to its source and model it properly. | ||
| - **Watch for services that conflict on the same port** — compose profiles often have services sharing ports (e.g., `mssql` and `postgres` both on different profiles). If the user selects conflicting profiles, surface the conflict. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once #15814 merges this would need to be updated to correctly resolve API docs.