Auto-detect project tech stack and generate stack-specific references for domain skills
Scanned 9/19/2026
npx -y skills add first-fluke/fullstack-starter --skill stack-set --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Stack Set?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/first-fluke-stack-set-191a651d)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: stack-set
description: Auto-detect project tech stack and generate stack-specific references for domain skills
disable-model-invocation: true
---
- **Response language follows `language` setting in `.agents/oma-config.yaml` if configured.**
- Follow `.agents/skills/_shared/core/execution-policy.md` for authorization, clarification, verification, and completion. Execute required steps on the selected path in dependency order; apply documented branch and skip conditions.
- **This workflow is slash-invoked only** (`/stack-set`). It is NOT triggered by keyword detection.
- **Read manifests BEFORE generating.** Never fabricate stack values that were not detected.
---
# /stack-set: Stack Configuration Workflow
## Goal
Analyze project files to detect the tech stack, resolve the target domain skill, then generate language-specific references in that skill's `stack/` directory.
> **Vendor note:** This workflow executes inline (no subagent spawning). All vendors use their native file reading tools for manifest detection and file writing tools for stack generation.
---
## Step 1: Detect
Scan the project root for package manifests. Evaluate **all three** tables independently — do not stop at the first match.
### Backend manifests → domain `backend`
| File | Detection |
|:---|:---|
| `pyproject.toml`, `requirements.txt`, `Pipfile` | Python |
| `package.json`, `tsconfig.json` | Node.js/TypeScript |
| `Cargo.toml` | Rust |
| `pom.xml`, `build.gradle`, `build.gradle.kts` | Java/Kotlin |
| `go.mod` | Go |
| `mix.exs` | Elixir |
| `Gemfile` | Ruby |
| `*.csproj`, `*.sln` | C#/.NET |
Read manifest contents to detect framework:
- Python: FastAPI? Django? Flask?
- Node.js: NestJS? Express? Hono?
- Rust: Axum? Actix-web? Rocket?
- Java: Spring Boot? Quarkus?
**Frontend exclusion (amendment F):** `package.json` + `tsconfig.json` count as a backend (Node.js/TypeScript) signal only when no frontend signal from the table below claims them. In a frontend-only repo (Angular, Next.js, React, Vue, Svelte, …), `package.json` and `tsconfig.json` do NOT create a backend domain. A backend domain still counts when a non-Node backend manifest exists (e.g. `pyproject.toml`) or a separate package carries backend framework dependencies (NestJS, Express, Hono, …) in a monorepo. (Next.js API routes / server actions belong to the frontend domain, not a separate backend domain, unless a dedicated backend package exists.)
### Frontend manifests → domain `frontend`
| File | Detection |
|:---|:---|
| `angular.json` | Angular |
| `package.json` with `@angular/core`, `@angular/cli`, or `@angular/build` | Angular |
| `next.config.{js,mjs,ts}` or `package.json` with `next` | Next.js (React) |
| `package.json` with `react` + `react-dom` (no `next`, no `react-native`) | React SPA (Vite / CRA / Remix — infer from deps) |
| `nuxt.config.*` or `package.json` with `vue` / `nuxt` | Vue / Nuxt |
| `svelte.config.*` or `package.json` with `svelte` | Svelte / SvelteKit |
| `tsconfig.app.json`, `tsconfig.spec.json` | Angular (supporting signal only — never sufficient alone) |
Any row above counts as a frontend signal for the amendment-F backend exclusion — a Next.js or React repo must NOT fall through to the backend (Node.js) domain just because it has `package.json` + `tsconfig.json`.
For Angular, read manifest contents to detect the rest of the stack:
- Angular version from `@angular/core`
- Package manager from the lockfile (`bun.lock` → bun, `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm)
- UI library: `@spartan-ng/*` (Spartan UI)? `@angular/material`? PrimeNG? none
- Styling: Tailwind CSS? SCSS? (`components.json` is a Spartan/shadcn-style marker)
- Test runner: Vitest? Jest? Jasmine/Karma?
- RxJS: is `rxjs` used beyond Angular internals (streams in services/components)?
### Mobile manifests → domain `mobile`
| File | Detection |
|:---|:---|
| `Package.swift`, `*.xcodeproj`, `*.xcworkspace`, `Podfile` | Swift / iOS native |
| `pubspec.yaml` | Flutter |
| `package.json` with a `react-native` dependency | React Native |
For Swift / iOS native, additionally inspect the project to detect the UI framework:
- Look for `import SwiftUI` in source files or a `SwiftUI` framework dependency in `Package.swift`.
- Default to **SwiftUI** when ambiguous.
### Resolve `target_skill`
After scanning all tables, record every domain that has at least one detected manifest file:
| Detected domain | `target_skill` |
|:---|:---|
| backend only | `oma-backend` |
| frontend only (Angular) | `angular-developer` if `.agents/skills/angular-developer/` is installed, otherwise `oma-frontend` |
| frontend only (React / Next.js / Vue / Svelte) | `oma-frontend` |
| mobile only | `oma-mobile` |
| two or more (monorepo) | carry **all** into Step 2 — do NOT first-match |
**Multi-domain rule (amendment B):** If manifests from two or more tables are found, collect all detected domains as the detected set and proceed to Step 2. Do not silently discard any.
---
## Step 2: Resolve Target & Present Detection
Reuse domains and stack choices already specified by the user. Present detected values for review; ask only about a material ambiguity, an unresolved target domain, or an overwrite outside the requested scope. The prompts below are for those unresolved cases.
### Single-domain: backend
Present detection results; ask for correction only if a material value remains unresolved:
```
Detected backend stack:
Language: {language}
Framework: {framework}
ORM: {orm}
Validation: {validation}
Migration: {migration}
Test: {test framework}
Correct? (Y/n) or modify:
```
### Single-domain: frontend (Angular)
Present detection results; ask for correction only if a material value remains unresolved:
```
Detected frontend stack:
Framework: Angular {version}
Package manager: {pm}
UI: {ui_library} (e.g. Spartan UI, Angular Material, none)
Styling: {styling} (e.g. Tailwind CSS v4, SCSS)
Async/State: {async} (signals-only, or signals + rxjs)
Test: {test_runner} (e.g. Vitest, Jest, Jasmine/Karma)
Correct? (Y/n) or modify:
```
For React / Next.js / Vue / Svelte detection, present an equivalent confirmation block using the relevant fields (framework + version, package manager, UI library, styling, state management, test runner).
### Single-domain: mobile (Swift)
Present detection results; ask for correction only if a material value remains unresolved:
```
Detected mobile stack:
Language: {language} (e.g. Swift)
UI: {ui} (e.g. SwiftUI)
API generator: {api_generator} (e.g. swift-openapi-generator)
API spec: {api_spec} (e.g. Core/Networking/openapi.yaml)
Structure: {structure} (e.g. App/Core/Features/Shared)
Test: {test} (e.g. XCTest)
Correct? (Y/n) or modify:
```
For Flutter or React Native mobile detection, present an equivalent confirmation block using the relevant fields (framework, sdk version, test framework, etc.).
### Multi-domain: present choice first
When more than one domain was detected in Step 1 and the request does not identify which domains to configure, ask:
```
Multiple domains detected in this repo:
[backend] {backend_language} / {backend_framework}
[frontend] Angular {angular_version} / {ui_library}
[mobile] {mobile_language} / {mobile_ui}
Generate stack references for: [all / backend / frontend / mobile]
```
(List only the domains actually detected.)
Once the target domains are resolved, show their detected values together and generate within the authorized scope. Do not request a separate approval for each domain.
---
## Step 3: Generate
Write generated files into `.agents/skills/{target_skill}/stack/`.
- Backend → `.agents/skills/oma-backend/stack/`
- Frontend → `.agents/skills/{frontend_target_skill}/stack/` (`angular-developer` or `oma-frontend`, per Step 1)
- Mobile → `.agents/skills/oma-mobile/stack/`
- Multi-domain → run the appropriate generation sub-path for each selected domain in turn.
---
### Backend path
#### stack.yaml
```yaml
language: {language}
framework: {framework}
orm: {orm}
validation: {validation}
migration: {migration}
test: {test_framework}
source: detected
detected_from:
- {manifest_file}
verify: # consumed by `oma verify backend` (see _shared/core/stack-verify.schema.json)
detect: {manifest_file} # e.g. package.json, pyproject.toml
syntax:
cmd: "{syntax_check_cmd}" # e.g. bunx tsc --noEmit
tests:
cmd: "{test_cmd}" # e.g. bun test
skip_if_missing: "{optional_binary}"
raw_sql: # raw-SQL injection grep scan — omit only when the stack has no raw-query escape hatch
patterns:
- "{raw_sql_pattern}" # ORM-appropriate ERE, e.g. "\\$queryRawUnsafe\\(" (Prisma), "f[\"'].*(SELECT|INSERT|UPDATE|DELETE)" (Python)
include_glob: "{source_glob}" # e.g. "*.ts", "*.py"
exclude_dirs: [{build_and_dep_dirs}] # e.g. node_modules, dist, .venv, target
```
Seed `raw_sql` patterns from the matching `variants/{language}/stack.yaml` when one exists (node/python/rust ship with tested patterns); otherwise derive patterns from the detected ORM's raw-query APIs.
#### tech-stack.md
Generate tech stack reference with these MANDATORY sections:
- Framework version and core API
- ORM/DB library and usage
- Validation library
- Migration tool
- Test framework
- Linter/formatter
#### snippets.md
Generate copy-paste code patterns. MANDATORY patterns (all 8 required):
- [ ] Route/Handler + Auth example
- [ ] Validation Schema example
- [ ] ORM Model/Entity example
- [ ] DI (Dependency Injection) example
- [ ] Repository pattern example
- [ ] Paginated Query example
- [ ] Migration example
- [ ] Test example
#### api-template.*
Generate CRUD endpoint boilerplate in the detected language.
---
### Frontend path — Angular
#### stack.yaml
```yaml
language: typescript
framework: angular
framework_version: "{angular_version}" # e.g. "22.0.0" from @angular/core
package_manager: {pm} # bun | pnpm | yarn | npm, from lockfile
ui: {ui_library} # e.g. spartan-ui, angular-material, none
styling: {styling} # e.g. tailwindcss-v4, scss
state: signals
async: {async} # signals-only | rxjs
test: {test_runner} # e.g. vitest, jest, jasmine-karma
source: detected
detected_from:
- angular.json
- package.json
verify: # consumed by `oma verify frontend` (see _shared/core/stack-verify.schema.json)
detect: angular.json
syntax:
cmd: "{syntax_check_cmd}" # e.g. bunx tsc --noEmit -p tsconfig.app.json
tests:
cmd: "{test_cmd}" # e.g. bunx vitest run, or ng test --watch=false
skip_if_missing: "{optional_binary}"
```
#### tech-stack.md
Generate an Angular-specific tech stack reference with these MANDATORY sections:
- Angular version and CLI workflow (`ng generate`, `ng build`, `ng serve`, detected build system)
- Standalone components (no NgModules in new code)
- Change detection: `ChangeDetectionStrategy.OnPush` default; zoneless setup if detected
- Signals as the default state primitive (`signal`, `computed`, `effect`, `input()`, `model()`)
- Lazy routes (`loadComponent` / `loadChildren`)
- UI library + styling integration (detected values)
- Test runner and how to run it
- **RxJS policy: signals first; every non-trivial Observable pipeline MUST ship with a marble test (`TestScheduler` from `rxjs/testing`)** — omit this section only when `async: signals-only`
#### snippets.md
Generate copy-paste code patterns. MANDATORY patterns (all 8 required):
- [ ] Standalone component with `ChangeDetectionStrategy.OnPush` + signals + new control flow (`@if` / `@for`)
- [ ] `inject()`-based service + provider example
- [ ] Lazy route config (`loadComponent` / `loadChildren`)
- [ ] Typed Reactive Form + validation example
- [ ] HttpClient data-access service (or `httpResource`) example
- [ ] Signal ↔ RxJS interop (`toSignal` / `toObservable`)
- [ ] RxJS stream **with paired marble test** (`TestScheduler.run`) — when `rxjs` is in the detected stack; otherwise a signals-based async pattern
- [ ] Component test in the detected runner
#### component-template.ts
Generate a standalone CRUD feature (component + data service) in the detected style. The template must:
- Use standalone components with `ChangeDetectionStrategy.OnPush` and signals for state.
- Be lazy-routable (`loadComponent`) and use `inject()` for DI.
- Use the detected UI library and styling conventions.
- If the data service exposes RxJS streams, include a paired `*.spec.ts` marble test using `TestScheduler`.
---
### Frontend path — React / Next.js / Vue / Svelte
Target is always `oma-frontend`. Mirror the Angular path's file set, adapted to the detected framework:
#### stack.yaml
Same shape as the Angular `stack.yaml` with framework-appropriate values: `language`, `framework` (`nextjs` | `react` | `vue` | `svelte`), `framework_version`, `package_manager` (from lockfile), `ui` (e.g. `shadcn-ui`, `mui`, none), `styling` (e.g. `tailwindcss-v4`, css-modules), `state` (e.g. `zustand`, `redux-toolkit`, context, runes/composables), `test`, `source: detected`, `detected_from`, and a `verify:` block with runnable `syntax.cmd` (e.g. `bunx tsc --noEmit`) and `tests.cmd`.
#### tech-stack.md
MANDATORY sections: framework version + CLI/build workflow (e.g. `next build` / Vite), routing model (App Router / file-based), rendering strategy (SSR/SSG/CSR as detected), state management approach, UI library + styling integration, data-fetching convention, test runner and how to run it.
#### snippets.md
MANDATORY patterns (all 8): typed component (server + client component split where the framework has one) · route/page with data loading · form with validation · shared state example (detected state lib) · API/data-access module · error/loading boundary · UI-library component usage · component test in the detected runner.
#### component-template.*
A CRUD feature (component + data-access module) in the detected framework's idiom, using the detected UI library and styling conventions.
---
### Mobile path — Swift / iOS native
**Adapt, not copy (amendment E):** Seed from `.agents/skills/oma-mobile/variants/swift-ios/` as the baseline, then adapt every value to match the detected project. Specifically:
- Replace placeholder `Features/` module names with the actual feature module names found in the project (e.g., `Features/Auth`, `Features/Home`).
<!-- oma-docs:ignore-start -->
- Set `api_spec` to the actual path where the OpenAPI document lives in this project (default `Core/Networking/openapi.yaml` only when no other location is found).
<!-- oma-docs:ignore-end -->
- Set the minimum iOS deployment target to the value detected from `Package.swift` or `.xcodeproj`; default `17.0` when not specified.
- Populate the DI wiring in the App entry snippet with the real `Client` and service types from the project, not generic placeholders.
Do **not** blind-copy the variant files; the generated `stack/` must be project-specific.
#### stack.yaml — mandatory fields
```yaml
language: swift
framework: swiftui # or uikit — adapt to detected value
ui: swiftui # adapt to detected value
api_generator: swift-openapi-generator
api_spec: {actual_path_to_openapi_yaml}
structure: App/Core/Features/Shared
test: XCTest
source: detected
detected_from:
- {manifest_file} # e.g. Package.swift
verify: # consumed by `oma verify mobile` (see _shared/core/stack-verify.schema.json)
detect: Package.swift
syntax:
cmd: "{non_emitting_syntax_check_cmd}" # detected project command; do not default to swift build
skip_if_missing: "swift"
tests:
cmd: "swift test"
skip_if_missing: "swift"
```
#### tech-stack.md
Generate a Swift-specific tech stack reference with these MANDATORY sections:
- SwiftUI + Observation framework (`@Observable`, iOS 17+)
- `swift-openapi-generator` + `swift-openapi-runtime` + `swift-openapi-urlsession` — SwiftPM build plugin wiring
- `App/Core/Features/Shared` module layout (App = entry/composition root, Core = networking/generated client/DI, Features = screen + view-model verticals, Shared = reusable UI/utils)
- API spec provenance: where `{api_spec}` comes from, how it is kept in sync with the backend producer
- Test framework (XCTest / Swift Testing)
- Linter/formatter (SwiftLint if present)
#### snippets.md — mandatory Swift snippet set (all required)
- [ ] `Package.swift` with the `OpenAPIGenerator` build plugin declared and `openapi.yaml` spec discovery configured
- [ ] `openapi-generator-config.yaml` — generator configuration (namespace, accessibility, etc.)
- [ ] `@Observable` view model (Observation framework, async data loading, error state)
- [ ] SwiftUI feature view consuming the view model
- [ ] `Core/Networking` service wrapping the generated `Client` (URLSession transport, bearer auth middleware)
- [ ] Generated-client call pattern (`Operations.listItems`, `Operations.createItem`, etc.)
- [ ] App entry point + DI composition root (wiring `Client` → service → view model)
- [ ] XCTest example for the service or view model
#### api-template.swift
Generate a CRUD service built on the generated `Client` (using `Operations.*` call patterns from `swift-openapi-generator`). This is the Swift analogue of the backend `api-template.*`. The template must:
- Import and instantiate the generated `Client` (not a hand-rolled `URLSession` request builder).
- Implement list, get-by-id, create, update, and delete operations using `Operations.*` types.
- Handle transport errors and map them to domain error types.
- Use `async/await` throughout.
---
### Mobile path — Flutter / React Native
Seed from `.agents/skills/oma-mobile/variants/flutter/` or `.agents/skills/oma-mobile/variants/react-native/` respectively (same **adapt, not copy** rule as amendment E): generate `stack.yaml` (with a runnable `verify:` block, e.g. `flutter analyze` / `flutter test`, or `bunx tsc --noEmit` / `jest`), `tech-stack.md`, `snippets.md`, and an api-template in the detected idiom, replacing variant placeholders with the project's actual module names, SDK versions, and state-management library.
---
## Step 4: Verify
Confirm generated files meet requirements. Verification commands must follow the execution policy: use detected non-emitting checks, and include build/compile/package commands only when explicitly requested. If no suitable non-emitting check is available, report that limitation instead of substituting a build.
### Backend checks
- [ ] `stack.yaml` has `language`, `framework`, `orm`, `validation` fields
- [ ] `stack.yaml` has a `verify:` block with runnable `syntax.cmd` and `tests.cmd` (otherwise `oma verify backend` cannot dispatch)
- [ ] `stack.yaml` `verify:` includes a `raw_sql` scan with ORM-appropriate patterns (without it `oma verify backend` silently skips the raw-SQL injection check); omit only when the stack has no raw-query escape hatch
- [ ] `snippets.md` contains all 8 mandatory patterns
- [ ] `tech-stack.md` contains all 6 mandatory sections
- [ ] `api-template` file uses the correct language extension
- [ ] Code follows existing project conventions
### Frontend (Angular) checks
- [ ] `stack.yaml` has `language`, `framework`, `framework_version`, `package_manager`, `ui`, `styling`, `test` fields
- [ ] `stack.yaml` has a `verify:` block with runnable `syntax.cmd` and `tests.cmd`
- [ ] `snippets.md` contains all 8 mandatory Angular patterns
- [ ] Generated references mention standalone components, OnPush, signals, lazy routes, and the Angular CLI
- [ ] If `rxjs` is in the detected stack: `snippets.md` includes a runnable marble test (`TestScheduler`) and `tech-stack.md` states that marble tests are mandatory for stream logic
- [ ] A frontend-only repo (Angular, Next.js, React, Vue, Svelte) did NOT also generate `.agents/skills/oma-backend/stack/`
### Frontend (React / Next.js / Vue / Svelte) checks
- [ ] `stack.yaml` has `language`, `framework`, `framework_version`, `package_manager`, `ui`, `styling`, `test` fields and a runnable `verify:` block
- [ ] `snippets.md` contains all 8 mandatory patterns in the detected framework's idiom
- [ ] Generated references match the detected rendering strategy and routing model (no Angular-specific content)
### Mobile (Swift) checks
- [ ] `stack.yaml` has `language`, `api_generator`, `api_spec`, and `structure` fields populated with project-specific values (not variant defaults)
- [ ] `stack.yaml` has a `verify:` block with runnable `syntax.cmd` and `tests.cmd` (otherwise `oma verify mobile` cannot dispatch)
- [ ] `snippets.md` includes the generator configuration snippet (`openapi-generator-config.yaml`) and at least one snippet that uses the generated `Client` via `Operations.*`
- [ ] `api-template.swift` uses the generated client — not hand-rolled `URLSession` request construction
- [ ] `tech-stack.md` documents where `api_spec` originates and how it syncs from the backend producer
- [ ] Module names in snippets reflect real `Features/` modules detected in the project, not generic placeholders
---
## Constraints
- Do NOT modify `.agents/skills/{target_skill}/SKILL.md` (abstract interface is protected)
- Do NOT modify `resources/` common files under any skill
- Only create or modify files in the resolved skill's `stack/` directory
- If `stack/` already exists, preserve unrelated content; ask before overwriting only when replacement is outside the authorized scope
- `target_skill` is always the resolved domain skill (`oma-backend`, `oma-mobile`, or the resolved frontend skill — `angular-developer` / `oma-frontend`); never hardcode a single skill name in generation logic
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!