Locara

44 — locara init Template Structure

What locara init <template> <name> actually creates on disk. Templates are the developer’s first contact with the framework; they define the mental model.

Available templates (v1)

TemplatePurposeModalities
blankMinimal scaffold; just chattext-to-text
chatPolished chat app with streaming + tool callstext-to-text, optional tools
transcribeAudio + STT scaffold (the Transcribe reference)speech-to-text + text-to-text + embedding
docvaultDocument + OCR scaffold (the DocVault reference)image-to-text + text-to-text + embedding
voiceVoice-to-voice assistant scaffoldvoice-to-voice

The transcribe and docvault templates produce starter code with the same architecture as the reference apps (described in apps/transcribe/SPEC.md and apps/docvault/SPEC.md); the templates are simpler, with reduced features for learning.

Common files (every template)

my-app/
├── locara.json              # Manifest
├── package.json             # Bun + Tauri deps
├── bun.lockb                # Bun lockfile
├── tsconfig.json
├── vite.config.ts
├── src-tauri/
│   ├── Cargo.toml           # depends on locara-runtime, locara-core, etc.
│   ├── tauri.conf.json
│   ├── build.rs
│   └── src/
│       ├── main.rs          # Tauri bootstrap; wires up Locara plugins
│       └── lib.rs
├── src/
│   ├── main.tsx             # React entry
│   ├── App.tsx              # Root component
│   ├── lib/                 # App-specific logic
│   ├── routes/              # If using a router
│   └── components/ui/       # Empty initially; populated via `locara add`
├── db/
│   ├── schema.sql           # Empty initially; user adds tables
│   └── migrations/
│       └── 0001_initial.sql # Auto-generated from schema.sql
├── tests/
│   ├── capabilities.test.ts # Required capability tests
│   └── unit.test.ts
├── public/
│   └── icon.png             # Default Locara-branded icon; user replaces
├── screenshots/             # Empty; user adds before publish
├── README.md
├── .gitignore
└── .env.example

Common file contents

locara.json (blank template)

{
  "schema": "locara/v1",
  "name": "my-app",
  "publisher": "your-publisher-id",
  "version": "0.0.1",

  "displayName": "My App",
  "description": "An app I'm building with Locara.",
  "license": "Apache-2.0",
  "icon": "./public/icon.png",
  "screenshots": [],
  "category": "productivity",

  "modalities": ["text-to-text"],
  "tooling": [],

  "capabilities": {
    "net": false,
    "fs": {
      "user-selected": false,
      "app-data": "read-write"
    },
    "device": {
      "microphone": false,
      "camera": false
    },
    "models": [
      "qwen2.5-3b-instruct-q4@sha256:..."
    ]
  },

  "profiles": {
    "mid": { "min_ram_gb": 16 }
  },

  "storage": {
    "schema": "./db/schema.sql"
  },

  "entry": {
    "frontend": "./src/main.tsx"
  }
}

package.json

{
  "name": "my-app",
  "version": "0.0.1",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "locara dev",
    "build": "locara build",
    "test": "locara test",
    "publish": "locara publish"
  },
  "dependencies": {
    "@locara/sdk": "^0.1.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "@locara/manifest": "^0.1.0",
    "@locara/test": "^0.1.0",
    "@tauri-apps/api": "^2.0.0",
    "@tauri-apps/cli": "^2.0.0",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "tailwindcss": "^3.4.0",
    "typescript": "^5.0.0",
    "vite": "^5.0.0"
  }
}

src-tauri/Cargo.toml

[package]
name = "my-app"
version = "0.0.1"
edition = "2024"

[dependencies]
locara-runtime = { version = "0.1" }
locara-core = { version = "0.1" }
locara-storage = { version = "0.1" }
tauri = { version = "2.0", features = [] }
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

[build-dependencies]
tauri-build = "2.0"

src-tauri/src/main.rs (boilerplate)

// SPDX-License-Identifier: Apache-2.0

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

fn main() {
    tauri::Builder::default()
        .plugin(locara_runtime::plugin::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

The locara_runtime::plugin::init() call registers all the Locara Tauri commands automatically. The app doesn’t have to wire each command manually.

src/main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import { App } from './App'
import './styles.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

src/App.tsx (blank template)

import { useState } from 'react'
import { llm } from '@locara/sdk'

export function App() {
  const [input, setInput] = useState('')
  const [response, setResponse] = useState('')

  async function handleSubmit() {
    const stream = llm.chatStream({
      model: 'qwen2.5-3b-instruct-q4',
      messages: [{ role: 'user', content: input }],
    })
    setResponse('')
    for await (const delta of stream) {
      setResponse(prev => prev + (delta.content ?? ''))
    }
  }

  return (
    <main className="min-h-screen flex flex-col p-8 bg-zinc-900 text-white">
      <h1 className="text-3xl font-semibold mb-6">My App</h1>
      <textarea
        value={input}
        onChange={(e) => setInput(e.target.value)}
        className="w-full p-4 rounded bg-zinc-800 mb-4"
        rows={4}
        placeholder="Ask anything..."
      />
      <button
        onClick={handleSubmit}
        className="self-start px-6 py-2 rounded bg-blue-600 hover:bg-blue-700 transition"
      >
        Send
      </button>
      {response && (
        <div className="mt-6 p-4 rounded bg-zinc-800 whitespace-pre-wrap">{response}</div>
      )}
    </main>
  )
}

db/schema.sql (blank template)

-- Your app's database schema goes here.
-- Run `locara db migrate` after changing this file.

-- Example:
-- CREATE TABLE conversations (
--   id          TEXT PRIMARY KEY,
--   title       TEXT NOT NULL,
--   created_at  INTEGER NOT NULL DEFAULT (unixepoch())
-- );

tests/capabilities.test.ts (every template)

import { test, expect, runApp } from '@locara/test'

test('does not attempt network', async () => {
  const app = await runApp()
  await app.simulate('user-interaction')
  expect(app.networkAttempts).toEqual([])
})

test('only loads declared models', async () => {
  const app = await runApp()
  await app.simulate('user-interaction')
  for (const model of app.modelsLoaded) {
    expect(app.manifest.capabilities.models).toContainEqual(
      expect.stringContaining(model)
    )
  }
})

// Add app-specific capability tests here as you build features.

README.md

# My App

A Locara app.

## Development

```bash
bun install
bun run dev

Build

bun run build

Output: dist/my-app-0.0.1.dmg (signed locally with your dev cert).

Publish

bun run publish

Submits to Locara registry CI for build + sign + notarize.

Manifest

See locara.json for capability declarations. Don’t edit manifest fields outside the defaults without understanding what they do — capability changes can require user re-consent on update.

See Locara docs for the full SDK reference.


### `.gitignore`

node_modules/ dist/ target/ src-tauri/target/ .env .locara/ *.dylib *.dSYM .DS_Store


### `.env.example`

For locara init --from-prompt if you use it later

ANTHROPIC_API_KEY= OPENAI_API_KEY=

For publishing (set after locara login)

LOCARA_PUBLISHER_TOKEN=


## Per-template variations

### `chat` template

Adds:

- `<Chat>`, `<MessageBubble>`, `<ChatInput>` copied via `locara add`.
- A `messages` table in `db/schema.sql`.
- Streaming chat UI in `App.tsx` with conversation history.
- Optional tool-calling demo (commented; user uncomments to enable).

### `transcribe` template

Adds:

- Manifest declares `speech-to-text`, microphone, transcription model.
- `<AudioInput>`, `<TranscriptStream>` components.
- Schema with `recordings` + `segments` tables (subset of full Transcribe).
- Live recording UX in `App.tsx`.

### `docvault` template

Adds:

- Manifest declares `image-to-text`, OCR + filesystem tools.
- `<DocDropzone>`, `<DocPreview>` components.
- Schema with `documents` + `chunks` tables (subset of full DocVault).
- Drop-zone + ingestion in `App.tsx`.

### `voice` template

Adds:

- Manifest declares `voice-to-voice`, microphone, models for STT + LLM + TTS.
- `<AudioInput>`, `<TranscriptStream>` components.
- Voice-loop logic in `App.tsx` (record → transcribe → LLM → TTS → play).
- Note about computational cost for full pipeline.

## `locara init --from-prompt "..."`

When the user provides a natural-language prompt, the CLI:

1. Calls the user's chosen LLM (Anthropic / OpenAI / etc.) with the prompt + a system prompt explaining Locara templates + manifest schema.
2. Receives a JSON response indicating: which template to start from, what manifest changes to make, what initial code to write.
3. Applies the JSON as a "starter customization" on top of the chosen template.
4. Writes the resulting files.

Example prompt: `"a fully-local journal app that lets me search across all my entries"` →

- Starts from `blank` template.
- Adds `nomic-embed-text-v1.5` to models, declares `text-to-embedding` modality.
- Generates an `entries` table with FTS5 + sqlite-vec.
- Writes a starting App.tsx with entry-list + new-entry form + search.

The user reviews the generated files, runs `locara dev`, iterates from there.

This is the agent-friendly authoring path. The templates ground what the LLM produces in valid Locara structure.

## Updating templates

Templates are versioned alongside the framework. When `@locara/sdk@1.5` ships, the templates are updated to use any new APIs.

Old projects don't auto-update; users opt in by running `locara init` again into a fresh directory and copying over their app code (rare).

## Open questions

- **(open)** Should `locara init` install dependencies (`bun install`) automatically or just print the next-step? Auto is friendlier; explicit is more transparent. Lean auto.
- **(open)** Tailwind preconfigured or opt-in? Components rely on Tailwind; lean preconfigured.
- **(open)** Default model in templates — Qwen2.5-3B (covers low-tier; smaller download)? Or 7B for better quality? Lean 3B as default; user can change.
- **(open)** Should templates include `git init`? Probably yes; first commit on creation.

## Cross-references

- [06-cli.md](./06-cli.md) — `locara init` command spec
- [02-manifest.md](./02-manifest.md) — `locara.json` schema
- [05-sdk.md](./05-sdk.md) — SDK API used in templates
- [11-components.md](./11-components.md) — components added via `locara add`
- [Transcribe SPEC](../apps/transcribe/SPEC.md) — what the transcribe template grows into
- [DocVault SPEC](../apps/docvault/SPEC.md) — what the docvault template grows into