@notis_ai/cli 0.2.0-beta.156.1 → 0.2.0-beta.157.1
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/README.md +11 -45
- package/config/notis_app_design_rules.json +135 -0
- package/dist/agent-hooks/notis-agent-hook.mjs +5168 -7271
- package/dist/base-skills/notis-apps/SKILL.md +108 -194
- package/dist/base-skills/notis-cli/SKILL.md +64 -131
- package/package.json +1 -2
- package/skills/notis-apps/cli.md +34 -95
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +1 -1
- package/src/command-specs/apps.js +322 -1560
- package/src/runtime/agent-browser.js +169 -1
- package/src/runtime/app-boundary-validator.js +221 -0
- package/src/runtime/app-platform.js +359 -233
- package/src/runtime/app-test-server.js +292 -0
- package/template/app/page.tsx +45 -44
- package/template/components/page-heading.tsx +23 -0
- package/template/components/ui/badge.tsx +7 -4
- package/template/components/ui/card.tsx +24 -11
- package/template/components/ui/native-select.tsx +24 -0
- package/template/notis.config.ts +0 -1
- package/template/package.json +2 -2
- package/template/packages/sdk/package.json +1 -2
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +20 -7
- package/template/packages/sdk/src/config.ts +0 -2
- package/template/packages/sdk/src/interactions.ts +2 -1
- package/template/packages/sdk/src/styles.css +28 -1
- package/src/runtime/app-dev-build-supervisor.js +0 -47
- package/src/runtime/app-dev-build.js +0 -41
- package/src/runtime/app-dev-consumers.js +0 -154
- package/src/runtime/app-dev-host-lock.js +0 -80
- package/src/runtime/app-dev-process-identity.js +0 -111
- package/src/runtime/app-dev-roots.js +0 -284
- package/src/runtime/app-dev-server.js +0 -1136
- package/src/runtime/app-dev-sessions.js +0 -185
- package/src/runtime/cli-mode.generated.js +0 -5
- package/src/runtime/cli-mode.js +0 -34
|
@@ -20,55 +20,64 @@ All Notis apps are built using the Notis CLI, either locally in a repo workspace
|
|
|
20
20
|
- the app is packaged as an ES module bundle
|
|
21
21
|
- the portal renders it as a React component inside the portal's React tree
|
|
22
22
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
23
|
+
## Release-only delivery
|
|
24
|
+
|
|
25
|
+
Workspace runs released app versions only. Local and cloud agents use the same workflow.
|
|
26
|
+
A request to create or edit app source authorizes updating that app in Workspace after checks pass.
|
|
27
|
+
Explicit read-only, preview-only or no-deploy requests stop at local artifacts and checks: no remote
|
|
28
|
+
app/resource creation or mutation, Workspace preview, deployment or live verification. Store
|
|
29
|
+
publication always needs separate explicit approval.
|
|
30
|
+
|
|
31
|
+
1. Inspect the effective CLI profile and the exact app's current version with `apps list --json`.
|
|
32
|
+
For an existing released app, preserve local edits and pull its exact app ID into the intended
|
|
33
|
+
directory. Retain the profile/app link, deployment version and revision. An unreleased container
|
|
34
|
+
has no source to pull: recover its original local source and edits, or scaffold locally only if
|
|
35
|
+
that source cannot be recovered. Confirm its exact ID, edit permission and personal/team scope,
|
|
36
|
+
then run `apps link <app-id> <source-directory> --expected-version 0` to resume that same container.
|
|
37
|
+
If a release has appeared, preserve local source separately, pull the current release into a fresh
|
|
38
|
+
directory and reapply the intended edits. The link guard compares against the same remote read
|
|
39
|
+
whose version/revision it saves; deploy still rejects a release racing after that read. Do not pull
|
|
40
|
+
missing source or create another remote app to recover a failed first release.
|
|
41
|
+
2. Scaffold a new app locally, or edit the pulled source. Run `apps build` and automated
|
|
42
|
+
`apps verify` before new remote creation. Missing browser tooling or failed checks blocks delivery;
|
|
43
|
+
printed URLs and `--no-browser` are not passing verification. Install browser tooling with
|
|
44
|
+
`npm exec --yes --package agent-browser@latest -- agent-browser install`; if needed run
|
|
45
|
+
`npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify`.
|
|
46
|
+
3. Reconcile `apps list --json` and the exact intended name/slug, edit permission and personal/team
|
|
47
|
+
scope. Default to personal only when no team was requested. Reuse a matching editable identity;
|
|
48
|
+
stop on ambiguous matches or conflicting identity/scope. Create only when none exists, using
|
|
49
|
+
`apps create "<exact app name>" .` (or `--team-id <verified team ID>`). Read back the same ID.
|
|
50
|
+
A failed first release leaves a container: reuse it, never duplicate or automatically delete it.
|
|
51
|
+
4. Compare existing app-owned schemas. Create only necessary missing databases against that exact
|
|
52
|
+
app ID. Change existing schemas by verified database ID and ownership, and only with backward-
|
|
53
|
+
compatible changes before release. Read back each change. Breaking changes need separate coordination.
|
|
54
|
+
Ordinary note/record edits and existing resource editors remain immediate.
|
|
55
|
+
5. Run `apps deploy` against the same linked app. It builds, verifies a frozen source/artifact
|
|
56
|
+
snapshot with stubs, then sends that snapshot to the backend. `--skip-build` accepts only unchanged,
|
|
57
|
+
valid output and still verifies. Do not bypass the backend or create implicitly on deploy.
|
|
58
|
+
6. Read back the exact installed app ID, integer version and Portal URL with `apps list --json`.
|
|
59
|
+
Run `apps verify --mode live` and open the installed app in the actual Portal for surface proof.
|
|
60
|
+
A live harness check alone does not prove the deployed bundle rendered in Portal.
|
|
61
|
+
7. Report **failed before activation**, **deployed but unverified**, or **outcome unknown** accurately.
|
|
62
|
+
Never blindly replay an uncertain create/deploy response; reconcile its exact identity/version first.
|
|
63
|
+
`apps publish --confirm-ready` is **Publish to Store**, separately approved and listing-gated.
|
|
64
|
+
Workspace delivery is **Update app**, with no Store screenshot/readiness requirement.
|
|
65
|
+
|
|
66
|
+
### Restore historical source as a new release
|
|
67
|
+
|
|
68
|
+
Pull the current release into a fresh checkout first. Retrieve historical source into a different
|
|
69
|
+
folder (`apps pull <id> <historical-dir> --source-version <n>`). Replace source in the current checkout
|
|
70
|
+
without replacing its `.notis` profile/app link or deployment base. Update `package.json`'s
|
|
71
|
+
`notisAppVersion`, check compatibility with current resources, build, verify and deploy as a new
|
|
72
|
+
release. Preserve app/database/skill IDs. Never decrement the deployment counter, rewrite snapshots,
|
|
73
|
+
or claim to undo user data or external actions.
|
|
65
74
|
|
|
66
75
|
## Architecture
|
|
67
76
|
|
|
68
77
|
```
|
|
69
78
|
Notis CLI (local workspace or Vercel Sandbox)
|
|
70
79
|
-> Vite + React project with @notis/sdk
|
|
71
|
-
-> notis apps init /
|
|
80
|
+
-> notis apps init / build / verify / create / link / pull / deploy
|
|
72
81
|
-> ES module bundle (app.js + app.css) + manifest
|
|
73
82
|
-> Portal renders as React component with real tools/databases
|
|
74
83
|
```
|
|
@@ -84,7 +93,7 @@ Notis CLI (local workspace or Vercel Sandbox)
|
|
|
84
93
|
- `@notis/sdk/vite` -- `notisViteConfig()` for vite.config.ts
|
|
85
94
|
- `@notis/sdk/styles.css` -- shadow-safe app shell styles and base app-surface classes
|
|
86
95
|
|
|
87
|
-
2. **CLI** (`packages/cli/src/command-specs/apps.js`) --
|
|
96
|
+
2. **CLI** (`packages/cli/src/command-specs/apps.js`) -- release delivery uses init, build, verify, create, deploy, link, pull, doctor, and list
|
|
88
97
|
|
|
89
98
|
3. **Server** (`server/routers/portal_views/`) -- Returns signed bundle URLs, proxies tool calls
|
|
90
99
|
|
|
@@ -94,7 +103,6 @@ Notis CLI (local workspace or Vercel Sandbox)
|
|
|
94
103
|
|
|
95
104
|
Apps communicate with the platform through the `NotisRuntime` interface, provided by the portal via React context:
|
|
96
105
|
|
|
97
|
-
- **Portal development**: the portal loads a local bundle for an active dev session and still provides a real `NotisRuntime`.
|
|
98
106
|
- **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.
|
|
99
107
|
- 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.
|
|
100
108
|
|
|
@@ -110,7 +118,7 @@ App code never accesses the runtime directly -- it uses SDK hooks (`useTool`, `u
|
|
|
110
118
|
5. **Declarative tools** -- Tool access is declared in `notis.config.ts` by the final names returned by tool discovery and enforced server-side. Views can call native Notis, connected integrations, PostForMe, and MCP tools directly; metered calls use the same credit-cap and usage-billing path as the CLI.
|
|
111
119
|
6. **shadcn + Notis theme** -- Apps must use shadcn components with the live Notis theme provided by the portal
|
|
112
120
|
7. **Phosphor icons only** -- Always `phosphor:` prefix. Never emojis.
|
|
113
|
-
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,
|
|
121
|
+
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, installation stamps ownership automatically, and deleting an app deletes its databases and their documents.
|
|
114
122
|
An app-owned database slug is a stable deployed contract because bundles and
|
|
115
123
|
collection routes may call it directly. Do not try to rename that slug with
|
|
116
124
|
a schema tool; rename the display title instead.
|
|
@@ -120,12 +128,12 @@ App code never accesses the runtime directly -- it uses SDK hooks (`useTool`, `u
|
|
|
120
128
|
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.
|
|
121
129
|
13. **Portal globals are off-limits** -- Never use `window.__NOTIS_RUNTIME__`, query portal-owned DOM hooks, or create global DOM portals.
|
|
122
130
|
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.
|
|
123
|
-
15. **
|
|
124
|
-
16. **
|
|
125
|
-
17. **
|
|
126
|
-
18. **
|
|
131
|
+
15. **One delivery gate** -- Follow Release-only delivery on local and cloud computers. No DEV runtime exists.
|
|
132
|
+
16. **Exact identity** -- Preserve the intended profile, editable app ID, personal/team scope and current deployment base. Never silently advance a stale checkout.
|
|
133
|
+
17. **Automatic source updates** -- Build and check requested app source changes, then update Workspace unless the user opted out. Store publication stays separate.
|
|
134
|
+
18. **Runtime permissions stay least-authority** -- Release activation preserves existing grants and keeps restricted capabilities denied until approved.
|
|
127
135
|
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.
|
|
128
|
-
20. **
|
|
136
|
+
20. **Source restoration** -- Restore historical source as a new release using the current deployment base; never revert data or decrement versions.
|
|
129
137
|
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.
|
|
130
138
|
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.
|
|
131
139
|
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.
|
|
@@ -137,9 +145,9 @@ App code never accesses the runtime directly -- it uses SDK hooks (`useTool`, `u
|
|
|
137
145
|
These are the most common mistakes agents make. Each one wastes time and produces broken results.
|
|
138
146
|
|
|
139
147
|
- **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.
|
|
140
|
-
- **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
|
|
148
|
+
- **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 build`, `apps verify`, `apps create`, `apps link`, and `apps deploy`.
|
|
141
149
|
- **NEVER use `apps pull` to clone a Store listing** -- `npx --package @notis_ai/cli@latest -- notis apps pull` only pulls source for an app the user can already access as an installed app. To fork a published Store app, run `npx --package @notis_ai/cli@latest -- notis apps init "My App" --from <slug>` instead: it downloads that app's source from the public registry, and installing the app first is not required.
|
|
142
|
-
- **
|
|
150
|
+
- **One local/cloud delivery contract** -- App source create/edit requests authorize Workspace delivery after checks. Explicit read-only, preview-only and no-deploy requests prohibit remote mutations. Neither authorizes Store publication.
|
|
143
151
|
- **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.
|
|
144
152
|
- **NEVER write raw `views/<slug>/index.js` files** -- Write standard React pages in `app/`.
|
|
145
153
|
- **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.
|
|
@@ -147,108 +155,13 @@ These are the most common mistakes agents make. Each one wastes time and produce
|
|
|
147
155
|
- **NEVER explore server code or tool schemas to invent an alternative app workflow** -- Use the Notis CLI.
|
|
148
156
|
- **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.
|
|
149
157
|
- **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.
|
|
150
|
-
- **NEVER hand-roll buttons/cards/badges when the scaffold already provides
|
|
158
|
+
- **NEVER hand-roll buttons/cards/badges when the scaffold already provides flat primitives** -- Prefer `@/components/ui/*` and portal token classes such as `bg-background`, `bg-muted`, and `text-muted-foreground`. Never add `border` or `shadow` classes to `Card`; a `Card` nested in a `Card` is flat automatically. See Design bar.
|
|
151
159
|
|
|
152
160
|
## Workflow
|
|
153
161
|
|
|
154
|
-
**
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
2. **Pull your own apps; fork Store apps with `--from`.** `apps pull` is for apps the user already has installed or deployed: run `npx --package @notis_ai/cli@latest -- notis apps list`, preserve any local edits in the target directory, then run `npx --package @notis_ai/cli@latest -- notis apps pull <app-id>` (lands in `~/.notis/apps/<app-slug>`; pass a `[dir]` argument to place it elsewhere). A pull reproduces the installed release, so increment `package.json` `notisAppVersion` above that release before `apps dev`; until then the online bundle remains active. To fork a published Store app, use `apps init --from <slug>` instead -- it downloads the source from the registry and does not require installing the app first.
|
|
158
|
-
3. **Edit the listing source.** In `notis.config.ts`, set `name` to the stable lowercase kebab-case identity and set `title` to the correctly cased human-facing name; for example, `name: 'link-building'` with `title: 'Link Building'`. Treat acronym and brand casing as editorial input, not something to derive mechanically from the slug. Then update description, icon, accent, author, categories, tagline, databases, routes, and tools. 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.
|
|
159
|
-
4. **Build pages in `app/`.** Reuse scaffold code wherever it fits.
|
|
160
|
-
5. **Test the source before remote mutation.** Before changing a linked installed app, increment `package.json` `notisAppVersion` above the installed release. In a fresh hosted sandbox, bootstrap Agent Browser with `npm exec --yes --package agent-browser@latest -- agent-browser install`. Generate configured screenshots; use `theme: 'dark'` or `theme: 'light'` where appropriate and reserve screenshot `--raw` for diagnostics. Run `npm install`, run `npx --package @notis_ai/cli@latest -- notis apps build`, then run `npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify` in the sandbox (or the normal NPX verification command locally). Fix every failure. Do not create an app, mutate a database, or deploy before both checks pass. `--no-browser` is manual triage, not a passing automated gate.
|
|
161
|
-
6. **Finish the local Desktop path at the DEV handoff.** Run `npx --package @notis_ai/cli@latest -- notis apps dev [folder]`; it creates the development identity and materializes available scaffold database snapshots without requiring a hosted app id. Hand off after the user can see and test the app in its DEV-badged Workspace row. Do not deploy until the user asks, and then run `apps deploy` directly so the existing `dev_app_id` is promoted in place; never run `apps create` after `apps dev`. **Before handing off, complete all three acceptance checks:**
|
|
162
|
-
1. Root: `apps roots list` contains the intended folder (or the app is under the implicit default root).
|
|
163
|
-
2. Bundle: the loopback `/snapshot` responds successfully and contains the expected manifest/routes.
|
|
164
|
-
3. Mount and render: the app appears exactly once with a compact `DEV` badge and its default route renders. For multi-instance work, verify each requested Desktop independently.
|
|
165
|
-
See Troubleshooting → *App is missing from the sidebar* if any check fails.
|
|
166
|
-
7. **Gate and resolve one hosted identity after tests pass.** If the request is preview-only, read-only, or no-deploy, stop after step 5: do not create or link an app, mutate a database, deploy, or run post-deploy checks. Otherwise, existing edits keep the exact profile-scoped id linked by `apps pull`, after a metadata-only (`include_documents: false`) app-detail read validates its edit permission and scope without materializing databases. For a new hosted app, default the intended scope to personal unless the user explicitly requests team scope, inspect `.notis/state.json`, and run `apps list --json`. Consider every accessible exact canonical-slug row, including development rows. Link only one editable, non-development candidate whose metadata-only exact app-detail read proves the intended scope; fail on multiple matches, development-row collisions, missing scope proof, or scope mismatch. Create only when there are zero exact-slug rows. First prove that lowercasing the config `title`, replacing non-alphanumeric runs with `-`, and trimming hyphens yields the config `name`. For personal scope, run `npx --package @notis_ai/cli@latest -- notis apps create "<canonical-config-title>" . --json` exactly once and verify the returned id, remote slug, edit permission, and personal scope. For explicitly requested team scope, use `notis tools search` to discover the team-capable app-creation tool, inspect its schema, dry-run it, then execute `LOCAL_NOTIS_CREATE_APP` exactly once with the canonical display title, `visibility: "team"`, and the exact current `team_id` when resolved. Verify the result's id, canonical slug, `team_id`, team visibility, and edit permission, then run `notis apps link <returned-id> .` before database reconciliation. Never retry an outcome-unknown create; reconcile read-only and stop on ambiguity or any returned identity/scope mismatch.
|
|
167
|
-
8. **Reconcile hosted database schemas safely.** Read the exact app detail and current schemas first; mutate only missing or changed declarations. For creation, pass the exact app id in the database tool's `app` argument. For an update, resolve the exact `database_id`, verify its `owner_app_id` equals the linked app id, update by that `database_id`, then read back slug, owner, and schema. Apply only backward-compatible schema expansion before deployment. Stage breaking or destructive changes through an expand-contract sequence and obtain the required destructive approval; never make the currently deployed bundle incompatible before its replacement is live.
|
|
168
|
-
9. **Deploy and prove the hosted sandbox result.** Use only the exact id established in step 7. Run `npx --package @notis_ai/cli@latest -- notis apps deploy`, read the matching row back with `npx --package @notis_ai/cli@latest -- notis apps list --json`, confirm its id and deployed version, then run `npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify --mode live`. Return that row's exact profile-appropriate `portal_url` only after every proof passes. Report state precisely: a definite pre-commit rejection is **tested but not deployed**; a timeout/network/incomplete mutation response is **tested, deployment outcome unknown**; a confirmed deploy followed by failed readback is **deployed but not remotely verified**; a failed live check is **deployed but live verification failed**. Never retry an outcome-unknown mutation.
|
|
169
|
-
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.
|
|
170
|
-
|
|
171
|
-
### Quick start
|
|
172
|
-
|
|
173
|
-
Choose the local or hosted finish after verification. Local deployment is
|
|
174
|
-
user-gated; hosted-sandbox deployment is the default for app create or edit
|
|
175
|
-
tasks unless the user explicitly requests preview-only, read-only, or no deploy.
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
# 1. Pick a published Store app as the scaffold (catalog comes from the public registry)
|
|
179
|
-
# The project lands in ~/.notis/apps/<slug>; append a directory argument when
|
|
180
|
-
# the user wants the app in a repo they track.
|
|
181
|
-
npx --package @notis_ai/cli@latest -- notis apps scaffolds list
|
|
182
|
-
npx --package @notis_ai/cli@latest -- notis apps init "My App" --from <slug>
|
|
183
|
-
cd ~/.notis/apps/my-app
|
|
184
|
-
npm install
|
|
185
|
-
|
|
186
|
-
# 2. LOCAL COMPUTER: register with Desktop and iterate. After the user approves
|
|
187
|
-
# deployment, run deploy directly to promote the existing dev_app_id.
|
|
188
|
-
npx --package @notis_ai/cli@latest -- notis apps dev
|
|
189
|
-
# ... user tests the DEV-badged app ...
|
|
190
|
-
npx --package @notis_ai/cli@latest -- notis apps build
|
|
191
|
-
npx --package @notis_ai/cli@latest -- notis apps screenshot
|
|
192
|
-
npx --package @notis_ai/cli@latest -- notis apps verify
|
|
193
|
-
npx --package @notis_ai/cli@latest -- notis apps deploy
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Hosted sandbox finish — never run `apps dev`. Test first, then reconcile the
|
|
197
|
-
exact app identity and changed databases before deployment:
|
|
198
|
-
|
|
199
|
-
```bash
|
|
200
|
-
npm exec --yes --package agent-browser@latest -- agent-browser install
|
|
201
|
-
npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps screenshot
|
|
202
|
-
npx --package @notis_ai/cli@latest -- notis apps build
|
|
203
|
-
npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify
|
|
204
|
-
# New unlinked app only: reconcile exact canonical slug with apps list. If no
|
|
205
|
-
# match exists, create once with the config title and verify the returned slug.
|
|
206
|
-
npx --package @notis_ai/cli@latest -- notis apps list --json
|
|
207
|
-
# Before create, prove canonicalize(config.title) == config.name.
|
|
208
|
-
npx --package @notis_ai/cli@latest -- notis apps create "<canonical-config-title>" . --json
|
|
209
|
-
# Compare schemas, then create only missing databases or update changed ones by
|
|
210
|
-
# verified database_id and read back owner/schema before continuing.
|
|
211
|
-
npx --package @notis_ai/cli@latest -- notis apps deploy
|
|
212
|
-
npx --package @notis_ai/cli@latest -- notis apps list --json
|
|
213
|
-
npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify --mode live
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Store submission on either path remains a separate approval-gated action:
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
For an existing app, link the checkout first so every later command uses the
|
|
223
|
-
same profile-scoped installed identity:
|
|
224
|
-
|
|
225
|
-
```bash
|
|
226
|
-
npx --package @notis_ai/cli@latest -- notis apps link <app-id> .
|
|
227
|
-
npx --package @notis_ai/cli@latest -- notis apps deploy
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
Or if editing an installed app locally:
|
|
231
|
-
|
|
232
|
-
```bash
|
|
233
|
-
npx --package @notis_ai/cli@latest -- notis apps list
|
|
234
|
-
npx --package @notis_ai/cli@latest -- notis apps pull <installed-app-id>
|
|
235
|
-
cd ~/.notis/apps/my-app
|
|
236
|
-
npm install
|
|
237
|
-
# Increment package.json notisAppVersion above the pulled online release.
|
|
238
|
-
npx --package @notis_ai/cli@latest -- notis apps dev
|
|
239
|
-
npx --package @notis_ai/cli@latest -- notis apps build
|
|
240
|
-
npx --package @notis_ai/cli@latest -- notis apps verify
|
|
241
|
-
npx --package @notis_ai/cli@latest -- notis apps link <installed-app-id> .
|
|
242
|
-
# Only after the user explicitly asks to deploy:
|
|
243
|
-
npx --package @notis_ai/cli@latest -- notis apps deploy
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
In a hosted sandbox, use the same pull/build/verify sequence but omit `apps
|
|
247
|
-
dev`; bootstrap `agent-browser`, materialize any new or changed database schemas
|
|
248
|
-
against the exact linked id, deploy automatically after verification, read back
|
|
249
|
-
the exact app id/version and `portal_url` with `apps list --json`, run live verification, and return that
|
|
250
|
-
exact Portal URL. Never run `apps publish --confirm-ready` without separate
|
|
251
|
-
Store approval.
|
|
162
|
+
Follow **Release-only delivery** above. Use `apps scaffolds list` to discover public Store starting
|
|
163
|
+
points, `apps init` to scaffold locally, and `apps pull` for existing source. App file operations go
|
|
164
|
+
through the CLI, never raw storage/database writes. Run all Notis commands through NPX.
|
|
252
165
|
|
|
253
166
|
## Building an App
|
|
254
167
|
|
|
@@ -290,7 +203,7 @@ For arbitrary app-owned resources that are not Notis collection rows, set `resou
|
|
|
290
203
|
|
|
291
204
|
### Step 2: Build pages
|
|
292
205
|
|
|
293
|
-
Standard React pages in `app/`. Use generic SDK tool hooks for data and build on top of the scaffolded
|
|
206
|
+
Standard React pages in `app/`. Use generic SDK tool hooks for data and build on top of the scaffolded flat components and portal shell classes (`notis-app-shell` for ordinary pages, `notis-app-split` for list-plus-detail pages, `notis-app-surface` for a flat panel):
|
|
294
207
|
|
|
295
208
|
```tsx
|
|
296
209
|
import { useDocuments, ViewSkeleton } from '@notis/sdk';
|
|
@@ -361,22 +274,37 @@ await upsertTask.call({
|
|
|
361
274
|
|
|
362
275
|
Do NOT pass Notion-style wrappers (`{select: {name: "Todo"}}`) when upserting.
|
|
363
276
|
|
|
364
|
-
### Design
|
|
277
|
+
### Design bar (enforced)
|
|
278
|
+
|
|
279
|
+
Every page must read as a native, flat Notis page. `npx --package @notis_ai/cli@latest -- notis apps build` and the deploy endpoint fail on the banned patterns below with the exact file and line; the only override is an inline `// notis-design-allow: <rule-id> <reason>` comment on the line before (reason required, at least 12 characters). Do not work around a failure by moving the markup elsewhere; fix it.
|
|
280
|
+
|
|
281
|
+
Banned in `app/` and `components/` (form controls in `components/ui/{input,textarea,checkbox,switch,button}.tsx` are exempt):
|
|
282
|
+
|
|
283
|
+
- Four-side `border` boxes, `border-dashed`, `divide-*`, `<hr>`, thick `border-l-2` bars, `ring-*` as a box or selection indicator (`focus-visible:ring-2` on controls is fine).
|
|
284
|
+
- `shadow-*` on panels, tiles, rows, or bubbles. Only a floating popover or menu may use `shadow-lg` together with `bg-popover`.
|
|
285
|
+
- Tailwind palette hues (`emerald-500`, `slate-200`, ...), hex colors, gradients, `backdrop-blur`, `font-serif`.
|
|
286
|
+
- Uppercase `tracking-wide` eyebrows and marketing headlines. Page titles are plain nouns matching the route ("Dashboard", "Meetings").
|
|
287
|
+
- Text below 12px (`text-[11px]`); use `text-xs` at minimum and `text-sm` for body.
|
|
288
|
+
- `Badge variant="outline"`, raw `<select>`, in-app search inputs, duplicate sidebars, untouched scaffold placeholder copy.
|
|
289
|
+
- Loading text ("Loading...") or whole-page spinners. Keep headings visible and render `Skeleton` / `ViewSkeleton` from `@notis/sdk` only in the missing region (see the Instant-view contract).
|
|
290
|
+
|
|
291
|
+
Use instead:
|
|
365
292
|
|
|
366
|
-
-
|
|
367
|
-
-
|
|
368
|
-
-
|
|
369
|
-
-
|
|
370
|
-
-
|
|
293
|
+
- `Card` from the scaffold: a flat `bg-muted` panel that becomes `bg-background` when nested. Page sections can also be plain `h2` + content with `space-y-8`.
|
|
294
|
+
- `.list-row` / `.list-row-selected` from `@notis/sdk/styles.css` for rows and table bodies (tinted on mobile, transparent with hover tint on desktop, selection by tint). Tables are flat on the page: `text-xs` muted header, `text-sm` rows, no wrapping panel.
|
|
295
|
+
- Stats as bare figures: `text-xs` label over `text-2xl font-semibold tabular-nums`. Tiles (`rounded-2xl bg-muted p-5`) only when they are the page's single grouping device.
|
|
296
|
+
- `PageHeading` for the header, `NativeSelect` for filters, `Badge` variants `default | secondary | destructive`, tokens only (`text-foreground`, `text-muted-foreground`, `text-primary`, `bg-primary/10`, `text-destructive`, `bg-destructive/10`), `tabular-nums` on numbers, `min-w-0` on every grid item that can hold long text.
|
|
297
|
+
- One hairline (`border-t` / `border-b border-border`) between major sections or large list entries is the only allowed line.
|
|
298
|
+
- List-plus-detail pages are full-bleed: `notis-app-split` with `notis-app-pane-list` (tinted, one `border-r` hairline, fixed width on desktop, stacked on mobile) and `notis-app-pane-detail` (`bg-background`), never the centered `notis-app-shell`.
|
|
299
|
+
- Respect the portal theme in both modes. Never hardcode dark mode or an app palette.
|
|
371
300
|
- 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.
|
|
372
|
-
- 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.
|
|
373
301
|
- 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. Use its `setLoading` only for an explicit submitted search, never initial view loading or background refresh.
|
|
374
302
|
|
|
375
303
|
### Sidebar invariants
|
|
376
304
|
|
|
377
305
|
- When a user asks for folders, sections, or hierarchy in the app sidebar, express that through `routes` and `collection.sidebar` in `notis.config.ts`.
|
|
378
306
|
- Treat an existing collection-tree sidebar as a locked structural requirement unless the user explicitly asks to change navigation architecture.
|
|
379
|
-
- If the sidebar appears missing for the
|
|
307
|
+
- If the sidebar appears missing for the installed app, do not silently redesign around it. Preserve the manifest contract, call out the discrepancy, and treat it as a portal/runtime bug.
|
|
380
308
|
|
|
381
309
|
### Step 3: Root layout
|
|
382
310
|
|
|
@@ -454,7 +382,7 @@ For a read-only database catalog app, declare `["LOCAL_NOTIS_DATABASE_LIST_DATAB
|
|
|
454
382
|
Every row in the `databases` table carries `owner_app_id` (uuid FK to
|
|
455
383
|
`apps.id`, `ON DELETE CASCADE`): a database belongs to exactly one app, and
|
|
456
384
|
deleting the app deletes its databases and their documents (`documents` cascade
|
|
457
|
-
from `databases`). Install,
|
|
385
|
+
from `databases`). Install, resource preparation, and Store updates stamp
|
|
458
386
|
`owner_app_id` automatically; standalone creation requires the `app` argument.
|
|
459
387
|
|
|
460
388
|
### Storage (Supabase)
|
|
@@ -507,7 +435,7 @@ already installs `ShortcutProvider`; app code should not add a second provider.
|
|
|
507
435
|
| `useNotisNavigation()` | `() => { toRoute, toDocument, toApp }` | Navigate between routes (including `toRoute(path, { resourceId })`), documents, or the app root |
|
|
508
436
|
| `useTopBarSearch(opts)` | `({ value, onChange, placeholder?, onSubmit? }) => { setLoading }` | Bind the current view to the Portal-owned top-bar search input |
|
|
509
437
|
| `useBackend()` | `() => { request }` | Raw backend request proxy with JWT auth |
|
|
510
|
-
| `useDatabaseSubscription(slug, opts?)` | `(slug: string, opts?) => { rows, documents, loading, error, refetch, live }` | Query a database and refetch it when its rows change. `live` is false on hosts without a change feed (
|
|
438
|
+
| `useDatabaseSubscription(slug, opts?)` | `(slug: string, opts?) => { rows, documents, loading, error, refetch, live }` | Query a database and refetch it when its rows change. `live` is false on hosts without a change feed (temporary test harness, vite preview) -- keep a manual refresh for those |
|
|
511
439
|
| `useHandover()` | `() => { handover, pending, error, available }` | Open manager chat with app/resource context plus an optional starter prompt or declared skill. Omit `prompt` for a context-only composer. `available` is false on hosts with no chat -- fall back to a copyable prompt |
|
|
512
440
|
| `useCloudComputer()` | `() => { facts, loading, error, refresh }` | Read-only cloud computer facts: sandbox existence/status and whether the GitHub CLI is signed in. Requires `capabilities.cloudComputer: 'read'` plus the user's approval; `facts.available === false` means answer from the app's own fallback |
|
|
513
441
|
| `useActiveResource(resource)` | `(ContextResource \| null) => void` | Publish the record currently open in the app so manager handover and context menus stay grounded |
|
|
@@ -529,9 +457,6 @@ editor should accept media or file blocks.
|
|
|
529
457
|
|
|
530
458
|
### App configuration additions
|
|
531
459
|
|
|
532
|
-
- `devSlug` is the stable local-development identity. Set it when a display
|
|
533
|
-
rename must not create a second DEV app; otherwise the CLI derives it from
|
|
534
|
-
`name`.
|
|
535
460
|
- `toolBindings` is only for provider-generated public tool names whose upstream
|
|
536
461
|
action cannot be reconstructed. Keep the exact final public `name` in
|
|
537
462
|
`tools`, then bind it to `providerToolName`; the public name remains the
|
|
@@ -558,32 +483,23 @@ const result = await queryTasks.call({ database_id: 'tasks-db-id', query: { page
|
|
|
558
483
|
// result.documents[0].properties.Status is typed as string
|
|
559
484
|
```
|
|
560
485
|
|
|
561
|
-
##
|
|
562
|
-
|
|
563
|
-
### Hosted sandbox development
|
|
564
|
-
|
|
565
|
-
Do not run `apps dev` in a hosted sandbox. The sandbox filesystem is not on the
|
|
566
|
-
user's computer, so Desktop cannot mount it. Bootstrap sandbox `agent-browser`,
|
|
567
|
-
then build and verify before any remote mutation. For a create or edit request,
|
|
568
|
-
unless the user explicitly says preview-only, read-only, or no-deploy, resolve
|
|
569
|
-
one exact identity, safely materialize only missing or changed database schemas,
|
|
570
|
-
deploy, read back the exact app id/version, run live verification, and return
|
|
571
|
-
the exact Portal URL. Inspection, review, and diagnosis remain read-only.
|
|
572
|
-
|
|
573
|
-
### Canonical local development
|
|
574
|
-
|
|
575
|
-
```bash
|
|
576
|
-
npx --package @notis_ai/cli@latest -- notis apps dev
|
|
577
|
-
```
|
|
486
|
+
## Testing
|
|
578
487
|
|
|
579
|
-
|
|
488
|
+
Build and automated stub verification precede release. `verify` and `screenshot` start temporary,
|
|
489
|
+
explicit test servers only: no folder discovery, watchers, Desktop registration, persistent roots,
|
|
490
|
+
consumer leases or Workspace mounting. They close server/browser resources at completion or interruption.
|
|
491
|
+
Build also enforces the design bar and refreshes the app's embedded SDK copy. Automated verification
|
|
492
|
+
checks every route at desktop (1280px) and phone (390px) widths, including nested boxes, text below
|
|
493
|
+
12px, lingering loading placeholders and horizontal overflow. Standalone verification writes a
|
|
494
|
+
local diagnostic report; deploy always verifies its own frozen snapshot, with no stamp or environment bypass.
|
|
495
|
+
After release, verify live runtime integration and open the installed bundle in Portal. Source edits
|
|
496
|
+
and Desktop restarts cannot change the running version.
|
|
580
497
|
|
|
581
|
-
|
|
498
|
+
### Screenshots
|
|
582
499
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
4. **Post-deploy**: Read back the exact app id, version, and `portal_url` with `apps list --json`, run `apps verify --mode live`, verify the deployed bundle via `/portal_views/get` -> `runtime_descriptor.bundle.js_url`, and return that profile-appropriate exact Portal URL. A confirmed deploy followed by failed readback is deployed but not remotely verified; a failed live check is deployed but live verification failed. The portal renders app bundles directly as React components, so navigate to the app page when an authenticated browser is available.
|
|
500
|
+
`apps screenshot` supports declared screenshot scenarios and stub fixtures. A scenario can set
|
|
501
|
+
`theme: 'dark'`; use `--raw` for uncomposited captures. Store screenshots and listing readiness
|
|
502
|
+
are required only for Publish to Store, never for Update app.
|
|
587
503
|
|
|
588
504
|
### Headless harness verification
|
|
589
505
|
|
|
@@ -600,17 +516,15 @@ Run `npx --package @notis_ai/cli@latest -- notis apps verify` after `npx --packa
|
|
|
600
516
|
#### What the harness does not catch
|
|
601
517
|
|
|
602
518
|
- 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.
|
|
603
|
-
-
|
|
519
|
+
- Pixel-level visual regressions beyond the automated design checks (the harness does flag nested boxes, sub-12px text, lingering loading placeholders, and horizontal overflow at 390px). For anything else, use `agent-browser screenshot` + a baseline compare.
|
|
604
520
|
- 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.
|
|
605
521
|
|
|
606
522
|
## Troubleshooting
|
|
607
523
|
|
|
608
524
|
### Common issues
|
|
609
525
|
|
|
610
|
-
- **Deploy
|
|
611
|
-
authenticated API available. Do not bypass DEV-app promotion or installed-app
|
|
612
|
-
identity with a direct database/storage write.
|
|
526
|
+
- **Deploy transport failure**: Run `notis doctor` and read back the exact app/version. Never blindly retry an unknown outcome or write directly to storage.
|
|
613
527
|
- **App shows old code after deploy**: Bundle cache is stale. Hard refresh (Cmd+Shift+R) or clear site data in DevTools.
|
|
614
|
-
- **App is missing from
|
|
528
|
+
- **App is missing from Workspace**: Inspect its exact installed version. Unreleased containers have no runnable routes. A successful release appears through ordinary refresh/navigation.
|
|
615
529
|
- **`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.
|
|
616
530
|
- **Properties are `undefined`**: Keep app-local result types for `useTool<TArgs, TResult>` and guard optional nested properties when reading live data.
|