create-coline-app 2.1.1 → 2.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-coline-app",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Scaffold, push, and iterate on Coline Apps.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,196 @@
1
+ # AGENTS.md — building __APP_NAME__ (a Coline App)
2
+
3
+ You are working on a Coline App. Everything an agent needs is in this file —
4
+ no URLs required. The scaffold compiles, tests, and runs as-is; evolve it
5
+ rather than starting from a blank page.
6
+
7
+ ## What a Coline App is
8
+
9
+ A Coline App runs INSIDE a Coline workspace on Coline's hosted runtime.
10
+ There are no servers, no database, no auth, no deploys — the platform
11
+ provides all of it through the `coline.*` capability API:
12
+
13
+ - `app.config.ts` — the ENTIRE backend: manifest + tools + file types +
14
+ UI handlers, bundled and executed server-side by Coline.
15
+ - `main.tsx` (optional) — a React UI running in a locked sandbox iframe.
16
+ Create it only if the manifest declares a `react` tier surface.
17
+ - `app.test.ts` — tests against an in-memory fake workspace.
18
+
19
+ ## Commands
20
+
21
+ - `npm test` — run tests (in-memory workspace; no Coline instance needed)
22
+ - `npm run typecheck` — strict TS; keep it clean
23
+ - `npx coline-app dev --internal` — watch mode; every save pushes a
24
+ `0.0.0-dev` version that hot-swaps the live app (reload the app page)
25
+ - `npx coline-app push --internal` — one-off push as a workspace-internal
26
+ app (auto-approved, private to the workspace)
27
+ - Needs `COLINE_API_KEY` (workspace API key with `apps.write` scope) and
28
+ `COLINE_BASE_URL` (the Coline instance) in the environment.
29
+
30
+ ## The manifest (`defineApp`)
31
+
32
+ ```ts
33
+ export default defineApp({
34
+ key: "__APP_KEY__", // immutable, lowercase, hyphens
35
+ name: "__APP_NAME__",
36
+ description: "…",
37
+ permissions: [...], // ONLY what you use — users see this list
38
+ hosting: { default: "coline" },// hosted runtime (no external server)
39
+ surfaces: { home: { tier: "tree" } }, // or { tier: "react" } with main.tsx
40
+ files: [...], // custom file types (optional)
41
+ tools: [...], // Kairo tools (optional)
42
+ handlers: { renderHome: async (context) => uiNode },
43
+ });
44
+ ```
45
+
46
+ ### Permissions (request the minimum)
47
+
48
+ | key | grants |
49
+ | --- | --- |
50
+ | `storage.app` | app key-value + record collections |
51
+ | `files.read` / `files.write` | read / create workspace files |
52
+ | `drives.app` | a private drive owned by the app |
53
+ | `members.read` | list workspace members |
54
+ | `search.index` / `search.query` | add to / query workspace search |
55
+ | `notifications.write` | send notifications |
56
+ | `ai.generate` | call Coline's AI model (200/day) |
57
+ | `ai.tools` | expose tools to Kairo (the workspace AI) |
58
+ | `events.emit` | publish app events |
59
+ | `commands.register` | add command-palette commands |
60
+ | `realtime.subscribe` | live updates in the UI |
61
+ | `network.external` | HTTPS fetch to allowlisted hosts only |
62
+
63
+ ## Tools (`defineTool`) — how Kairo operates your app
64
+
65
+ ```ts
66
+ const logDecision = defineTool({
67
+ name: "__APP_KEY__.log_decision", // MUST be prefixed with the app key
68
+ description: "…", // what Kairo reads to pick the tool
69
+ input: z.object({ title: z.string().min(1) }), // zod schema
70
+ effect: "write", // read | write | destructive | external
71
+ execute: async (input, context) => {
72
+ // context.coline — the capability client
73
+ // context.workspace — { id, slug, name }
74
+ // context.actor — who invoked it
75
+ return {
76
+ output: { id: "…" }, // structured result for the model
77
+ card: ui.stack([...]), // optional rich card shown in chat
78
+ };
79
+ },
80
+ });
81
+ ```
82
+
83
+ `effect` is ENFORCED by the runtime, not a hint: a `read` tool cannot
84
+ write. Reads auto-approve; `write` follows the session's permission mode;
85
+ `destructive`/`external` always prompt the user. Be honest.
86
+
87
+ ## Storage — `coline.storage`
88
+
89
+ ```ts
90
+ // Key-value
91
+ await context.coline.storage.kv.set("settings", { theme: "auto" });
92
+ const settings = await context.coline.storage.kv.get("settings");
93
+
94
+ // Typed record collections (the main data store)
95
+ interface Decision { title: string; status: "proposed" | "decided"; tags: string[] }
96
+ const decisions = context.coline.storage.collection<Decision>("decisions");
97
+
98
+ const rec = await decisions.insert({ title: "Use Postgres", status: "decided", tags: ["infra"] });
99
+ await decisions.update(rec.id, { status: "proposed" });
100
+ await decisions.get(rec.id); // envelope | null
101
+ await decisions.delete(rec.id);
102
+ const { records, nextCursor } = await decisions.query({
103
+ where: { status: "decided" }, // exact-match on top-level fields
104
+ orderBy: "title", order: "asc", // or omit for newest-first
105
+ limit: 50, cursor: undefined,
106
+ });
107
+ // Every record comes wrapped: { id, createdBy, createdAt, updatedAt, data }
108
+ ```
109
+
110
+ Limits: 5,000 kv keys · 200,000 records per collection · 200 kB per value.
111
+ `query.where` is exact-match only — filter/search in memory after querying
112
+ when you need contains/ranges (keep result sets bounded with `limit`).
113
+
114
+ ## UI — two tiers
115
+
116
+ ### Tree tier (`ui.*` builders) — renders everywhere incl. mobile & chat cards
117
+
118
+ `ui.stack(children, { direction?, gap? })` · `ui.row(children)` ·
119
+ `ui.heading(text, { level? 1-4 })` · `ui.text(text, { tone? })` ·
120
+ `ui.badge(text, { tone? })` · `ui.card({ title?, description?, children?, footer? })` ·
121
+ `ui.button({ label, action, variant? })` · `ui.divider()` ·
122
+ `ui.emptyState({ title, description? })` · `ui.fileCard({ title, fileId, action? })` ·
123
+ `ui.table({ columns, rows })` · `ui.link({ label, href })` · `ui.image({ src, alt })`
124
+
125
+ Tones: `default | muted | positive | warning | danger`.
126
+ Actions: `actions.openFile(fileId)`, `actions.openAppHome()`,
127
+ `actions.navigate(path)`, `actions.createFile(...)`, `actions.custom(name, payload)`.
128
+ Chat cards (tool results) allow a validated subset — keep them simple
129
+ (stack/heading/text/badge/table/fileCard are all safe). Max 120 nodes.
130
+
131
+ ### React tier (`main.tsx` + `@colineapp/ui`) — full React in a sandbox
132
+
133
+ Set `surfaces: { home: { tier: "react" } }` and create `main.tsx`:
134
+
135
+ ```tsx
136
+ import { createRoot } from "react-dom/client";
137
+ import { ColineAppProvider, useColine, useColineQuery, useColineContext } from "@colineapp/ui";
138
+
139
+ function Home() {
140
+ const coline = useColine(); // capability client
141
+ const { data, isLoading, error, refetch } = // simple async hook
142
+ useColineQuery(() => coline.storage.collection("decisions").query({ limit: 100 }), []);
143
+ // …render
144
+ }
145
+
146
+ const root = document.getElementById("root");
147
+ if (root) createRoot(root).render(<ColineAppProvider><Home /></ColineAppProvider>);
148
+ ```
149
+
150
+ Sandbox rules (enforced, not conventions):
151
+ - NO network (`fetch` is blocked by CSP), no localStorage, no cookies.
152
+ ALL data flows through the `coline.*` client over the capability bridge.
153
+ - Theme tokens follow the host automatically (dark mode included) — use
154
+ the CSS variables / `@colineapp/ui` components, never hardcoded colors.
155
+ - One bundle per app: switch on `useColineContext().surface` if you have
156
+ multiple react surfaces.
157
+
158
+ ## Rate limits & budgets
159
+
160
+ 120 capability calls burst / 20 per second per install · `net.fetch`
161
+ 20 burst / 2 per second · `ai.generate` 200/day · 10,000 executions/day.
162
+ Exceeding any returns a named error visible in the execution log.
163
+
164
+ ## Testing (`app.test.ts` pattern)
165
+
166
+ ```ts
167
+ import { describe, expect, it } from "vitest";
168
+ import { createTestWorkspace } from "@colineapp/sdk/testing";
169
+ import app from "./app.config";
170
+
171
+ it("logs a decision", async () => {
172
+ const workspace = createTestWorkspace(app);
173
+ const result = await workspace.invokeTool("__APP_KEY__.log_decision", {
174
+ title: "Use Postgres",
175
+ });
176
+ expect(result.card).not.toBeNull(); // result: { output, card }
177
+ // Also available: workspace.renderHome(), workspace.files.byType(...),
178
+ // workspace.renderFileSurface(...), workspace.fireSchedule(...)
179
+ });
180
+ ```
181
+
182
+ ## Debugging
183
+
184
+ - Tool throws → the error lands in the app's execution log (developer
185
+ console → your app → Executions) with the message redacted of secrets.
186
+ - Permission denied → the manifest doesn't declare it, the user didn't
187
+ grant it, or the tool's `effect` is too low for the operation.
188
+ - UI blank → check the browser console for the sandbox frame; the
189
+ capability bridge logs denials with reason codes.
190
+
191
+ ## Style rules for the UI
192
+
193
+ - No eyebrow labels / kicker pills above headings. No monospace accent
194
+ fonts. Solid font weights (500–600).
195
+ - Explicit loading, empty, and error states for every async view.
196
+ - Dark mode must work — it's automatic if you use theme tokens.
@@ -0,0 +1,5 @@
1
+ # Claude Instructions
2
+
3
+ Read and follow [`AGENTS.md`](./AGENTS.md) — it contains the complete
4
+ Coline App contract: capabilities, tools, storage, UI builders, sandbox
5
+ rules, limits, and testing patterns.