@lumerahq/cli 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -226,25 +226,25 @@ async function main() {
226
226
  switch (command) {
227
227
  // Resource commands
228
228
  case "plan":
229
- await import("./resources-R4IYU4BF.js").then((m) => m.plan(args.slice(1)));
229
+ await import("./resources-W7TYJOQN.js").then((m) => m.plan(args.slice(1)));
230
230
  break;
231
231
  case "apply":
232
- await import("./resources-R4IYU4BF.js").then((m) => m.apply(args.slice(1)));
232
+ await import("./resources-W7TYJOQN.js").then((m) => m.apply(args.slice(1)));
233
233
  break;
234
234
  case "pull":
235
- await import("./resources-R4IYU4BF.js").then((m) => m.pull(args.slice(1)));
235
+ await import("./resources-W7TYJOQN.js").then((m) => m.pull(args.slice(1)));
236
236
  break;
237
237
  case "destroy":
238
- await import("./resources-R4IYU4BF.js").then((m) => m.destroy(args.slice(1)));
238
+ await import("./resources-W7TYJOQN.js").then((m) => m.destroy(args.slice(1)));
239
239
  break;
240
240
  case "list":
241
- await import("./resources-R4IYU4BF.js").then((m) => m.list(args.slice(1)));
241
+ await import("./resources-W7TYJOQN.js").then((m) => m.list(args.slice(1)));
242
242
  break;
243
243
  case "show":
244
- await import("./resources-R4IYU4BF.js").then((m) => m.show(args.slice(1)));
244
+ await import("./resources-W7TYJOQN.js").then((m) => m.show(args.slice(1)));
245
245
  break;
246
246
  case "diff":
247
- await import("./resources-R4IYU4BF.js").then((m) => m.diff(args.slice(1)));
247
+ await import("./resources-W7TYJOQN.js").then((m) => m.diff(args.slice(1)));
248
248
  break;
249
249
  // Development
250
250
  case "dev":
@@ -255,7 +255,7 @@ async function main() {
255
255
  break;
256
256
  // Project
257
257
  case "init":
258
- await import("./init-ZFBNNPDX.js").then((m) => m.init(args.slice(1)));
258
+ await import("./init-RPJHBBWD.js").then((m) => m.init(args.slice(1)));
259
259
  break;
260
260
  case "register":
261
261
  await import("./register-OOU477QY.js").then((m) => m.register(args.slice(1)));
@@ -43,45 +43,6 @@ function processTemplate(content, replacements) {
43
43
  var TEMPLATE_EXCLUDE = /* @__PURE__ */ new Set(["template.json"]);
44
44
  var TEMPLATE_RENAMES = /* @__PURE__ */ new Map([["_gitignore", ".gitignore"]]);
45
45
  var TEMPLATE_RAW_COPY = /* @__PURE__ */ new Set(["pnpm-lock.yaml"]);
46
- var STUDIO_PROBLEM_FIRST_FLAG = "studio_problem_first";
47
- var PROBLEM_FIRST_AGENTS_MD = `# {{projectTitle}}
48
-
49
- This file is project-specific durable memory for future Studio agent sessions.
50
- Keep it concise and update it only with context that is not obvious from the
51
- file tree alone.
52
-
53
- ## Purpose
54
-
55
- Describe the business problem this workspace solves and who uses it.
56
-
57
- ## Business Workflow
58
-
59
- Capture the durable workflow, domain terms, review expectations, and important
60
- edge cases.
61
-
62
- ## Domain Notes
63
-
64
- Record user-specific terminology, assumptions, constraints, and preferences
65
- that future agents should preserve.
66
-
67
- ## Project Conventions
68
-
69
- Document non-obvious architecture, naming, validation, or UX conventions that
70
- should guide future changes.
71
-
72
- ## Validation
73
-
74
- List the commands or checks that should pass before changes are considered
75
- done.
76
-
77
- ## Decisions and Follow-ups
78
-
79
- Record durable decisions and unresolved questions that should carry forward.
80
-
81
- Do not use this file as a generic Lumera platform manual, tool guide, or code
82
- index. Do not store secrets, credentials, tokens, private keys, or transient
83
- chat summaries.
84
- `;
85
46
  function copyDir(src, dest, replacements, isRoot = true) {
86
47
  if (!existsSync(dest)) {
87
48
  mkdirSync(dest, { recursive: true });
@@ -121,20 +82,6 @@ function ensureClaudeInstructionsLink(projectRoot) {
121
82
  writeFileSync(claudeMdPath, readFileSync(agentsMdPath, "utf-8"));
122
83
  }
123
84
  }
124
- async function isStudioProblemFirstScaffoldEnabled(targetDir) {
125
- try {
126
- const token = getToken(targetDir);
127
- const api = createApiClient(token);
128
- const me = await api.getMe();
129
- return me.user?.feature_flags?.[STUDIO_PROBLEM_FIRST_FLAG] === true;
130
- } catch {
131
- return false;
132
- }
133
- }
134
- function applyProblemFirstProjectInstructionsScaffold(projectRoot, replacements) {
135
- const agentsMdPath = join(projectRoot, "AGENTS.md");
136
- writeFileSync(agentsMdPath, processTemplate(PROBLEM_FIRST_AGENTS_MD, replacements));
137
- }
138
85
  function isGitInstalled() {
139
86
  try {
140
87
  execSync("git --version", { stdio: "ignore" });
@@ -427,11 +374,7 @@ async function init(args) {
427
374
  [sourceName, finalProjectName],
428
375
  [sourceTitle, projectTitle]
429
376
  ];
430
- const problemFirstScaffold = await isStudioProblemFirstScaffoldEnabled(targetDir);
431
377
  copyDir(templateDir, targetDir, replacements);
432
- if (problemFirstScaffold) {
433
- applyProblemFirstProjectInstructionsScaffold(targetDir, replacements);
434
- }
435
378
  ensureClaudeInstructionsLink(targetDir);
436
379
  const installCommand = existsSync(join(targetDir, "pnpm-lock.yaml")) ? "pnpm install --frozen-lockfile" : "pnpm install";
437
380
  function listFiles(dir, prefix = "") {
@@ -2306,6 +2306,7 @@ async function planAgents(api, localAgents, projectId) {
2306
2306
  if ((remote.description || "") !== (agent.description || "")) diffs.push("description");
2307
2307
  if ((remote.system_prompt || "").trim() !== systemPrompt.trim()) diffs.push("system_prompt");
2308
2308
  if ((remote.model || "") !== (agent.model || "")) diffs.push("model");
2309
+ if (agent.thinking_level !== void 0 && (remote.thinking_level || "") !== agent.thinking_level) diffs.push("thinking_level");
2309
2310
  if ((remote.idle_reap_timeout_seconds ?? 0) !== (agent.idle_reap_timeout_seconds ?? 0)) diffs.push("idle_reap_timeout_seconds");
2310
2311
  if ((remote.policy_script || "").trim() !== (policyScript || "").trim()) diffs.push("policy_script");
2311
2312
  if ((remote.policy_enabled || false) !== (agent.policy_enabled || false)) diffs.push("policy_enabled");
@@ -2399,6 +2400,7 @@ async function applyAgents(api, localAgents, projectId) {
2399
2400
  description: agent.description || "",
2400
2401
  system_prompt: systemPrompt,
2401
2402
  model: agent.model || "",
2403
+ ...agent.thinking_level !== void 0 ? { thinking_level: agent.thinking_level } : {},
2402
2404
  idle_reap_timeout_seconds: agent.idle_reap_timeout_seconds ?? 0,
2403
2405
  skill_ids: skillIds,
2404
2406
  policy_script: policyScript || "",
@@ -2457,6 +2459,7 @@ async function pullAgents(api, platformDir, filterName, projectId) {
2457
2459
  };
2458
2460
  if (agent.description) config.description = agent.description;
2459
2461
  if (agent.model) config.model = agent.model;
2462
+ if (agent.thinking_level) config.thinking_level = agent.thinking_level;
2460
2463
  if (agent.idle_reap_timeout_seconds) config.idle_reap_timeout_seconds = agent.idle_reap_timeout_seconds;
2461
2464
  if (skillSlugs.length > 0) config.skills = skillSlugs;
2462
2465
  if (agent.policy_enabled) config.policy_enabled = true;
@@ -3702,13 +3705,16 @@ async function diff(args) {
3702
3705
  console.log(` description: ${pc2.red(remote.description || "(empty)")} \u2192 ${pc2.green(local.agent.description || "(empty)")}`);
3703
3706
  if ((remote.model || "") !== (local.agent.model || ""))
3704
3707
  console.log(` model: ${pc2.red(remote.model || "(default)")} \u2192 ${pc2.green(local.agent.model || "(default)")}`);
3708
+ if (local.agent.thinking_level !== void 0 && (remote.thinking_level || "") !== local.agent.thinking_level)
3709
+ console.log(` thinking_level: ${pc2.red(remote.thinking_level || "(default)")} \u2192 ${pc2.green(local.agent.thinking_level)}`);
3705
3710
  if ((remote.idle_reap_timeout_seconds ?? 0) !== (local.agent.idle_reap_timeout_seconds ?? 0))
3706
3711
  console.log(` idle_reap_timeout_seconds: ${pc2.red(String(remote.idle_reap_timeout_seconds ?? 0))} \u2192 ${pc2.green(String(local.agent.idle_reap_timeout_seconds ?? 0))}`);
3707
3712
  if ((remote.policy_enabled || false) !== (local.agent.policy_enabled || false))
3708
3713
  console.log(` policy_enabled: ${pc2.red(String(remote.policy_enabled || false))} \u2192 ${pc2.green(String(local.agent.policy_enabled || false))}`);
3709
3714
  const promptChanged = (remote.system_prompt || "").trim() !== local.systemPrompt.trim();
3710
3715
  const policyChanged = (remote.policy_script || "").trim() !== (local.policyScript || "").trim();
3711
- if (!promptChanged && !policyChanged && remote.name === local.agent.name && (remote.description || "") === (local.agent.description || "") && (remote.model || "") === (local.agent.model || "") && (remote.idle_reap_timeout_seconds ?? 0) === (local.agent.idle_reap_timeout_seconds ?? 0) && (remote.policy_enabled || false) === (local.agent.policy_enabled || false)) {
3716
+ const thinkingLevelChanged = local.agent.thinking_level !== void 0 && (remote.thinking_level || "") !== local.agent.thinking_level;
3717
+ if (!promptChanged && !policyChanged && !thinkingLevelChanged && remote.name === local.agent.name && (remote.description || "") === (local.agent.description || "") && (remote.model || "") === (local.agent.model || "") && (remote.idle_reap_timeout_seconds ?? 0) === (local.agent.idle_reap_timeout_seconds ?? 0) && (remote.policy_enabled || false) === (local.agent.policy_enabled || false)) {
3712
3718
  console.log(pc2.green(` \u2713 No changes`));
3713
3719
  } else {
3714
3720
  if (promptChanged) renderFullDiff("system_prompt.md", remote.system_prompt || "", local.systemPrompt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumerahq/cli",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "CLI for building and deploying Lumera apps",
5
5
  "type": "module",
6
6
  "engines": {
@@ -1,215 +1,53 @@
1
1
  # {{projectTitle}}
2
2
 
3
- A Lumera app built with collections, automations, hooks, custom agents, and a React frontend.
3
+ > Status: new project nothing built yet.
4
4
 
5
- ## Project Structure
5
+ <!--
6
+ AGENT CONTRACT — keep this file true.
7
+ This is the project's durable memory, shared by every coding agent that works
8
+ here. It records what cannot be re-derived from the code: why things are the
9
+ way they are, what the business needs, what must not break.
10
+ - Update it in the same turn as the change that makes it stale.
11
+ - Small focused edits; delete stale lines rather than appending duplicates.
12
+ - Leave a section empty rather than padding it.
13
+ - Do NOT put here: platform documentation (skills and prompts carry that),
14
+ code inventories, secrets/credentials, or transient chat summaries.
15
+ - Preserve user-written notes verbatim unless the user says otherwise.
16
+ -->
6
17
 
7
- ```
8
- platform/
9
- ├── collections/ # Collection schemas (JSON) — deployed via lumera apply
10
- ├── automations/ # Python automations (config.json + run.py per automation)
11
- ├── agents/ # Custom AI agents (config.json + system_prompt.md + optional policy.js)
12
- ├── hooks/ # JavaScript hooks on collection lifecycle events
13
- src/
14
- ├── routes/index.tsx # Home page
15
- ├── lib/queries.ts # Data fetching helpers
16
- ├── components/ # Shared UI components
17
- ├── components/ui/ # shadcn UI primitives (installed via MCP)
18
- scripts/ # Utility scripts (seed data, migrations, etc.)
19
- ```
18
+ ## What this is
20
19
 
21
- The starter UI is intentionally throwaway. `src/routes/index.tsx` is just the
22
- default landing page shown before the real app exists, and `index.html` is only
23
- the Vite shell. When building the user's first real workflow or screen, replace
24
- the starter home page with the app experience instead of preserving or building
25
- around the placeholder content.
20
+ One paragraph: the problem this solves, for whom, and its current shape
21
+ (app / automation / agent / one-off analysis). Update when scope changes.
26
22
 
27
- ## Lumera Concepts
23
+ ## Business context
28
24
 
29
- A Lumera app is built from these primitives all defined as code in `platform/`:
25
+ - Who uses it, when, and for what workflow (roles, cadence e.g. month-end)
26
+ - Domain terms with tenant-specific meaning
27
+ - Source-of-truth data and how it arrives (uploads, mailbox, webhooks, integrations)
30
28
 
31
- - **Collections** (`platform/collections/*.json`) — Data tables with typed fields. Deployed via `lumera apply`.
32
- - **Automations** (`platform/automations/*/`) — Python scripts that run on Lumera's servers. Each has a `config.json` and `run.py`.
33
- - **Custom agents** (`platform/agents/*/`) — AI-powered agents with prompts, skills, optional policies, and reusable sessions. Use them for classification, extraction, enrichment, drafting, guided review, and tool-heavy workflows. When creating or changing agents, read the Building Agents skill first.
34
- - **Hooks** (`platform/hooks/*.js`) — JavaScript on collection lifecycle events (`before_create`, `after_update`, etc.).
35
- - **Webhooks** — Receive events from external services (Stripe, GitHub, etc.). Events land in `lm_event_log`; process them with hooks or automations.
36
- - **Mailbox** — Each tenant gets an email address. Inbound emails are persisted to `lm_mailbox_messages` — use hooks to trigger automations on new mail.
37
- - **Email** — Send transactional emails from automations via `from lumera import email`. Logged to `lm_email_logs`.
29
+ ## Data
38
30
 
39
- All resources use **external IDs** in the format `<app-name>:<resource-name>` (auto-derived from `package.json` name + directory/file name).
31
+ - What each collection represents; non-obvious field/status semantics
32
+ - Identifiers that map to external systems, and their formats
40
33
 
41
- ## Project Namespacing
34
+ ## Decisions
42
35
 
43
- **All collection names are automatically namespaced by project.** Always use bare names (e.g. `orders`, not `myproject__orders`) everywhere in collection JSON files, Python SDK calls, JavaScript hooks, and frontend queries. The platform resolves bare names to the project-scoped version transparently.
36
+ - YYYY-MM-DD decisionwhy.
44
37
 
45
- - `pb.ensure_collection("orders", ...)` → stored as `{project}__orders`
46
- - `query_sql("SELECT id FROM orders")` → resolves to `{project}__orders`
47
- - `ctx.dao.find("orders", ...)` → resolves to `{project}__orders`
48
- - `pbSql({ sql: "SELECT id FROM orders" })` in frontend → resolves via `X-Lumera-Project` header
38
+ ## Working agreements
49
39
 
50
- **Never manually prefix collection names with `{project}__`.** Two projects can each have an `orders` collection — they are fully isolated.
40
+ - User preferences: review gates, deploy expectations, formatting
51
41
 
52
- For detailed technical reference (data models, relationships, design decisions), see [architecture.md](architecture.md).
42
+ ## Correctness
53
43
 
54
- ## UI Components
44
+ - What "correct" means here: validations, reconciliations, tolerances, approvals
45
+ - Known edge cases; behavior when required data is missing
55
46
 
56
- **Always use shadcn components for UI.** Never hand-code primitives like buttons, dialogs, cards, inputs, tables, etc.
47
+ ## Gotchas
57
48
 
58
- ### Installing components
49
+ - Things that look wrong but are intentional; workarounds and why
59
50
 
60
- Install shadcn components with `pnpm dlx` (the sandbox's default JS package manager). Match the sandbox-pinned shadcn version:
51
+ ## Follow-ups
61
52
 
62
- ```bash
63
- pnpm dlx shadcn@4.1.2 add button card dialog # Install specific components
64
- pnpm dlx shadcn@4.1.2 add table input label select # Install more as needed
65
- ```
66
-
67
- Components install into `src/components/ui/` and are fully editable.
68
-
69
- ### shadcn MCP server
70
-
71
- A `shadcn` MCP server is available to browse and search the registry. Use `mcp({ server: "shadcn" })` to list available tools, then:
72
-
73
- ```
74
- mcp({ search: "button" }) # Search across all MCP servers
75
- mcp({ server: "shadcn" }) # List shadcn tools
76
- ```
77
-
78
- Useful MCP tools:
79
- - **search_items_in_registries** — fuzzy search components by name
80
- - **view_items_in_registries** — view component source code
81
- - **get_item_examples_from_registries** — find usage examples with full code
82
- - **get_add_command_for_items** — get the install command
83
-
84
- All tools use the `@shadcn` registry (e.g. `@shadcn/button`, `@shadcn/card`).
85
-
86
- ### Project setup
87
-
88
- - `components.json` — configures shadcn paths, aliases, and theme
89
- - `src/styles.css` — shadcn neutral theme CSS variables
90
- - `src/lib/utils.ts` — `cn()` utility, import from `@/lib/utils`
91
- - `src/components/ui/` — installed shadcn components (editable)
92
-
93
- **Do not hand-code UI primitives.** If you need a button, card, dialog, table, form, select, tabs, tooltip, or any standard UI element — install it from shadcn first.
94
-
95
- ## Frontend API Calls
96
-
97
- Custom apps run in an **iframe**. **Always use the `@lumerahq/ui` bridge for API calls.** Never use raw `fetch()` against Lumera API endpoints.
98
-
99
- ```ts
100
- // ✅ Correct — uses postMessage bridge, no CORS issues
101
- import { pbSql, pbCreate, pbUpdate, pbDelete } from '@lumerahq/ui/lib';
102
-
103
- // New read queries: use SQL for collection access, including lookups, lists, reports, joins, ordering, and filters
104
- const result = await pbSql<{ id: string; amount: number }>({
105
- sql: 'SELECT id, amount FROM orders WHERE status = {:status} ORDER BY updated DESC LIMIT 100',
106
- params: { status: 'pending' },
107
- });
108
- const records = result.rows;
109
-
110
- // CRUD
111
- await pbCreate('orders', { amount: 100 });
112
- await pbUpdate('orders', recordId, { status: 'done' });
113
- await pbDelete('orders', recordId);
114
-
115
- // ❌ WRONG — direct fetch will fail with CORS errors
116
- fetch('/api/pb/sql', { method: 'POST', body: JSON.stringify({ sql: '...' }) });
117
- fetch('https://app.lumerahq.com/api/pb/sql', ...);
118
- ```
119
-
120
- **Why:** The bridge sends requests to the parent window via `postMessage`. The parent makes the actual API call on its own origin — no CORS, and auth is handled automatically.
121
-
122
- **Rules:**
123
- - Always import from `@lumerahq/ui/lib` — never write a custom `apiFetch` with raw `fetch()`
124
- - Use `pbSql()` for new read-only collection access
125
- - Existing `pbList()` / `pbSearch()` filter code remains supported for simple existing list/search reads; do not rewrite working code just because SQL exists
126
- - Use record helpers such as `pbCreate()`, `pbUpdate()`, and `pbDelete()` for mutations
127
- - Token is not needed — the bridge handles auth via the parent session
128
- - `X-Lumera-Project` header is not needed — the parent adds it automatically
129
-
130
- ## Shareable Links
131
-
132
- For any URL a human will open (copy-link, invites, emails, QR codes), use `getShareableAppUrl()` or `buildShareableAppUrl()` from `@lumerahq/ui/lib` — **never `window.location.href`**. The app runs inside an iframe, so `window.location` returns the internal `/_apps/{company}/{app}/...` URL instead of the shell's `/app/{appId}` URL.
133
-
134
- ```ts
135
- import { buildShareableAppUrl, getShareableAppUrl } from '@lumerahq/ui/lib';
136
- const link = getShareableAppUrl(); // preserves current path/search/hash
137
- const invoiceLink = buildShareableAppUrl('/invoices/123', { router: 'hash' });
138
- ```
139
-
140
- When reporting a deployed app, use `launch_url` from the deploy response. Do not share `iframe_url`; it is only the internal iframe/static asset mount.
141
-
142
- ## Preview and Verification
143
-
144
- The Studio environment already runs the Vite dev server and exposes it through
145
- the portal's **Preview tab**. Users are not expected to run local developer
146
- commands, and raw localhost URLs are not reachable from the Studio portal.
147
-
148
- **Never tell users to run `pnpm dev`, `pnpm dev:vite`, `npm run dev`, or any
149
- other dev-server command. Never tell users to open `localhost`, `127.0.0.1`, or
150
- Vite ports such as `http://localhost:5173/`.** When explaining how to verify UI
151
- changes, tell them to open the **Preview tab**.
152
-
153
-
154
- ## Workflow
155
-
156
- Follow the user's lead. If they tell you exactly what to build and no critical unknowns block correctness, build it. If critical unknowns exist, ask before encoding assumptions. The workflow below is the default when they describe a goal and leave the approach to you.
157
-
158
- ### Step 1: Understand and Plan
159
- 1. **Read skills first** — Read the matching skill files for API details and patterns.
160
- 2. **Decide one-off vs repeatable** — Determine whether the user needs a one-off answer/artifact or a repeatable workflow. For one-off tasks, answer the question, analyze the data, or produce the requested artifact directly; do not build an app unless the user asks. Propose an app, automation, agent, or durable workflow only when the process will be reused.
161
- 3. **Inspect uploaded files** — If the user uploaded files, inspect them before planning. Identify what each file represents: one-time input data, recurring source data, configuration data, mapping/lookup tables, business rules or domain knowledge, templates, expected input/output examples, exports from another system, or debugging evidence. Decide whether each file should be used only for this turn, stored as durable configuration/reference data, imported into collections, converted into an automation input, or preserved as documentation/examples.
162
- 4. **Identify critical unknowns** — Before proposing or building, list unknowns that affect correctness in real use. Pay special attention to source-of-truth data, ownership/assignment/approval rules, escalation or routing rules, permissions/roles/access control, integrations and required scopes, identifiers and mappings between systems, and what should happen when required data is missing.
163
- 5. **Ask blockers, state assumptions** — Ask concise clarifying questions for blockers. Do not silently create manual lookup tables, fallback rules, durable apps, or business assumptions unless the user explicitly accepts them. For non-blocking details, state your assumption and proceed.
164
- 6. **Discuss the plan** — Propose the **smallest validated useful slice**: a complete horizontal slice that proves the workflow with real data and realistic edge cases. Propose incremental steps that layer on complexity; if decisions are obvious, you can execute multiple steps in one go.
165
- 7. **Stop and ask the user to approve.** Iterate until they're happy with the plan. They may reorder steps, drop features, or add ones you didn't think of.
166
-
167
- ### Step 2: Build (one slice at a time)
168
- 1. **Build horizontally** — Pick the first step. Build the full slice: collection schema and/or agent definition → `lumera apply` → seed data → UI route/components → commit. Each slice should be deployable and usable on its own.
169
- 2. **Stop and ask for feedback** — Tell the user to open the **Preview tab** to see the app. Do not mention local dev-server commands or localhost URLs. Iterate on the slice until they're happy.
170
- 3. **Repeat** — Move to the next step. Build, deploy, get feedback.
171
-
172
- ### Rules
173
- 1. **Code is source of truth** — Edit files in `platform/`, then deploy with `lumera apply`. Don't edit in the Lumera UI.
174
- 2. **Keep docs current** — After each slice, update `architecture.md` with what was built (data models, relationships, hook logic, design decisions). Also update the project description at the top of this file (`AGENTS.md`) so it reflects what the project actually does now — not the original template description.
175
- 3. **Commit and push** — After each slice or significant change, stage your changes, create a meaningful commit with both a title and description, then push. The sandbox is ephemeral — uncommitted work is lost if recycled.
176
- - Use a conventional subject (`feat:`, `fix:`, `chore:`, `docs:`, `test:`) that explains the user-visible change, not just the filenames.
177
- - Include a body that describes why the change was needed, what you changed, and the main areas covered.
178
- - Avoid generic messages like `update files`, `changes`, `WIP`, or filename-only summaries.
179
- - Prefer a HEREDOC so multi-line messages are reliable:
180
- ```bash
181
- git add -A
182
- git commit -m "$(cat <<'EOF'
183
- feat: add invoice approval dashboard
184
-
185
- Adds the first invoice review slice with a collection schema, seeded
186
- example data, and a dashboard for approving or rejecting invoices.
187
-
188
- Covers:
189
- - invoice collection fields and statuses
190
- - approval actions in the UI
191
- - project documentation updates
192
- EOF
193
- )"
194
- git push
195
- ```
196
- 4. **Deploy marker** — When your changes need `lumera apply`, include at the end of your response: `<!-- DEPLOY: short commit message -->`. Skip for frontend-only changes.
197
-
198
- ## File Artifacts
199
-
200
- When you create a file the user should **see** in chat (HTML pages, SVG graphics, CSV exports, PDFs, images, charts, etc.), call `create_artifact` with the file path after writing it. This uploads the file and renders an inline preview or download card in the chat. Without this step, the file exists in the sandbox but the user cannot see it.
201
-
202
- Always call `create_artifact` for: `.html`, `.svg`, `.csv`, `.json`, `.xml`, `.md`, `.txt`, `.pdf`, `.png`, `.jpg`, `.gif`, `.xlsx`, `.docx`, `.pptx`, `.zip` — any file the user asked to see or download.
203
-
204
- ## Python
205
-
206
- Python 3.14 is pre-installed with common data packages (pandas, numpy, matplotlib, pdfplumber, openpyxl, etc.). To install additional packages use `uv add <package>` (preferred) or `pip install <package>`.
207
-
208
- ## Key Commands
209
-
210
- ```bash
211
- lumera plan # Preview changes (dry run)
212
- lumera apply # Deploy everything
213
- lumera run scripts/seed.py # Run a script remotely
214
- lumera status # Show sync status
215
- ```
53
+ - [ ] Open questions and deferred work
@@ -3,5 +3,5 @@
3
3
  "title": "Blank Starter",
4
4
  "description": "A minimal blank canvas with React frontend and Lumera platform scaffolding. No pre-built collections or agents — start from scratch.",
5
5
  "category": "General",
6
- "version": "1.1.0"
6
+ "version": "1.2.0"
7
7
  }
@@ -1,29 +0,0 @@
1
- # Architecture
2
-
3
- > Update this file as you build — it should always reflect the current state of the project so any agent or developer can understand the system from this file alone.
4
-
5
- ## Overview
6
-
7
- This is a blank starter. No collections, automations, or hooks are defined yet. Build your app by adding resources to `platform/` and deploying with `lumera apply`.
8
-
9
- ## Collections
10
-
11
- _None yet — add collection schemas to `platform/collections/` as you build._
12
-
13
- ## Automations
14
-
15
- _None yet — add automation configs to `platform/automations/` as you build._
16
-
17
- ## Hooks
18
-
19
- _None yet — add hook scripts to `platform/hooks/` as you build._
20
-
21
- ## Frontend Routes
22
-
23
- | Route | Purpose |
24
- |-------|---------|
25
- | `/` | Home page — blank canvas, ready to build |
26
-
27
- ## Design Decisions
28
-
29
- _Document key decisions here as you make them._