MANDATORY RULES: VIOLATION IS FORBIDDEN
- Response language follows
languagesetting in.agents/oma-config.yamlif configured. - NEVER skip steps. Execute from Step 1 in order.
- 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.jsonis a Spartan/shadcn-style marker) - Test runner: Vitest? Jest? Jasmine/Karma?
- RxJS: is
rxjsused 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 SwiftUIin source files or aSwiftUIframework dependency inPackage.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: Confirm
Single-domain: backend
Present detection results and ask for confirmation:
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 and ask for confirmation:
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 and ask for confirmation:
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, before showing any per-domain confirm block, 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.)
After the user selects, show the per-domain confirmation block(s) for the chosen domain(s) and confirm each before generating.
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-developeroroma-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
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
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.OnPushdefault; 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 (
TestSchedulerfromrxjs/testing) — omit this section only whenasync: 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) — whenrxjsis 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.OnPushand signals for state. - Be lazy-routable (
loadComponent) and useinject()for DI. - Use the detected UI library and styling conventions.
- If the data service exposes RxJS streams, include a paired
*.spec.tsmarble test usingTestScheduler.
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).
- Set
api_specto the actual path where the OpenAPI document lives in this project (defaultCore/Networking/openapi.yamlonly when no other location is found).
- Set the minimum iOS deployment target to the value detected from
Package.swiftor.xcodeproj; default17.0when not specified. - Populate the DI wiring in the App entry snippet with the real
Clientand 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
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: "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 wiringApp/Core/Features/Sharedmodule 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.swiftwith theOpenAPIGeneratorbuild plugin declared andopenapi.yamlspec discovery configured - [ ]
openapi-generator-config.yaml— generator configuration (namespace, accessibility, etc.) - [ ]
@Observableview model (Observation framework, async data loading, error state) - [ ] SwiftUI feature view consuming the view model
- [ ]
Core/Networkingservice wrapping the generatedClient(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-rolledURLSessionrequest 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/awaitthroughout.
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.
Backend checks
- [ ]
stack.yamlhaslanguage,framework,orm,validationfields - [ ]
stack.yamlhas averify:block with runnablesyntax.cmdandtests.cmd(otherwiseoma verify backendcannot dispatch) - [ ]
stack.yamlverify:includes araw_sqlscan with ORM-appropriate patterns (without itoma verify backendsilently skips the raw-SQL injection check); omit only when the stack has no raw-query escape hatch - [ ]
snippets.mdcontains all 8 mandatory patterns - [ ]
tech-stack.mdcontains all 6 mandatory sections - [ ]
api-templatefile uses the correct language extension - [ ] Code follows existing project conventions
Frontend (Angular) checks
- [ ]
stack.yamlhaslanguage,framework,framework_version,package_manager,ui,styling,testfields - [ ]
stack.yamlhas averify:block with runnablesyntax.cmdandtests.cmd - [ ]
snippets.mdcontains all 8 mandatory Angular patterns - [ ] Generated references mention standalone components, OnPush, signals, lazy routes, and the Angular CLI
- [ ] If
rxjsis in the detected stack:snippets.mdincludes a runnable marble test (TestScheduler) andtech-stack.mdstates 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.yamlhaslanguage,framework,framework_version,package_manager,ui,styling,testfields and a runnableverify:block - [ ]
snippets.mdcontains 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.yamlhaslanguage,api_generator,api_spec, andstructurefields populated with project-specific values (not variant defaults) - [ ]
stack.yamlhas averify:block with runnablesyntax.cmdandtests.cmd(otherwiseoma verify mobilecannot dispatch) - [ ]
snippets.mdincludes the generator configuration snippet (openapi-generator-config.yaml) and at least one snippet that uses the generatedClientviaOperations.* - [ ]
api-template.swiftuses the generated client — not hand-rolledURLSessionrequest construction - [ ]
tech-stack.mddocuments whereapi_specoriginates 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 for the resolved domain skill, ask before overwriting target_skillis 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