DEV Community

Cover image for From Specs to Spooky: Using Kiro to Build My AI Psychic Hotline
Hulya
Hulya

Posted on

From Specs to Spooky: Using Kiro to Build My AI Psychic Hotline

For a Halloween-themed hackathon, I decided to build AI Psychic Hotline – a web app where you choose a “realm,” ask a question, the system draws tarot cards, and an AI “psychic” generates a themed

fortune that you can read (and optionally listen to) in a Halloween-inspired interface.

Instead of jumping straight into ad-hoc coding, I tried to use Kiro’s spec-driven workflow end to end: PRD → SRS → SDD → Kiro spec → implementation → quality checks.

This post walks through how I used Kiro’s features – spec-driven development, vibe coding, steering docs, hooks, and MCP – to keep the project structured and consistent.

Project images and links:

landing page

tarot cards

chat

movie suggestion

Starting with specs: PRD, SRS, SDD

Before writing code, I created three documents:

  • PRD (Product Requirements Document)
    High-level goals: what the app should do, core flows, target users, and hackathon constraints.

  • SRS (Software Requirements Specification)
    Detailed functional and non-functional requirements, written using the EARS pattern and following INCOSE-style quality rules (clear, testable, focused requirements).

  • SDD (Software Design Document)
    Architecture and design: Next.js App Router, tarot data model, LLM integration, API routes, and error handling strategy.

Example EARS-style requirement from the SRS:

WHEN the user submits a valid question, THE SYSTEM SHALL generate a new reading by drawing 3–5 tarot cards and calling the LLM to produce a fortune based on the cards and the question.

Once these documents were in place, I fed them into Kiro.

From documents to Kiro spec

My first step with Kiro was spec-driven, not code-driven.

I asked it to:

“Take this PRD, SRS, and SDD for AI Psychic Hotline and turn them into a Kiro spec using your spec-driven development flow.”

kiro view

Kiro created a project spec under:

.kiro/specs/ai-psychic-hotline/
  requirements.md
  design.md
  tasks.md
Enter fullscreen mode Exit fullscreen mode

requirements.md

Kiro combined the PRD and SRS into a requirements document with EARS-style requirements and user stories covering:

  • Text-based question input
  • Optional voice input (STT)
  • Tarot spreads with three realms
  • LLM-based fortune generation
  • Optional TTS playback
  • Movie-style or story-style recommendations aligned with the reading
  • A simple “fate meter” interaction
  • Privacy and error handling

kiro view

design.md

It then turned the SDD into a design document that described:

  • Next.js App Router architecture
  • A TarotDeck module and FortuneService
  • The POST /api/fortune contract (request and response structure)
  • Data models for cards and readings
  • A service-layer approach (business logic outside React components)
  • Error handling and basic security considerations
  • Integration of a 3D fog background using something like Vanta.js

kiro view

tasks.md

Finally, it generated a task list with a logical order:

  • Project setup
  • Tarot data and TypeScript types
  • Backend services and endpoints
  • UI components
  • Optional voice features (STT/TTS)
  • Optional “movie oracle” integration
  • Visual and atmospheric polish
  • Tests and documentation

This turned my initial documents into something Kiro could follow step by step.


Spec-driven development vs. vibe coding

Once the spec was ready, I used two different working styles with Kiro: strict, spec-driven implementation for core logic, and more relaxed “vibe coding” for UI and copy.

Spec-driven: backend and contracts

For backend code and data contracts, I stayed close to the spec. Examples of prompts:

  • “Implement the TarotDeck module and Card type exactly as defined in design.md under Data Model.”
  • “Create the POST /api/fortune endpoint according to the API contract in the design document.”

Kiro generated:

  • TypeScript types for Card and ReadingResponse
  • A TarotDeck that loads card definitions and draws a small spread without duplicates
  • A Next.js API route for /api/fortune that:

    • validates the question
    • draws cards
    • builds an LLM prompt from the cards and question
    • returns { cards, fortune, movieRecommendation? } as JSON

Using the design document as the source of truth meant I didn’t have to keep re-explaining shapes and data; I just asked Kiro to “follow the spec.”

Vibe coding: UI, microcopy, and interactions

For the frontend and overall feel of the app, I used a more conversational, iterative approach:

  • “Generate React components for the question input, tarot spread, and fortune view wired to /api/fortune.”
  • “Write short, on-theme microcopy for labels, placeholders, and error messages.”
  • “Add a simple card reveal animation that feels responsive but not distracting.”
  • “Integrate a subtle 3D fog background behind the main panel.”

This “vibe coding” style worked well for layout, transitions, and tone. The spec ensured structure, while the conversation with Kiro let me experiment quickly with presentation and copy.


Steering docs: controlling style and behavior

Kiro has strong defaults (for example, it likes purple for UI). That didn’t match the Halloween palette I wanted, so I introduced steering docs.

UI steering

I created a colors and typography steering file that defined the palette and fonts. For example:

:root {
  --background: #0a0a0a;
  --foreground: #ededed;
  --accent-primary: #f97316;   /* orange */
  --accent-secondary: #a3e635; /* green */
}
Enter fullscreen mode Exit fullscreen mode

And I added explicit guidance:

  • Don’t use purple/violet/fuchsia or Tailwind purple-* / violet-* classes.
  • Use the accent colors above for highlights and borders.
  • Use serif headings for titles and sans-serif for body text.

Whenever Kiro generated or refactored UI, I reminded it to follow this steering doc. That helped keep the visual language consistent: dark background, orange/green accents, and typography that fit the theme.

kiro view

Persona steering

I also wrote a short steering doc for the “psychic” persona. Key points:

  • Fortunes should be 3–6 sentences, within a reasonable word limit.
  • They should reference the drawn cards by name.
  • The tone should change slightly depending on the chosen realm (Love, Fate, Shadows).
  • No literal claims about real-world outcomes (e.g., health, lottery, etc.).

Kiro then used this to shape the LLM system prompt and example outputs. This reduced the amount of manual tweaking I had to do to keep the “voice” consistent.

Engineering conventions

A third steering doc captured some simple engineering guidelines:

  • Business logic should live in services, not React components.
  • API contracts shouldn’t be changed casually; if they change, update the spec first.
  • Use TypeScript strictly (no any where a proper type is possible).

Together, these steering docs gave Kiro a clear set of constraints and preferences. The result was less drift in style and structure as the project evolved.


Agent hooks: automation for safety and feedback

As the project grew, I used agent hooks to have Kiro react to certain file changes automatically.

A typical pattern was:

  • Trigger: On save of files in src/services/** or tarot-related modules.
  • Action: Run tests and simple smoke tests that:

    • call /api/fortune with a sample question
    • verify the response includes cards and a fortune in the expected shape

This meant that when Kiro refactored FortuneService or tarot logic, the hook would automatically run checks and surface problems without me having to remember to run commands manually.

It’s not full-blown CI, but for a hackathon-scale project it was enough to catch obvious regressions early.

kiro view

MCP tools: project-specific quality checks

Traditional tests tell you whether the code runs and returns the right structure. For this project, I also cared about more qualitative aspects: Are fortunes too long? Do they mention the cards? Are realm tones consistent?

To address this, I experimented with MCP tools as a kind of domain-specific quality layer:

  • A tarot deck validator that checks tarot.json for missing data or inconsistencies.
  • A fortune quality checker that:

    • samples fortunes
    • checks the length against persona guidelines
    • looks for card names in the text
    • evaluates basic tone consistency per realm

These tools flagged issues that unit tests didn’t. For example, an early version of the fortune generator produced text that was structurally correct but too long and verbose. Using MCP, I could measure average length and compliance with the 3–6 sentence rule, then adjust the prompt and token limits and re-check.

This made quality feel less subjective and provided feedback that fit the specific needs of this app.

Atmosphere and UX

Once the main functionality was in place, I used Kiro to help refine the user experience so it felt more like a focused “reading space” than a generic form:

  • A realm selection step before the question, with short descriptions of Love, Fate, and Shadows.
  • A simple question screen with on-theme copy (e.g., a more descriptive placeholder rather than just “Enter question”).
  • A 3D-style fog background using a library like Vanta.js, tuned to stay subtle behind the main panel.
  • Small touches such as:

    • “Shuffling the deck…” while the cards are being drawn
    • “The spirits are silent. Try again in a moment.” for error states
    • A basic “fate meter” interaction where the user can choose to accept or defy the reading

These are not complex features individually, but together they make the experience more coherent and intentional.

Takeaways

Working with Kiro on this project felt less like using a single “code generation button” and more like collaborating with a tool that understood structure, style, and workflow.

In practice, I used Kiro in a few distinct ways:

  • Spec-driven development: Turning PRD/SRS/SDD into requirements.md, design.md, and tasks.md and then implementing core logic from that spec.
  • Vibe coding: Iterating on UI components, microcopy, and subtle interactions through conversational prompts.
  • Steering docs: Controlling visual design, tone of voice, and coding conventions so generated output stayed consistent.
  • Agent hooks: Automating tests and smoke checks after certain changes, which made refactoring safer.
  • MCP tools: Adding domain-specific quality checks that go beyond “does this compile” or “does this return JSON.”

For a hackathon project, this approach was surprisingly effective at keeping things organized and on-theme without losing the ability to iterate quickly.

If I repeat this pattern on another project, I’ll likely follow the same steps: start with lightweight specs, let Kiro build a project spec and tasks, add steering docs early, and then layer on hooks and MCP tools as the core features solidify.

Top comments (0)