@notis_ai/cli 0.2.6 → 0.2.8

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.
Files changed (151) hide show
  1. package/README.md +17 -0
  2. package/dist/scaffolds/notis-database/app/page.tsx +38 -41
  3. package/dist/scaffolds/notis-database/lib/types.ts +5 -7
  4. package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
  5. package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
  6. package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
  7. package/dist/scaffolds/notis-database/metadata/screenshot-4.png +0 -0
  8. package/dist/scaffolds/notis-database/metadata/screenshot-5.png +0 -0
  9. package/dist/scaffolds/notis-database/metadata/screenshot-fixtures.json +1839 -0
  10. package/dist/scaffolds/notis-database/notis.config.ts +48 -1
  11. package/dist/scaffolds/notis-database/package-lock.json +3935 -0
  12. package/dist/scaffolds/notis-database/package.json +2 -1
  13. package/dist/scaffolds/notis-database/packages/sdk/package.json +36 -0
  14. package/dist/scaffolds/notis-database/packages/sdk/src/components/DocumentEditor.tsx +93 -0
  15. package/dist/scaffolds/notis-database/packages/sdk/src/components/Markdown.tsx +60 -0
  16. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
  17. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
  18. package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
  19. package/dist/scaffolds/notis-database/packages/sdk/src/config.ts +196 -0
  20. package/dist/scaffolds/notis-database/packages/sdk/src/documents.ts +229 -0
  21. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useBackend.ts +41 -0
  22. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
  23. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocument.ts +78 -0
  24. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocuments.ts +121 -0
  25. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
  26. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotis.ts +34 -0
  27. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
  28. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTool.ts +64 -0
  29. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTools.ts +56 -0
  30. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
  31. package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
  32. package/dist/scaffolds/notis-database/packages/sdk/src/index.ts +83 -0
  33. package/dist/scaffolds/notis-database/packages/sdk/src/provider.tsx +43 -0
  34. package/dist/scaffolds/notis-database/packages/sdk/src/runtime.ts +220 -0
  35. package/dist/scaffolds/notis-database/packages/sdk/src/styles.css +186 -0
  36. package/dist/scaffolds/notis-database/packages/sdk/src/ui.ts +15 -0
  37. package/dist/scaffolds/notis-database/packages/sdk/src/vite.ts +56 -0
  38. package/dist/scaffolds/notis-database/packages/sdk/tsconfig.json +15 -0
  39. package/dist/scaffolds/notis-database/src/mock-runtime.ts +4 -1
  40. package/dist/scaffolds/notis-journal/CHANGELOG.md +4 -0
  41. package/dist/scaffolds/notis-journal/app/page.tsx +1 -0
  42. package/dist/scaffolds/notis-journal/package-lock.json +4615 -0
  43. package/dist/scaffolds/notis-journal/package.json +2 -2
  44. package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +52 -1
  45. package/dist/scaffolds/notis-notes/app/page.tsx +39 -46
  46. package/dist/scaffolds/notis-notes/app/phosphor-icons.ts +596 -0
  47. package/dist/scaffolds/notis-notes/lib/visible-properties.ts +144 -0
  48. package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
  49. package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
  50. package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
  51. package/dist/scaffolds/notis-notes/metadata/screenshot-4.png +0 -0
  52. package/dist/scaffolds/notis-notes/metadata/screenshot-5.png +0 -0
  53. package/dist/scaffolds/notis-notes/metadata/screenshot-fixtures.json +752 -0
  54. package/dist/scaffolds/notis-notes/notis.config.ts +48 -3
  55. package/dist/scaffolds/notis-notes/package-lock.json +4636 -0
  56. package/dist/scaffolds/notis-notes/package.json +7 -3
  57. package/dist/scaffolds/notis-notes/packages/sdk/package.json +36 -0
  58. package/dist/scaffolds/notis-notes/packages/sdk/src/components/DocumentEditor.tsx +93 -0
  59. package/dist/scaffolds/notis-notes/packages/sdk/src/components/Markdown.tsx +60 -0
  60. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
  61. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
  62. package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
  63. package/dist/scaffolds/notis-notes/packages/sdk/src/config.ts +196 -0
  64. package/dist/scaffolds/notis-notes/packages/sdk/src/documents.ts +229 -0
  65. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useBackend.ts +41 -0
  66. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
  67. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocument.ts +78 -0
  68. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocuments.ts +121 -0
  69. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
  70. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotis.ts +34 -0
  71. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
  72. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTool.ts +64 -0
  73. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTools.ts +56 -0
  74. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
  75. package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
  76. package/dist/scaffolds/notis-notes/packages/sdk/src/index.ts +83 -0
  77. package/dist/scaffolds/notis-notes/packages/sdk/src/provider.tsx +43 -0
  78. package/dist/scaffolds/notis-notes/packages/sdk/src/runtime.ts +220 -0
  79. package/dist/scaffolds/notis-notes/packages/sdk/src/styles.css +186 -0
  80. package/dist/scaffolds/notis-notes/packages/sdk/src/ui.ts +15 -0
  81. package/dist/scaffolds/notis-notes/packages/sdk/src/vite.ts +56 -0
  82. package/dist/scaffolds/notis-notes/packages/sdk/tsconfig.json +15 -0
  83. package/dist/scaffolds/notis-random/CHANGELOG.md +12 -2
  84. package/dist/scaffolds/notis-random/app/history/page.tsx +5 -4
  85. package/dist/scaffolds/notis-random/app/page.tsx +141 -74
  86. package/dist/scaffolds/notis-random/components/ui/button.tsx +50 -0
  87. package/dist/scaffolds/notis-random/components/ui/card.tsx +16 -0
  88. package/dist/scaffolds/notis-random/components/ui/input.tsx +23 -0
  89. package/dist/scaffolds/notis-random/components.json +20 -0
  90. package/dist/scaffolds/notis-random/lib/notis-tools.ts +26 -7
  91. package/dist/scaffolds/notis-random/lib/rng.ts +179 -19
  92. package/dist/scaffolds/notis-random/lib/roll-record.ts +103 -16
  93. package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
  94. package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
  95. package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
  96. package/dist/scaffolds/notis-random/metadata/screenshot-4.png +0 -0
  97. package/dist/scaffolds/notis-random/metadata/screenshot-5.png +0 -0
  98. package/dist/scaffolds/notis-random/metadata/screenshot-fixtures.json +753 -0
  99. package/dist/scaffolds/notis-random/notis.config.ts +47 -3
  100. package/dist/scaffolds/notis-random/package-lock.json +4513 -0
  101. package/dist/scaffolds/notis-random/package.json +6 -2
  102. package/dist/scaffolds/notis-random/packages/sdk/package.json +36 -0
  103. package/dist/scaffolds/notis-random/packages/sdk/src/components/DocumentEditor.tsx +93 -0
  104. package/dist/scaffolds/notis-random/packages/sdk/src/components/Markdown.tsx +60 -0
  105. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
  106. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
  107. package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
  108. package/dist/scaffolds/notis-random/packages/sdk/src/config.ts +196 -0
  109. package/dist/scaffolds/notis-random/packages/sdk/src/documents.ts +229 -0
  110. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useBackend.ts +41 -0
  111. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
  112. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocument.ts +78 -0
  113. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocuments.ts +121 -0
  114. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
  115. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotis.ts +34 -0
  116. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
  117. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTool.ts +64 -0
  118. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTools.ts +56 -0
  119. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
  120. package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
  121. package/dist/scaffolds/notis-random/packages/sdk/src/index.ts +83 -0
  122. package/dist/scaffolds/notis-random/packages/sdk/src/provider.tsx +43 -0
  123. package/dist/scaffolds/notis-random/packages/sdk/src/runtime.ts +220 -0
  124. package/dist/scaffolds/notis-random/packages/sdk/src/styles.css +186 -0
  125. package/dist/scaffolds/notis-random/packages/sdk/src/ui.ts +15 -0
  126. package/dist/scaffolds/notis-random/packages/sdk/src/vite.ts +56 -0
  127. package/dist/scaffolds/notis-random/packages/sdk/tsconfig.json +15 -0
  128. package/dist/scaffolds/notis-random/tailwind.config.ts +8 -1
  129. package/dist/scaffolds.json +8 -6
  130. package/package.json +2 -1
  131. package/skills/notis-apps/SKILL.md +449 -144
  132. package/skills/notis-apps/cli.md +15 -0
  133. package/skills/notis-cli/SKILL.md +8 -4
  134. package/skills/notis-onboarding/BRIEF.md +93 -0
  135. package/src/cli.js +52 -7
  136. package/src/command-specs/apps.js +154 -21
  137. package/src/command-specs/diagnostics.js +674 -0
  138. package/src/command-specs/helpers.js +4 -1
  139. package/src/command-specs/index.js +8 -0
  140. package/src/command-specs/onboarding.js +216 -0
  141. package/src/command-specs/smoke.js +386 -0
  142. package/src/command-specs/tools.js +189 -5
  143. package/src/runtime/app-platform.js +65 -4
  144. package/src/runtime/desktop-auth.js +66 -17
  145. package/src/runtime/errors.js +1 -0
  146. package/src/runtime/output.js +21 -3
  147. package/src/runtime/profiles.js +214 -18
  148. package/src/runtime/telemetry.js +92 -0
  149. package/src/runtime/transport.js +29 -1
  150. package/template/.harness/index.html.tmpl +30 -2
  151. package/template/packages/sdk/src/config.ts +38 -1
@@ -5,216 +5,521 @@ description: Design and package Notis apps. Use when users want an app that grou
5
5
 
6
6
  # Notis Apps Skill
7
7
 
8
- Use this skill when the user wants to create, edit, debug, or package a Notis app. Notis apps are Vite + React projects that deploy into the Notis Portal as installed apps for the current user or team.
8
+ Use this skill when the user wants a packaged Notis app -- task manager, CRM, dashboard, internal tool, etc. Notis apps are **Vite + React projects** that deploy into the Notis portal as installed apps for the current user or team.
9
9
 
10
10
  Run the Notis CLI through NPX, for example `npx --package @notis_ai/cli@latest -- notis apps list`. Notis Desktop keeps the CLI auth profile current. This `notis-apps` skill is delivered through normal Notis skill sync for the signed-in user, alongside other curated skills.
11
11
 
12
- ## Platform Model
12
+ For the platform model, architecture, and local development workflow, read [docs/notis-apps-platform.md](../../../docs/notis-apps-platform.md) first. This skill is the execution guide for using that platform correctly.
13
13
 
14
- A Notis app is:
14
+ ## How Apps Are Built
15
15
 
16
- - a Vite + React project
17
- - built with `@notis/sdk`
18
- - configured through `notis.config.ts`
19
- - packaged as an ES module bundle with `app.js`, `app.css`, and `manifest.json`
20
- - rendered by the Notis Portal as a React component, not an iframe
21
- - connected to Notis data and tools through the Portal-provided runtime and SDK hooks
16
+ All Notis apps are built using the Notis CLI, either locally in a repo workspace or inside a Vercel Sandbox. The platform contract is the same in both cases:
17
+ - the app is a Vite + React project
18
+ - the app uses `@notis/sdk`
19
+ - the app is packaged as an ES module bundle
20
+ - the portal renders it as a React component inside the portal's React tree
22
21
 
23
- Use the Notis CLI for the app lifecycle:
22
+ ## App Workspace Tool Rules
23
+
24
+ - Apps are the top-level packaging unit in Notis.
25
+ - Use `LOCAL_NOTIS_CREATE_APP` to register a new app, then use the Notis CLI in the shell to build and deploy it.
26
+ - Use `LOCAL_NOTIS_UPDATE_APP` to update app metadata.
27
+ - Use `LOCAL_NOTIS_LIST_APPS` to discover the user's apps.
28
+ - The full app lifecycle uses the CLI in the shell. Always run it through the registry-resolved package, for example `npx --package @notis_ai/cli@latest -- notis apps init`; use the same prefix for `build` and `deploy`. In hosted shells, the CLI is pre-authenticated through `NOTIS_JWT`.
29
+ - There are no `save_app` or `load_app` tools. Do not attempt to call them. Use only the CLI for app file operations.
30
+ - Use `npx --package @notis_ai/cli@latest -- notis apps scaffolds list` to discover bundled starting points before scaffolding.
31
+ - Use `LOCAL_NOTIS_LIST_PUBLIC_APP_STORE` only to help users choose apps to install, not as a source-clone workflow.
32
+ - Use `LOCAL_NOTIS_INSTALL_APP` only when the user explicitly wants to install from a listing.
33
+ - Before installing, inspect the listing's `required_capabilities`. Explain each
34
+ requested capability and obtain explicit approval; only then pass the matching
35
+ token in `approved_capabilities`. Never infer capability approval. The current
36
+ workspace-wide read token is `workspace_databases_read`.
37
+
38
+ ## Architecture
39
+
40
+ ```
41
+ Notis CLI (local workspace or Vercel Sandbox)
42
+ -> Vite + React project with @notis/sdk
43
+ -> notis apps init / dev / build / verify / create / link / pull / deploy
44
+ -> ES module bundle (app.js + app.css) + manifest
45
+ -> Portal renders as React component with real tools/databases
46
+ ```
47
+
48
+ `deploy` updates the linked installed app for the current account or team. After the user explicitly confirms the App Details page is ready, `apps publish --confirm-ready` submits that deployed version to the Team or Public Store review flow.
49
+
50
+ ### Key Components
51
+
52
+ 1. **@notis/sdk** (`packages/sdk/`) -- SDK for app developers
53
+ - `@notis/sdk` -- NotisProvider and generic runtime hooks such as useTool and useTools
54
+ - `@notis/sdk/config` -- `defineNotisApp()` for notis.config.ts
55
+ - `@notis/sdk/vite` -- `notisViteConfig()` for vite.config.ts
56
+ - `@notis/sdk/styles.css` -- shadow-safe app shell styles and base app-surface classes
57
+
58
+ 2. **CLI** (`packages/cli/src/command-specs/apps.js`) -- local development should center on `apps dev`, plus init, build, verify, create, deploy, link, pull, doctor, and list
59
+
60
+ 3. **Server** (`server/routers/portal_views/`) -- Returns signed bundle URLs, proxies tool calls
61
+
62
+ 4. **Portal** (`portal/src/components/apps/`) -- Renders app bundles as React components via AppViewRenderer
63
+
64
+ ### Runtime Bridge
65
+
66
+ Apps communicate with the platform through the `NotisRuntime` interface, provided by the portal via React context:
67
+
68
+ - **Portal development**: the portal loads a local bundle for an active dev session and still provides a real `NotisRuntime`.
69
+ - **Portal**: the portal creates a real `NotisRuntime` and passes it as a prop to `NotisProvider`. All calls go to `/portal_views/runtime_query` via fetch with the user's JWT.
70
+ - The portal mounts the app inside a shadow-scoped content surface and injects the runtime before app mount. There is no supported window-global runtime fallback.
71
+
72
+ App code never accesses the runtime directly -- it uses SDK hooks (`useTool`, `useTools`, `useNotis`, etc.) which read from the `NotisProvider` context.
73
+
74
+ ## Hard Rules
75
+
76
+ 1. **React + Vite only** -- No Next.js, no custom server
77
+ 2. **ES module bundle** -- Vite builds a library-mode bundle with React externalized
78
+ 3. **Component rendering** -- Apps render as React components directly in the portal. No iframes.
79
+ The portal owns the `ShadowRoot`, theme tokens, and runtime provider.
80
+ 4. **HTTP bridge** -- Runtime calls use fetch to `/portal_views/runtime_query`
81
+ 5. **Declarative tools** -- Tool access declared in `notis.config.ts`, enforced server-side
82
+ 6. **shadcn + Notis theme** -- Apps must use shadcn components with the live Notis theme provided by the portal
83
+ 7. **Phosphor icons only** -- Always `phosphor:` prefix. Never emojis.
84
+ 8. **Database refs only** -- `notis.config.ts` references existing databases by slug. The schema source of truth lives in the `databases` table, not in the manifest. Every native database is owned by exactly one app (`databases.owner_app_id`): creating one through `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` requires the owning app's slug or id in the `app` argument, install/dev materialization stamps ownership automatically, and deleting an app deletes its databases and their documents.
85
+ An app-owned database slug is a stable deployed contract because bundles and
86
+ collection routes may call it directly. Do not try to rename that slug with
87
+ a schema tool; rename the display title instead.
88
+ 9. **Use NPX for CLI commands** -- Always run `npx --package @notis_ai/cli@latest -- notis ...`.
89
+ 10. **`apps deploy` is not store publishing** -- `apps deploy` updates an installed app only and persists its source snapshot. Store review starts separately with `apps publish --confirm-ready` after explicit user approval.
90
+ 11. **Routes are canonical** -- Define navigation only in `manifest.routes`. Every configured route must declare an explicit `slug`. Do not rely on legacy `manifest.views`.
91
+ 12. **Portal-owned sidebars stay portal-owned** -- If a route uses `collection.sidebar`, treat that sidebar as platform chrome. Do not remove it, recreate it inside app JSX, or replace it with a custom in-app folder rail.
92
+ 13. **Portal globals are off-limits** -- Never use `window.__NOTIS_RUNTIME__`, query portal-owned DOM hooks, or create global DOM portals.
93
+ 14. **Prefer inline optimistic edits** -- Rename-like edits for collections, app-owned rows, and sidebar-backed entities should use inline editing with an optimistic UI update, then roll back on backend failure. Use modals only when the edit requires multiple fields or destructive confirmation.
94
+ 15. **Local development first; deploy is user-gated** -- Iterate with `apps dev` and let the **user** test the app in the desktop **Local development** sidebar group. Do NOT run `apps create` or `apps deploy` on your own initiative, even after a clean build and verify. `deploy` installs the app onto the user's account and is a one-directional, outward-facing action — treat it like publishing: only run it when the user has tested the local build and **explicitly asks you to deploy**. Building a new app end-to-end without deploying is the expected, complete outcome. (See the **Local-development-first handoff** in the Workflow.)
95
+ 16. **Installed app links are explicit** -- A mounted dev session updates an installed workspace app only when the local checkout is linked by app id in `.notis/state.json` and the dev-session registry mirrors that id. Name or slug matches may be suggestions, never update targets. After first install, keep that link so Portal and CLI show/update the same app instead of creating duplicates.
96
+ 17. **Development identities stay separate** -- `.notis/state.json` uses `dev_app_id` for the hidden development-runtime row and `app_id` only for an accessible installed workspace app. The Electron registry mirrors the installed id as `targetAppId`. Never pass a runtime app whose manifest has `is_dev: true` to `notis apps link`; it is not an install/update target. Current CLIs reject that link and repair stale hidden, deleted, or inaccessible targets on the next `apps dev` without erasing valid state on transport or authentication failures.
97
+ 18. **Mounted means Portal-acknowledged** -- A running process, registry row, or HTTP 200 proves only that the app is serving. Before telling the user an app is mounted, require the current `apps dev` process to report `Mounted in Notis`. That status comes from an exact session/app/slug/nonce acknowledgment written by Electron only after the app enters the Portal's final **Local development** sidebar model. Do not require a manual Electron route/render check merely to prove the mount; route rendering is a separate behavior or visual verification task.
98
+ 19. **Store submission is user-gated** -- Run `apps publish --confirm-ready` only after the user explicitly confirms the current App Details page and Store listing are ready. Deploy the exact approved local state first. The command must reject missing confirmation, incomplete listing media, a local/deployed version mismatch, private visibility, or an existing pending review.
99
+ 20. **Bump `notisAppVersion` for every Store update** -- `package.json` must contain a semver `notisAppVersion`. For an existing Store app, increment it beyond the currently published registry version before deploy and submission; registry CI rejects equal or lower versions.
100
+ 21. **`CHANGELOG.md` owns release history** -- Keep the complete release history in one root `CHANGELOG.md`, newest entry first. Do not add new `versionNotes` values to `notis.config.ts`. Use `## [Release title] - YYYY-MM-DD`, or `{PR_MERGE_DATE}` for an unpublished entry. App Details reads **What’s New** and **Version History** from the deployed package manifest, while the Store reads them from the latest published snapshot; unpublished workspace edits must never change the Store page. The manifest also exposes `package.json` `notisAppVersion` as the package version shown in App Details.
101
+ 22. **Database rows are private unless explicitly seeded** -- A string declaration such as `databases: ['notes']` publishes schema only and never includes the developer's rows. Use `{ slug: 'templates', seedDocuments: true }` only for small, intentional starter content that every installer should receive. Never enable it for user-created notes, history, leads, or other personal data.
102
+ 23. **Public submissions are complete, reviewable packages** -- The registry PR must contain the full editable source tree, Store assets, exact source-declared database schemas, and only explicitly seeded starter rows. Registry CI validates those boundaries before merge; do not hand-edit `notis-listing.json` or strip source files to make a check pass. Fix the app locally, redeploy, and resubmit.
103
+
104
+ ## Anti-patterns -- NEVER do these
105
+
106
+ These are the most common mistakes agents make. Each one wastes time and produces broken results.
107
+
108
+ - **NEVER assume app deploys create databases for you** -- Create or update databases through native Notis database tools or the assistant first, then reference them by slug in `notis.config.ts`. Database creation requires the owning app to exist: pass its slug or id in the `app` argument of `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` (create the app first with `LOCAL_NOTIS_CREATE_APP` if needed). A database can only be referenced by the app that owns it.
109
+ - **NEVER bypass the supported workflow by manually stitching together low-level save or lint calls from a local workspace** -- Local agents should go through the NPX Notis CLI for `apps pull`, `apps dev`, `apps build`, `apps verify`, `apps create`, `apps link`, and `apps deploy`.
110
+ - **NEVER invent a Store clone path** -- `npx --package @notis_ai/cli@latest -- notis apps pull` only pulls source for an app the user can already access as an installed app. Pulling source directly from arbitrary Store listings is not supported yet.
111
+ - **NEVER deploy on your own initiative** -- A clean `apps build` + `apps verify` is NOT a signal to deploy. `apps create` / `apps deploy` install the app onto the user's account; run them only after the user has tested the local (`apps dev`) build and explicitly asked you to deploy. When you finish building, hand off for local testing and stop — do not create or deploy unprompted.
112
+ - **NEVER submit without explicit approval** -- A deploy request alone does not authorize Store submission. Run `npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready` only when the user confirms App Details is ready for Store review.
113
+ - **NEVER write raw `views/<slug>/index.js` files** -- Write standard React pages in `app/`.
114
+ - **NEVER invent `npx --package @notis_ai/cli@latest -- notis apps push` or bypass the review flow** -- Source moves through `apps pull` and `apps deploy`; `apps publish --confirm-ready` submits the deployed snapshot through the same authenticated review endpoint as App Details.
115
+ - **NEVER treat `apps deploy` as store submission** -- It updates the linked installed app for the current account or team scope only. Store submission is a separate, explicitly confirmed step.
116
+ - **NEVER explore server code or tool schemas to invent an alternative app workflow** -- Use the Notis CLI.
117
+ - **NEVER work around a missing `collection.sidebar` portal tree by rendering a duplicate sidebar inside the app** -- keep the route manifest as the source of truth and escalate the missing portal sidebar as a platform bug instead.
118
+ - **NEVER invent a custom visual language** -- Do not ship full-screen gradients, glassmorphism, bright neon palettes, or raw HTML controls as the primary UI. Apps should look like a natural extension of the portal.
119
+ - **NEVER hand-roll buttons/cards/badges when the scaffold already provides shadcn primitives** -- Prefer `@/components/ui/*` and portal token classes such as `bg-background`, `bg-card`, `border-border`, and `text-muted-foreground`.
120
+
121
+ ## Workflow
122
+
123
+ **Default to the bundled scaffold catalog, not a blank project.** Most user requests overlap with one of the scaffolds shipped inside the CLI. Starting from a bundled scaffold is faster than a bare app and does not require backend Store access.
124
+
125
+ 1. **Find a starting point.** Run `npx --package @notis_ai/cli@latest -- notis apps scaffolds list`. If something close matches, run `npx --package @notis_ai/cli@latest -- notis apps init "My App" --from <slug>`. Only run plain `npx --package @notis_ai/cli@latest -- notis apps init "My App"` when no scaffold fits.
126
+ 2. **Pull only installed apps.** If the user explicitly wants to fork an app they already installed, run `npx --package @notis_ai/cli@latest -- notis apps list`, then `npx --package @notis_ai/cli@latest -- notis apps pull <app-id> ./<dir>`. To fork a Store app that is not installed, tell the user to install it from `/store` first.
127
+ 3. **Edit the listing source.** Update `name` (slug), `title`, description, icon, accent, author, categories, tagline, databases, routes, and tools in `notis.config.ts`. Declare a database as a string for schema-only Store packaging; use `{ slug: 'templates', seedDocuments: true }` only when its rows are deliberate starter content for every installer. Keep the complete Store release history in the root `CHANGELOG.md`, newest entry first, using `## [Release title] - YYYY-MM-DD` (or `{PR_MERGE_DATE}` before publication). The first entry powers **What’s New** and the same file powers **Version History**. `icon` is a `phosphor:<name>` value or `metadata/icon.png`; when unset the app shows its **two-letter initials** everywhere (store, sidebar, app details). `accent` optionally pins the avatar color to one of `blue|violet|emerald|amber|rose|sky|fuchsia|teal` (default derived from the app id). Icon/accent flow through deploy onto the app row + listing and can also be set later via the `update_app` tool.
128
+ 4. **Build pages in `app/`.** Reuse scaffold code wherever it fits.
129
+ 5. **Iterate live.** Run `npx --package @notis_ai/cli@latest -- notis apps dev` so the desktop sidebar's Local development group discovers the app and the Electron Portal renders the local bundle. In the Notis monorepo's supervised `dev.sh` stack, keep app projects under `apps/<slug>/`; the root supervisor discovers that directory, owns the long-running process, and restarts it. A root-level app folder is not supervised and can disappear as soon as an agent-owned terminal exits.
130
+ 6. **Capture listing screenshots.** Declare 3–6 screenshots in `notis.config.ts`, each with a stable `path`, descriptive `alt`, and optional `route`/`scenario`/`focus`/`theme`, then run `npx --package @notis_ai/cli@latest -- notis apps screenshot`. Use `focus` to frame a real app root without empty browser canvas; use `theme: 'light'` or `theme: 'dark'` to match both the Portal render and Store backdrop, and pair both modes when that best represents the app. It renders the configured states in a headless harness and writes exact 2000x1250 PNGs under `metadata/`, using the deterministic Store presentation by default (`--raw` is diagnostic only). Apps are icon-led like Raycast — the icon set in `notis.config.ts` represents the app, so there is no cover image, only these screenshots. Never hand-author the PNGs; regenerate them when routes or UI change.
131
+ 7. **Verify locally.** Run `npm install`, then `npx --package @notis_ai/cli@latest -- notis apps build` and `npx --package @notis_ai/cli@latest -- notis apps verify`. Surface the verify report and fix failures.
132
+ 8. **Local-development-first handoff — STOP HERE.** Keep `apps dev` running and hand off to the user: tell them the app is live in the desktop **Local development** sidebar group (green `DEV` badge) and ask them to test it there. Building a new app to this point, without deploying, is a **complete and expected** result. Do NOT proceed to `apps create` / `apps deploy` yet — wait for the user to test and explicitly ask to deploy. (`apps dev` is what puts the app in Local development; without a running session the app never appears there.) **Before handing off, complete all three acceptance checks:**
133
+ 1. Registry: `cat "$CONDUCTOR_WORKSPACE_PATH/.context/app-dev-sessions.json"` lists the app's `devSlug` with a fresh `lastHeartbeatAt`. If it is absent (the session landed in global `~/.notis` instead), restart with `NOTIS_APP_DEV_SESSIONS_FILE="$CONDUCTOR_WORKSPACE_PATH/.context/app-dev-sessions.json"`.
134
+ 2. Bundle: the session's loopback `/snapshot` URL responds successfully and contains the expected manifest/routes.
135
+ 3. Mount acknowledgment: capture the current `apps dev` status `Mounted in Notis: <app name>`. This is the deterministic proof that the nonce-backed session reached the Portal's final Local development sidebar model. If the CLI says only `Serving locally`, do not claim the app is mounted. Manual Electron rendering is optional here; use it when the task specifically requires UI/behavior verification, not as the mount handshake.
136
+ See Troubleshooting → *App is missing from Local development* if any check fails.
137
+ 9. **Deploy only when the user asks.** Once the user has tested locally and explicitly requests a deploy, run `npx --package @notis_ai/cli@latest -- notis apps create "<name>" .` (first time) then `npx --package @notis_ai/cli@latest -- notis apps deploy --direct`, or link first with `npx --package @notis_ai/cli@latest -- notis apps link <id> .` / pass `--app-id <id>` for an existing app. Deploy installs or updates the app on the user's account (it appears under **Workspace**, not Local development). After first install, `.notis/state.json` must point at the installed app id so future local-dev actions become **Update**, not another **Install**.
138
+ 10. **Submit only after confirmation.** When the user explicitly confirms the current App Details page is ready, ensure the approved state is deployed, then run `npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready`. The command submits Team apps immediately or opens the Public Store registry review PR. Without that confirmation, stop after deploy.
139
+
140
+ ### Quick start
141
+
142
+ Steps 1–3 are the agent's job on a build request. Step 4 is **user-gated** — do not run it until the user has tested the local build and asked you to deploy.
24
143
 
25
144
  ```bash
26
- npx --package @notis_ai/cli@latest -- notis apps init
145
+ # 1. Pick a scaffold and scaffold
146
+ npx --package @notis_ai/cli@latest -- notis apps scaffolds list
147
+ npx --package @notis_ai/cli@latest -- notis apps init "My App" --from <scaffold-slug>
148
+ cd my-app
149
+ npm install
150
+
151
+ # 2. Develop against the Electron Portal, then HAND OFF for the user to test.
152
+ # Keep this running — it is what surfaces the app in the Local development
153
+ # sidebar group. This is the finish line for a build request.
27
154
  npx --package @notis_ai/cli@latest -- notis apps dev
155
+ # ... iterate until the app looks right in the Local development sidebar group ...
156
+
157
+ # 3. Build, capture listing screenshots, and verify (still local — no deploy)
28
158
  npx --package @notis_ai/cli@latest -- notis apps build
29
159
  npx --package @notis_ai/cli@latest -- notis apps screenshot
30
160
  npx --package @notis_ai/cli@latest -- notis apps verify
31
- npx --package @notis_ai/cli@latest -- notis apps create
32
- npx --package @notis_ai/cli@latest -- notis apps link
33
- npx --package @notis_ai/cli@latest -- notis apps pull
34
- npx --package @notis_ai/cli@latest -- notis apps deploy
161
+
162
+ # 4. ONLY after the user tested locally and asked to deploy: create + deploy.
163
+ # This writes .notis/state.json so future deploys update this app.
164
+ npx --package @notis_ai/cli@latest -- notis apps create "My App" .
165
+ npx --package @notis_ai/cli@latest -- notis apps deploy --direct
166
+
167
+ # 5. ONLY after the user explicitly confirms App Details is ready for Store review
35
168
  npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready
36
- npx --package @notis_ai/cli@latest -- notis apps doctor
37
169
  ```
38
170
 
39
- Use this command form everywhere:
171
+ If deploying to an existing app without linking first, pass `--app-id` directly. Prefer linking when this checkout will keep being used for development:
40
172
 
41
173
  ```bash
42
- npx --package @notis_ai/cli@latest -- notis apps ...
174
+ npx --package @notis_ai/cli@latest -- notis apps link <app-id> .
175
+ npx --package @notis_ai/cli@latest -- notis apps deploy --direct --app-id <app-id>
43
176
  ```
44
177
 
45
- ## Hard Rules
46
-
47
- - Use React + Vite only. Do not use Next.js or a custom server for app projects.
48
- - Build standard pages in `app/`. Do not write raw `views/<slug>/index.js` files.
49
- - Keep navigation in `notis.config.ts` `routes`; each route needs a stable `slug`.
50
- - Reference existing databases by slug in `notis.config.ts`; do not assume deploy creates databases automatically.
51
- - Every native database is owned by exactly one app. Creating one with `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` requires the owning app's slug or id in the `app` argument (create the app first with `LOCAL_NOTIS_CREATE_APP` if needed); deleting an app deletes its databases and their documents.
52
- - Declare runtime tool access in `notis.config.ts` `tools`.
53
- - Use `@notis/sdk` hooks instead of direct runtime access.
54
- - Do not rely on `window.__NOTIS_RUNTIME__` or portal-owned DOM hooks.
55
- - `npx --package @notis_ai/cli@latest -- notis apps deploy` updates an installed app. It does not publish to the public App Store.
56
- - **Local development first; deploy is user-gated.** Iterate with `apps dev` and hand off for the **user** to test in the desktop **Local development** sidebar group. Do not run `apps create` / `apps deploy` on your own — a clean build/verify is not permission to deploy. Deploy only when the user has tested the local build and explicitly asks. Finishing a build without deploying is the expected outcome.
57
- - **Installed app links are explicit.** A mounted dev session updates an installed app only when the checkout is linked by app id in `.notis/state.json` and the active dev-session registry mirrors that id. Name or slug matches may be suggestions, never update targets.
58
- - **Store submission is separately user-gated.** Run `apps publish --confirm-ready` only after the user explicitly confirms the current App Details page is ready. Deploy that exact approved state first; never infer Store approval from a deploy request.
59
- - **Bump `notisAppVersion` for every Store update.** `package.json` must contain a semver `notisAppVersion`; increment it beyond the currently published registry version before deploying an update.
60
- - **Keep release history in one root `CHANGELOG.md`.** Put the newest entry first and use `## [Release title] - YYYY-MM-DD`, or `{PR_MERGE_DATE}` before publication. Do not add new `versionNotes` fields to `notis.config.ts`. App Details reads **What’s New** and **Version History** from the deployed package manifest; the Store reads the latest published snapshot, so unpublished workspace edits never change it. The manifest exposes `package.json` `notisAppVersion` as the package version shown in App Details.
61
- - Public submissions still use the registry review flow; the CLI invokes the same authenticated submission endpoint as App Details.
62
- - Use Phosphor icon names with the `phosphor:` prefix. Do not use emoji icons.
63
- - Prefer scaffolded shadcn components and portal theme tokens over custom visual systems.
64
-
65
- ## App Workflow
66
-
67
- 1. Discover existing apps if needed:
178
+ Or if editing an installed app:
68
179
 
69
180
  ```bash
70
181
  npx --package @notis_ai/cli@latest -- notis apps list
182
+ npx --package @notis_ai/cli@latest -- notis apps pull <installed-app-id> ./my-app
183
+ cd my-app
184
+ npm install
185
+ npx --package @notis_ai/cli@latest -- notis apps dev
186
+ npx --package @notis_ai/cli@latest -- notis apps build
187
+ npx --package @notis_ai/cli@latest -- notis apps verify
188
+ npx --package @notis_ai/cli@latest -- notis apps deploy --direct --app-id <existing-app-id>
71
189
  ```
72
190
 
73
- 2. Start from a scaffold or pull an existing app:
191
+ ## Building an App
74
192
 
75
- ```bash
76
- npx --package @notis_ai/cli@latest -- notis apps scaffolds list
77
- npx --package @notis_ai/cli@latest -- notis apps init "My App"
78
- npx --package @notis_ai/cli@latest -- notis apps pull <app-id> ./my-app
79
- ```
193
+ ### Step 1: Define the config
80
194
 
81
- 3. Install dependencies and develop. Keep `apps dev` running and **hand off to the user** — it is what surfaces the app in the desktop **Local development** sidebar group, and letting the user test there is the finish line for a build request:
195
+ Create `notis.config.ts` with:
196
+ - **name** -- Display name
197
+ - **databases** -- Slug references to existing Notis databases
198
+ - **routes** -- Route-first sidebar entries with explicit `slug`, optional `parentSlug`, and optional `collection.sidebar` tree config
199
+ - **tools** -- Tool names the app can call at runtime
82
200
 
83
- ```bash
84
- npm install
85
- npx --package @notis_ai/cli@latest -- notis apps dev
201
+ For collection-backed sidebars, use the route schema directly:
202
+
203
+ ```ts
204
+ routes: [
205
+ {
206
+ path: '/',
207
+ slug: 'notes',
208
+ name: 'Notes',
209
+ icon: 'phosphor:note-pencil',
210
+ default: true,
211
+ collection: {
212
+ database: 'notes',
213
+ titleProperty: 'Title',
214
+ parentProperty: 'Parent note',
215
+ sidebar: {
216
+ mode: 'tree',
217
+ allowCreate: true,
218
+ },
219
+ },
220
+ },
221
+ ]
86
222
  ```
87
223
 
88
- The desktop lists a dev app only if its session is in the per-workspace registry it polls: `<workspace>/.context/app-dev-sessions.json`. A current CLI auto-resolves the nearest `.context` registry; on older CLIs (or when launched outside the workspace), pass `NOTIS_APP_DEV_SESSIONS_FILE="$CONDUCTOR_WORKSPACE_PATH/.context/app-dev-sessions.json"`. After starting, confirm your app's `devSlug` is in that file with a fresh `lastHeartbeatAt` — if it isn't, the session landed in the global `~/.notis` registry and the app won't appear in the sidebar.
224
+ Use the same page template for the root Notes route and collection/sub-collection detail states. The portal sidebar injects live collection items under the static route row when `collection.sidebar.mode === 'tree'`.
89
225
 
90
- 4. Build, capture listing screenshots, and verify (still local — this does not deploy):
226
+ ### Step 2: Build pages
91
227
 
92
- ```bash
93
- npx --package @notis_ai/cli@latest -- notis apps build
94
- npx --package @notis_ai/cli@latest -- notis apps screenshot
95
- npx --package @notis_ai/cli@latest -- notis apps verify
228
+ Standard React pages in `app/`. Use generic SDK tool hooks for data and build on top of the scaffolded shadcn components and portal shell classes (`notis-app-shell`, `notis-app-surface`):
229
+
230
+ ```tsx
231
+ 'use client';
232
+ import { useEffect, useState } from 'react';
233
+ import { useTool } from '@notis/sdk';
234
+ import { Card } from '@/components/ui/card';
235
+
236
+ type QueryTasksArgs = { database_id?: string; database_slug?: string; query: { page_size?: number } };
237
+ type TaskDoc = { document_id?: string; id?: string; title?: string; properties?: Record<string, unknown> };
238
+ type QueryTasksResult = { documents?: TaskDoc[] };
239
+
240
+ export default function TasksPage() {
241
+ const queryTasks = useTool<QueryTasksArgs, QueryTasksResult>('LOCAL_NOTIS_DATABASE_QUERY');
242
+ const [documents, setDocuments] = useState<TaskDoc[]>([]);
243
+
244
+ useEffect(() => {
245
+ void queryTasks
246
+ .call({ database_id: 'tasks-db-id', query: { page_size: 25 } })
247
+ .then((result) => setDocuments(result.documents || []));
248
+ }, [queryTasks.call]);
249
+
250
+ if (queryTasks.loading) return <div>Loading...</div>;
251
+
252
+ return (
253
+ <div className="p-6 space-y-4">
254
+ {documents.map((doc) => (
255
+ <Card key={doc.id || doc.document_id} className="p-4">
256
+ <h3>{doc.title || 'Untitled'}</h3>
257
+ <p className="text-muted-foreground">{String(doc.properties?.status || '')}</p>
258
+ </Card>
259
+ ))}
260
+ </div>
261
+ );
262
+ }
96
263
  ```
97
264
 
98
- Declare 3–6 screenshots in `notis.config.ts`, each with a `path`, descriptive
99
- `alt`, and optional `route`/`scenario`/`focus`/`theme`. Use `focus` to crop to a
100
- real app root instead of empty browser canvas, and use `theme: 'light'` or
101
- `theme: 'dark'` for a matching Portal render and Store frame. Paired light/dark
102
- entries may reuse the same route and scenario. `notis apps screenshot` renders
103
- those configured states in a headless harness and writes exact 2000x1250 PNGs
104
- under `metadata/`, using the deterministic Store presentation by default. Use
105
- `--raw` only for an unframed diagnostic capture. Apps are icon-led like Raycast:
106
- the app icon represents the app everywhere, so there is no cover image — only
107
- these screenshots. Re-run after changing routes or UI; it overwrites the set and
108
- drops stale files. Use `--routes home,history` to capture a subset.
265
+ ### Discovering database schema
109
266
 
110
- 5. **Only after the user has tested the local build and explicitly asks to deploy**, create or link the remote app, then deploy (this installs or updates the app on the user's account, where it appears under **Workspace** rather than Local development). After first install, keep `.notis/state.json` linked to that installed app id so future dev-session actions become **Update** instead of another **Install**:
267
+ Before writing app code, inspect the database schema to know what properties exist:
111
268
 
112
269
  ```bash
113
- npx --package @notis_ai/cli@latest -- notis apps create "My App" .
114
- npx --package @notis_ai/cli@latest -- notis apps deploy
270
+ npx --package @notis_ai/cli@latest -- notis tools search "list Notis databases"
271
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_LIST_DATABASES --arguments '{}'
272
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_GET_DATABASE --arguments '{"database_slug":"social_media_calendar"}'
273
+ npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_QUERY --arguments '{"database_id":"social-media-calendar-db-id","query":{"page_size":1}}'
115
274
  ```
116
275
 
117
- 6. **Only after the user explicitly confirms App Details is ready for Store review**, submit the deployed version:
276
+ Prefer the database `id` returned by `LOCAL_NOTIS_DATABASE_LIST_DATABASES` or `LOCAL_NOTIS_DATABASE_GET_DATABASE` when calling `LOCAL_NOTIS_DATABASE_QUERY`; use `database_slug` only as a fallback.
118
277
 
119
- ```bash
120
- npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready
278
+ Use `LOCAL_NOTIS_DATABASE_GET_DATABASE` through `useTool` when an app needs schema detail at runtime. Keep database-specific result and property helper types inside the app code.
279
+ For document writes, declare the generated canonical tool for the target database, such as `LOCAL_NOTIS_DATABASE_UPSERT_TASKS`, and call it through `useTool`. Pass flat property values; the server wraps them:
280
+
281
+ ```tsx
282
+ const upsertTask = useTool<Record<string, unknown>, { document?: { id: string } }>('LOCAL_NOTIS_DATABASE_UPSERT_TASKS');
283
+
284
+ await upsertTask.call({
285
+ title: 'My Task',
286
+ Status: 'Todo',
287
+ Priority: 'P1',
288
+ Due: '2025-04-01',
289
+ Done: false,
290
+ Count: 5,
291
+ });
121
292
  ```
122
293
 
123
- or:
294
+ Do NOT pass Notion-style wrappers (`{select: {name: "Todo"}}`) when upserting.
124
295
 
125
- ```bash
126
- npx --package @notis_ai/cli@latest -- notis apps link <app-id>
127
- npx --package @notis_ai/cli@latest -- notis apps deploy
296
+ ### Design rules
297
+
298
+ - Start from the scaffolded `@/components/ui/*` components before writing new UI primitives.
299
+ - Use restrained portal surfaces: `bg-background`, `bg-card`, `border-border`, `text-foreground`, `text-muted-foreground`.
300
+ - Keep layouts compact and dashboard-like. Prefer cards, sections, badges, and tables over marketing-style hero treatments.
301
+ - Respect the portal theme. Do not hardcode dark mode or create an app-specific palette.
302
+ - If a screen looks like a standalone microsite instead of a portal tool, it is too custom.
303
+ - For Notes-style apps, the folder tree belongs to the portal sidebar when configured via `collection.sidebar`. The page content should complement that chrome, not duplicate or replace it.
304
+ - Never indicate selected items with a heavy left-border bar (e.g. `border-l-2 border-l-foreground` paired with a muted background). It looks dated and clashes with the portal chrome. Use a single subtle background change (`bg-muted` for selected, `hover:bg-muted/50` for hover) and let typography or an icon carry the rest of the state.
305
+ - Do not render any search input inside the app (in-page search rails, "Ask Notis…" pills, command-palette-style bars, etc.). The portal already owns the top-bar search field. Wire your view to it with `useTopBarSearch({ value, onChange, placeholder, onSubmit })` from `@notis/sdk` and let the page filter or refetch on the values it receives. The hook also exposes `setLoading` so the standard top-bar spinner reflects in-flight queries.
306
+
307
+ ### Sidebar invariants
308
+
309
+ - When a user asks for folders, sections, or hierarchy in the app sidebar, express that through `routes` and `collection.sidebar` in `notis.config.ts`.
310
+ - Treat an existing collection-tree sidebar as a locked structural requirement unless the user explicitly asks to change navigation architecture.
311
+ - If the sidebar appears missing in the Local development sidebar group or deployed portal build, do not silently redesign around it. Preserve the manifest contract, call out the discrepancy, and treat it as a portal/runtime bug.
312
+
313
+ ### Step 3: Root layout
314
+
315
+ ```tsx
316
+ import { NotisProvider } from '@notis/sdk';
317
+ import '@notis/sdk/styles.css';
318
+ import './globals.css';
319
+
320
+ export default function AppShell({ children }: { children: React.ReactNode }) {
321
+ return <NotisProvider>{children}</NotisProvider>;
322
+ }
128
323
  ```
129
324
 
130
- ## App Structure
325
+ ## Manifest Format
131
326
 
132
- Expected project shape:
327
+ Generated by `npx --package @notis_ai/cli@latest -- notis apps build` at `.notis/output/manifest.json`:
133
328
 
134
- ```text
135
- notis.config.ts
136
- CHANGELOG.md # complete Store release history, newest entry first
137
- vite.config.ts
138
- app/
139
- components/
140
- metadata/ # screenshot-N.png only — generated by `notis apps screenshot`
141
- .notis/output/
329
+ ```json
330
+ {
331
+ "version": 1,
332
+ "spec_version": 4,
333
+ "app": { "name": "My App", "description": "...", "icon": "phosphor:..." },
334
+ "routes": [
335
+ {
336
+ "path": "/",
337
+ "slug": "index",
338
+ "name": "Dashboard",
339
+ "icon": "phosphor:squares-four",
340
+ "default": true,
341
+ "export_name": "index",
342
+ "collection": null
343
+ }
344
+ ],
345
+ "bundle": {
346
+ "js": "bundle/app.js",
347
+ "css": "bundle/app.css"
348
+ },
349
+ "databases": ["tasks", { "slug": "templates", "seed_documents": true }],
350
+ "tools": ["LOCAL_NOTIS_DATABASE_QUERY"]
351
+ }
142
352
  ```
143
353
 
144
- `metadata/` holds 3–6 listing screenshots (`screenshot-1.png`, `screenshot-2.png`,
145
- …). Each must be an exact 2000x1250 PNG no larger than 2 MB, with descriptive
146
- alt text declared in `notis.config.ts`. There is no `cover.png`: apps are icon-led like Raycast and the icon set in
147
- `notis.config.ts` represents the app in the Store. Generate screenshots with
148
- `notis apps screenshot` rather than authoring PNGs by hand.
354
+ Use canonical `notis-*` tool names for explicit app tool declarations. App-specific TypeScript shapes for tool arguments and results live in the app code; the SDK exposes the generic `useTool<TArgs, TResult>()` hook instead of database-specific tool hooks.
355
+
356
+ Database strings package schema only. The object form shown above opts that database into copying its current rows as Store starter content. Use it sparingly and only for non-personal fixtures/templates every installer is meant to receive.
357
+
358
+ For a read-only database catalog app, declare `["LOCAL_NOTIS_DATABASE_LIST_DATABASES", "LOCAL_NOTIS_DATABASE_GET_DATABASE"]`. Use the list tool for the left/catalog pane and the get tool for the selected database detail pane.
359
+
360
+ ## Database Schema
361
+
362
+ ### apps table
363
+
364
+ | Column | Type | Description |
365
+ |---|---|---|
366
+ | id | uuid PK | App ID |
367
+ | user_id | uuid FK | Owner |
368
+ | team_id | uuid FK | Team (nullable) |
369
+ | name | text | Display name |
370
+ | slug | text UNIQUE | URL slug |
371
+ | description | text | App description |
372
+ | icon | text | Phosphor icon (e.g. "phosphor:list") |
373
+ | status | text | draft, active, archived |
374
+ | visibility | text | private, team |
375
+ | manifest | jsonb | Latest deployed manifest |
376
+ | current_version | integer | Version counter |
377
+ | source_listing_id | uuid FK | Source App Store listing for installed store apps; cleared when submitted as a derivative |
378
+ | installed_snapshot | jsonb | Store-installed baseline used for update/reset comparison |
379
+ | customization_overlay | jsonb | User changes over the installed store baseline |
380
+ | update_status | text | up_to_date, update_available, needs_resolution, update_failed |
381
+ | bundled_automation_ids | uuid[] | Linked automations |
382
+ | bundled_skill_ids | uuid[] | Linked skills |
383
+
384
+ ### databases ownership
385
+
386
+ Every row in the `databases` table carries `owner_app_id` (uuid FK to
387
+ `apps.id`, `ON DELETE CASCADE`): a database belongs to exactly one app, and
388
+ deleting the app deletes its databases and their documents (`documents` cascade
389
+ from `databases`). Install, dev materialization, and store updates stamp
390
+ `owner_app_id` automatically; standalone creation requires the `app` argument.
391
+
392
+ ### Storage (Supabase)
393
+
394
+ Files stored in `app-code` bucket at `{app_id}/v{version}/`:
395
+ - `manifest.json`
396
+ - `bundle/app.js`
397
+ - `bundle/app.css`
398
+
399
+ Editable source snapshots are stored in the private `app-source` bucket at
400
+ `{app_id}/v{version}/`. Portal App Store listing screenshots are uploaded to
401
+ the public `app-listing-assets` bucket before submission.
402
+
403
+ ### Related tables
404
+
405
+ - **databases** -- Apps reference these rows by slug. Schema lives on the database row (`schema_metadata` / `original_fields`), not in the app manifest.
406
+ - **documents** -- `database_id` links to databases. Properties in `properties` jsonb.
407
+ - **app_store_listings** -- Snapshots for publishing to the app store.
408
+ - **app_submissions** -- Portal review submissions keyed to an app source version and registry slug.
409
+
410
+ ## Server Endpoints
411
+
412
+ | Endpoint | Method | Purpose |
413
+ |---|---|---|
414
+ | `/portal_views/get` | GET | Route detail + runtime descriptor with signed bundle URLs |
415
+ | `/portal_views/runtime_query` | POST | Proxy tool calls and DB operations |
416
+ | `/portal_views/collection_items` | GET | List collection items |
417
+ | `/portal_views/collection_tree` | GET | List normalized collection tree nodes for a tree sidebar route |
418
+ | `/portal_views/collection_tree/create` | POST | Create a root or child collection row from the sidebar |
419
+ | `/portal_views/collection_tree/rename` | POST | Rename a collection tree item inline |
420
+ | `/portal_views/collection_tree/delete` | POST | Delete a collection tree item from the sidebar |
421
+ | `/portal_apps/list` | GET | List apps |
422
+ | `/portal_apps/get` | GET | Get app detail |
423
+ | `/portal_apps/publish` | POST | Submit a deployed app source snapshot for public store review |
424
+ | `/portal_apps/listing_assets/upload` | POST | Legacy pre-manifest screenshot upload; do not use for current manifest-media workflows |
425
+ | `/portal_apps/submissions` | GET/PATCH | List or edit App Store submissions |
426
+ | `/portal_apps/submissions/withdraw` | POST | Close a pending App Store submission |
427
+ | `/cli_tools` | POST | CLI tool execution (save_app_files, create_app, etc.) |
428
+
429
+ ## SDK Hook Reference
430
+
431
+ All hooks are imported from `@notis/sdk`:
432
+
433
+ | Hook | Signature | Description |
434
+ |------|-----------|-------------|
435
+ | `useNotis()` | `() => { app, route, context, ready }` | App metadata, current route, generic portal context, ready state |
436
+ | `useTool<TArgs, TResult>(name)` | `(name: string) => { call, loading, error }` | Call a specific tool by name with app-defined argument and result types |
437
+ | `useTools()` | `() => { tools, loading }` | List available tools |
438
+ | `useNotisNavigation()` | `() => { toRoute, toDocument, toApp }` | Navigate between routes, documents, or the app root |
439
+ | `useTopBarSearch(opts)` | `({ value, onChange, placeholder?, onSubmit? }) => { setLoading }` | Bind the current view to the Portal-owned top-bar search input |
440
+ | `useBackend()` | `() => { request }` | Raw backend request proxy with JWT auth |
441
+
442
+ ### Typed tool calls
443
+
444
+ `useTool` accepts generic argument and result types. Query the database at dev time to discover actual property shapes, then keep those types in the app:
149
445
 
150
- `notis.config.ts` owns:
446
+ ```tsx
447
+ type QueryTasksArgs = { database_id?: string; database_slug?: string; query: { page_size?: number } };
448
+ interface TaskDoc {
449
+ title: string;
450
+ properties: {
451
+ Status: string;
452
+ Priority: string;
453
+ Due: string;
454
+ };
455
+ };
456
+ type QueryTasksResult = { documents: TaskDoc[] };
457
+
458
+ const queryTasks = useTool<QueryTasksArgs, QueryTasksResult>('LOCAL_NOTIS_DATABASE_QUERY');
459
+ const result = await queryTasks.call({ database_id: 'tasks-db-id', query: { page_size: 25 } });
460
+ // result.documents[0].properties.Status is typed as string
461
+ ```
151
462
 
152
- - app metadata: name, title, description, icon, accent, categories, tagline
153
- - listing screenshots: stable path, alt text, and optional capture route/scenario/focus/theme
154
- - database slug references
155
- - route definitions
156
- - tool access
463
+ ## Development Modes
157
464
 
158
- `CHANGELOG.md` owns Store release copy. Keep all releases in that single file:
465
+ ### Canonical local development
159
466
 
160
- ```markdown
161
- # My App Changelog
467
+ ```bash
468
+ npx --package @notis_ai/cli@latest -- notis apps dev
469
+ ```
162
470
 
163
- ## [A Better Workflow] - {PR_MERGE_DATE}
471
+ Runs the real desktop-local development workflow. The CLI should discover all apps in the target workspace, serve their bundles from loopback, and surface them in the Electron Portal's Local development sidebar group through the local desktop session registry.
164
472
 
165
- - Added a faster daily workflow.
473
+ ## Deploy Without Backend Server
166
474
 
167
- ## [Initial Release] - 2026-07-01
475
+ If the backend server at `localhost:3001` is not running, use `--direct`:
168
476
 
169
- - Published the first version.
477
+ ```bash
478
+ npx --package @notis_ai/cli@latest -- notis apps deploy --direct
170
479
  ```
171
480
 
172
- Store publication is blocked until the deployed manifest contains at least one
173
- valid entry. Ordinary local development and deployment still work when an app
174
- is not being prepared for the Store.
481
+ This uploads the bundle and editable source snapshot directly to Supabase storage and updates the app manifest in the database, bypassing the backend server entirely. The CLI auto-falls back to direct mode on network errors.
175
482
 
176
- **Icon & accent.** Set `icon` to a `phosphor:<name>` value (e.g. `phosphor:dice-five`) or `metadata/icon.png`. When `icon` is unset, the app shows its **two-letter initials** everywhere (store, sidebar, app details). `accent` optionally pins the avatar color to one of `blue | violet | emerald | amber | rose | sky | fuchsia | teal`; when unset a stable color is derived automatically from the app id. Both flow from `notis.config.ts` through deploy onto the app row and listing, and can also be changed later with the `update_app` tool (`{ "icon": "phosphor:...", "accent": "emerald" }`).
483
+ ## Testing
177
484
 
178
- Example:
485
+ 1. **Build validation**: `npx --package @notis_ai/cli@latest -- notis apps build` must succeed without errors. Vite surfaces TypeScript and bundling errors during this step.
486
+ 2. **Headless render verification** (recommended after every build): run `npx --package @notis_ai/cli@latest -- notis apps verify`. It builds unless `--skip-build` is passed, spins up a loopback harness, drives `agent-browser` against every route, and reports per-route pass/fail with captured render errors and runtime calls.
487
+ 3. **Local development acceptance**: Require the running CLI to report `Mounted in Notis`, which proves the exact nonce-backed session entered the current Electron Portal's final `Local development` sidebar model. Registry presence and bundle HTTP health alone prove only `Serving locally`. Opening the default route, asserting shadow-root content, and exercising hot reload remain the right checks when the task includes UI, runtime behavior, or visual acceptance, but they are not required merely to prove mounting.
488
+ 4. **Post-deploy**: Verify the deployed bundle via `/portal_views/get` -> `runtime_descriptor.bundle.js_url`, then verify the app renders in the portal. The portal renders app bundles directly as React components, so the fastest verification is navigating to the app page in the portal.
179
489
 
180
- ```ts
181
- import { defineNotisApp } from '@notis/sdk/config';
182
-
183
- export default defineNotisApp({
184
- name: 'my-app',
185
- title: 'My App',
186
- description: 'A focused Notis app.',
187
- icon: 'phosphor:squares-four',
188
- accent: 'blue',
189
- databases: ['tasks'],
190
- routes: [
191
- {
192
- path: '/',
193
- slug: 'tasks',
194
- name: 'Tasks',
195
- icon: 'phosphor:check-square',
196
- default: true,
197
- },
198
- ],
199
- tools: ['LOCAL_NOTIS_DATABASE_QUERY', 'LOCAL_NOTIS_DATABASE_UPSERT_TASKS'],
200
- });
201
- ```
490
+ ### Headless harness verification
202
491
 
203
- ## Runtime Usage
492
+ Run `npx --package @notis_ai/cli@latest -- notis apps verify` after `npx --package @notis_ai/cli@latest -- notis apps build`. Use `--mode live` after deploy to exercise the real `/portal_views/runtime_query` with the CLI JWT instead of stub data. If `agent-browser` is unavailable, pass `--no-browser` to print URLs and use `--keep-open` for interactive triage with `notis-browser-control`.
204
493
 
205
- Use SDK hooks for data and tool access:
494
+ #### What the harness catches that `npx --package @notis_ai/cli@latest -- notis apps build` does not
206
495
 
207
- ```tsx
208
- import { useTool } from '@notis/sdk';
496
+ - Hooks that mount but throw on first read (`useTool` called with the wrong tool name or argument shape, accessing nested props that are undefined).
497
+ - Runtime database queries whose slug is not declared by the app, and collection routes that never query their configured collection database. Declared databases may also support automations or agent workflows, so ordinary routes do not need to query every app database.
498
+ - Tool names referenced by hooks but missing from `notis.config.ts -> tools`.
499
+ - Suspense / async boundaries that never resolve because a runtime stub returned the wrong shape.
500
+ - Render-time exceptions that the portal would surface as the `View crashed` error boundary.
209
501
 
210
- const query = useTool('LOCAL_NOTIS_DATABASE_QUERY');
211
- ```
502
+ #### What the harness does not catch
212
503
 
213
- Do not fetch Notis backend endpoints directly from app code unless the SDK explicitly requires it. The Portal provides authentication, runtime context, and route rendering.
504
+ - Bugs that only manifest with real backend data (auth-scoped filters, RLS, malformed prod records). For those, swap the stub runtime for a real one that posts to `/portal_views/runtime_query` with a JWT.
505
+ - Visual regressions (use `agent-browser screenshot` + a baseline compare if you need this).
506
+ - Bugs that depend on the portal's shadow-DOM stylesheet wrapping. The harness mounts in light DOM, so global Tailwind/shadcn classes work normally; portal-specific theme tokens injected as inline styles are not present.
214
507
 
215
508
  ## Troubleshooting
216
509
 
217
- - If deploy fails with backend connectivity errors, run `npx --package @notis_ai/cli@latest -- notis apps doctor` and check the configured API base.
218
- - If editing an existing app, use `npx --package @notis_ai/cli@latest -- notis apps pull <app-id>` when source snapshots are available.
219
- - If the Portal shows a stale bundle, hard refresh the Portal or deploy a new version.
220
- - If an app route fails to load, run `npx --package @notis_ai/cli@latest -- notis apps verify` and inspect `.notis/output/manifest.json`.
510
+ ### Common issues
511
+
512
+ - **Deploy fails with network error**: Backend server not running. Use `npx --package @notis_ai/cli@latest -- notis apps deploy --direct` or start the server.
513
+ - **App shows old code after deploy**: Bundle cache is stale. Hard refresh (Cmd+Shift+R) or clear site data in DevTools.
514
+ - **App is missing from Local development**: First confirm the app lives in the directory owned by the long-running supervisor (`apps/<slug>/` in the Notis monorepo), the current worktree's Electron app is running, and `npx --package @notis_ai/cli@latest -- notis apps dev` is bound to loopback. Then check the **dev-session registry**: `apps dev` records its session in a JSON registry the desktop polls, and the desktop reads a **per-workspace** file at `<workspace>/.context/app-dev-sessions.json` (it sets `NOTIS_APP_DEV_SESSIONS_FILE` on its own runs). An agent-launched `apps dev` that does not resolve that file writes to the global `~/.notis/app-dev-sessions.json` instead — the bundle serves fine but the app never shows in that workspace's sidebar. Verify your session is in the workspace file:
515
+ ```bash
516
+ cat "$CONDUCTOR_WORKSPACE_PATH/.context/app-dev-sessions.json" # your devSlug must be present with a fresh lastHeartbeatAt
517
+ ```
518
+ If it is missing, restart `apps dev` pointed at that file:
519
+ ```bash
520
+ NOTIS_APP_DEV_SESSIONS_FILE="$CONDUCTOR_WORKSPACE_PATH/.context/app-dev-sessions.json" \
521
+ npx --package @notis_ai/cli@latest -- notis apps dev --no-open
522
+ ```
523
+ Current CLI auto-resolves the nearest `.context` registry, so the explicit env is only a fallback for older CLIs. Startup and heartbeat preserve other sessions, so this coexists with the desktop's own dev sessions. If the registry row has a `targetAppId`, that id must resolve to an installed app whose manifest is not `is_dev: true`; never repair this by linking to another development-runtime id. Restart the current CLI and let it clear stale hidden/deleted targets automatically. Then wait for `Mounted in Notis`; Electron writes the matching nonce acknowledgment beside the registry only after the app reaches the final Local development sidebar model. A corrected registry row or HTTP 200 is not mount proof by itself. Open and render the route separately only when validating UI or runtime behavior.
524
+ - **`LOCAL_NOTIS_DATABASE_QUERY` returns empty documents**: Check that the database ID passed to the tool matches the intended database. Use `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_LIST_DATABASES --arguments '{}'` to verify the ID; use the database slug only as a fallback.
525
+ - **Properties are `undefined`**: Keep app-local result types for `useTool<TArgs, TResult>` and guard optional nested properties when reading live data.