Skip to content

Latest commit

 

History

History
503 lines (351 loc) · 11 KB

File metadata and controls

503 lines (351 loc) · 11 KB

Here’s a spec you can hand to Codex as “v1 plan” for this app.


1. Project overview

We’re building a personal life logging app with a terminal-style web UI.

Core idea:

  • User types commands or natural language.
  • Backend parses into structured events (workouts, meals, time, etc.).
  • Data is stored in a unified schema so we can do cross-category analysis later.
  • The app eventually proposes schema evolutions (“self-expanding app”) via AI.

Initial focus: 3 categories → workouts, eating, and time worked.


2. Tech stack

Frontend + Backend

  • Next.js (App Router)
  • TypeScript
  • Tailwind CSS
  • Single main page: /console

Storage

  • SQLite (local dev + production)
  • ORM: Drizzle or Prisma (pick one and stick with it; I slightly prefer Drizzle for simplicity)

AI

  • OpenAI API (Node SDK)
  • All AI calls from server-side (no secrets in browser)

Deployment

  • Local dev: next dev with SQLite file.
  • Later: deploy to Vercel/Fly/etc. with a hosted or file-based SQLite solution.

3. High-level architecture

  • Console Page (/console): Full-screen terminal-like interface:

    • scrollable history
    • input at bottom
    • monospace font
  • API endpoint (POST /api/command):

    • Input: { input: string }
    • Output: { lines: string[], meta?: any } lines = text lines to append to the console history.
  • Core logic (framework-agnostic):

    • core/commandTypes.ts – TS types for commands & events.
    • core/commandParser.ts – turns raw string into internal Command (optionally using AI).
    • core/commandHandler.ts – executes command against DB and returns display lines.
    • core/repo.ts – DB access helpers.
    • core/ai.ts – wrappers for OpenAI calls.

Next.js is just the thin HTTP + rendering layer calling into core/**.


4. Data model (v1)

We want a generic event model that can handle workouts, meals, and time blocks.

4.1 Tables

events

  • id (string or UUID, PK)
  • timestamp (datetime, not null)
  • kind (string, not null) e.g. "workout" | "meal" | "time_block" | "custom"
  • subtype (string, nullable) e.g. "strength", "climbing", "breakfast", "deep_work"
  • payload (JSON, not null) Structured fields, see below.
  • created_at (datetime)
  • updated_at (datetime)

migrations (for AI-driven schema evolution, v1 just scaffolding)

  • id (integer PK, autoincrement)
  • applied_at (datetime)
  • description (string)
  • details (JSON) – description of what changed
  • For now we don’t need to actually modify DB schema; we track “virtual schema changes” here.

(This keeps implementation simple: we evolve meaning in code and payload JSON, not DB columns.)

4.2 Payload conventions (not enforced by DB)

We’ll define conventions for payload JSON per kind:

Workouts (kind = "workout")

Payload example (per event – one set or one exercise entry):

{
  "sport": "strength",
  "exercise": "bench press",
  "sets": 3,
  "reps": 5,
  "weight": { "value": 165, "unit": "lb" },
  "rpe": 8,
  "note": "felt heavy"
}

Meals (kind = "meal")

{
  "meal_type": "lunch",
  "description": "burrito + chips",
  "calories": { "value": 900, "unit": "kcal" },
  "protein_g": 35,
  "carbs_g": 90,
  "fat_g": 25
}

(Protein/carbs/fat may be missing early; we can add later.)

Time blocks (kind = "time_block")

{
  "project": "Make Studio",
  "task": "docs",
  "duration": { "value": 2.5, "unit": "hours" },
  "focus_mode": "deep",
  "note": "good flow"
}

We’ll centralize these conventions in code (TypeScript types and helper functions), not DB.


5. Command model

We support a small set of core verbs, but allow full natural language.

5.1 Core verbs (reserved)

  • log – create an event
  • show – display events
  • stats – aggregated view
  • define – schema/meta changes (v1: stub)
  • edit / delete – modify events (v1: basic)
  • undo – revert last write (v1: basic)
  • help – show help

Everything else can be free-form and is parsed by AI into these.

5.2 Internal Command type (rough sketch)

type BaseCommand = {
  raw: string;
};

type LogCommand = BaseCommand & {
  type: "log";
  kind: string;          // "workout" | "meal" | "time_block" | custom
  payload: any;          // structured fields as per conventions
};

type ShowCommand = BaseCommand & {
  type: "show";
  kind?: string;         // optional filter
  filters?: any;         // time range, field filters, etc.
};

type StatsCommand = BaseCommand & {
  type: "stats";
  kind?: string;
  groupBy?: string;      // e.g. "day", "project"
  timeRange?: { from?: Date; to?: Date };
};

// For v1, define Edit/Delete/Undo simply:
type EditCommand = BaseCommand & { type: "edit_last"; /* v1: just edit last */ };
type DeleteCommand = BaseCommand & { type: "delete_last"; };
type UndoCommand = BaseCommand & { type: "undo"; };

type HelpCommand = BaseCommand & { type: "help"; };

type Command =
  | LogCommand
  | ShowCommand
  | StatsCommand
  | EditCommand
  | DeleteCommand
  | UndoCommand
  | HelpCommand
  | { type: "unknown"; raw: string };

6. Parsing strategy

6.1 Simple commands (no AI needed)

If the input matches a structured pattern, we parse locally:

Examples:

log meal lunch burrito 900
log workout squat 3x5 @ 225
log time 2.5h Make Studio
show meals today
show workouts this week
stats time by project this month
help
undo

We can implement a small regex-based parser for these patterns in core/commandParser.ts.

6.2 Natural language (use AI)

If we can’t confidently parse locally, we call AI:

Examples:

had a big burrito for lunch, about 900 calories
did squats 3x5 at 225, pretty heavy
worked like 2.5 hours on Make Studio docs
what did I eat yesterday?
how many hours did I work this week?

We send the raw input + some context (known kinds, recent events) to OpenAI with a strict JSON schema response describing a Command.

Server-side only; no AI calls from the client.


7. Console page behavior

7.1 Layout (Tailwind)

Single page:

  • Container: full viewport (min-h-screen) with dark background.

  • Inside:

    • div for history:

      • scrollable
      • monospace (font-mono)
      • each entry prefixed with > for user input and plain text for system output.
    • form at bottom:

      • single <input> or <textarea> for command

      • when submitted:

        • optimistic echo: > {input}
        • call /api/command
        • append returned lines

7.2 History state

In React:

  • const [history, setHistory] = useState<string[]>([])
  • const [input, setInput] = useState("")
  • const [commandHistory, setCommandHistory] = useState<string[]>([]) (for ↑ navigation later)

Add:

  • Auto-scroll to bottom on new lines.
  • Simple client-side input history (ArrowUp to recall previous commands) – v1 nice-to-have.

7.3 Response format

/api/command returns:

type CommandResponse = {
  lines: string[];
  // optional: structured metadata later
};

Frontend just appends lines to history.


8. API route: /api/command

File: app/api/command/route.ts

Rough flow:

import { NextRequest, NextResponse } from "next/server";
import { parseCommand } from "@/core/commandParser";
import { handleCommand } from "@/core/commandHandler";

export async function POST(req: NextRequest) {
  const { input } = await req.json();

  const command = await parseCommand(input); // may call AI
  const result = await handleCommand(command); // talks to DB

  return NextResponse.json({
    lines: result.lines,
    meta: result.meta ?? null,
  });
}

handleCommand returns something like:

type CommandResult = {
  lines: string[];
  meta?: any;
};

9. Command behaviors (v1)

We can give Codex specific behaviors to implement for each:

9.1 log (workout / meal / time_block)

Examples:

log workout squat 3x5 @ 225
log meal lunch burrito 900
log time 2.5h Make Studio docs

Behavior:

  • Construct LogCommand with:

    • kind: "workout" / "meal" / "time_block"
    • payload: per conventions
  • Insert one events row.

  • Return lines:

Logged workout:
  exercise: squat
  sets: 3
  reps: 5
  weight: 225 lb

or

Logged meal:
  type: lunch
  description: burrito
  calories: 900 kcal

9.2 show

Examples:

show meals today
show workouts this week
show time last 7 days

Behavior:

  • Parse time range:

    • today, this week, last 7 days, yesterday
  • Filter events by:

    • kind
    • timestamp in range
  • Render as ASCII table.

Meals example:

Meals – Today

Time   Type    Description        Calories
-----  ------  -----------------  --------
08:30  breakfast  oats + coffee   450
12:20  lunch      burrito         900
19:00  dinner     pasta           800
-----------------------------------------
Total                       2,150 kcal

9.3 stats

Examples:

stats workouts last 4 weeks
stats calories by day last 7 days
stats time by project this month

Behavior:

  • Build aggregated queries:

    • workouts: count per week, volume, etc.
    • meals: sum calories per day.
    • time: sum duration per project.
  • Render as ASCII table and simple bar charts using characters.

Example:

Calories by day – last 7 days

Date       Calories  Chart
---------- --------  ----------
12-26-25   2100      ███
12-27-25   2300      ████
12-28-25   1950      ██▌
12-29-25   2600      █████
12-30-25   2400      ████▌
12-31-25   2200      ███▌
01-01-26   2000      ███

9.4 help

Show:

  • examples of log/show/stats
  • emphasize “you can also just type normal sentences”

9.5 edit_last, delete_last, undo (v1 minimal)

  • edit last (for now): just echo the last event in a structured way and say “(editing not implemented yet)” → stub, or allow simple note update.
  • delete last: delete the most recent event for the current user.
  • undo: alias to delete last for v1.

(We can wire this up to a lastEventId in session or query the latest event.)


10. AI layer (v1 scope)

In core/ai.ts:

  • A function parseWithAI(raw: string, context: ParseContext): Promise<Command>.

For v1:

  • Use a system prompt explaining:

    • available kinds (workout, meal, time_block)
    • basic field conventions
  • Request a JSON object matching our Command schema.

  • On errors or weird responses:

    • fall back to type: "unknown" with a friendly error line.

We do not need to implement schema evolution / migrations in v1; just parsing.


11. Future extensions (for context, not for v1)

  • AI-proposed schema changes:

    • “you keep mentioning RPE, add a field?”
    • “you’re logging protein, add macros to meals?”
  • “Templates” for repeated workouts/time blocks.

  • Cross-domain queries:

    • “Do I work less on days I lift heavy?”
    • “How does calorie intake relate to hours worked?”

These all build on the same event model and command framework.