@notis_ai/cli 0.2.0-beta.158.1 → 0.2.0-beta.159.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/dist/base-skills/notis-apps/SKILL.md +42 -59
- package/dist/base-skills/notis-apps/references/architecture.md +18 -1
- package/dist/base-skills/notis-apps/references/design.md +45 -34
- package/dist/base-skills/notis-apps/references/release.md +99 -93
- package/dist/base-skills/notis-apps/references/sdk.md +2 -1
- package/dist/base-skills/notis-apps/references/troubleshooting.md +20 -23
- package/dist/base-skills/notis-cli/SKILL.md +2 -2
- package/package.json +1 -1
- package/template/packages/sdk/src/hooks/useDocuments.ts +4 -1
- package/template/packages/sdk/src/queryCache.ts +10 -2
|
@@ -7,62 +7,45 @@ mcp_tool_patterns: ["LOCAL_NOTIS_INSTALL_APP"]
|
|
|
7
7
|
mcp_references: ["references/release.md", "references/architecture.md", "references/design.md", "references/sdk.md", "references/troubleshooting.md"]
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
# Notis Apps
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
4. **
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
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.
|
|
53
|
-
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.
|
|
54
|
-
24. **New projects default to `~/.notis/apps/<slug>`, and `[dir]` overrides it** -- `apps init` and `apps pull` use this stable, predictable home unless the app belongs in a specific repository, monorepo, or user-chosen location. In those cases, pass `[dir]` and report the resulting path. Do not nest an app inside a directory whose local workspace metadata selects an unrelated Notis runtime or profile: later CLI calls inherit that routing and may target the wrong environment.
|
|
55
|
-
25. **Machine names and display titles use different casing** -- In `notis.config.ts`, `name` is the stable machine identity and must be lowercase kebab-case (`name: 'link-building'`). `title` is the human-facing app name and must use deliberate display casing (`title: 'Link Building'`), preserving product spelling and acronyms such as `Notis` and `SEO`. Never put a title-cased phrase in `name`, never show a raw slug as the title, and never change an existing canonical `name` or remote slug merely to repair display casing. The persisted `apps.name`, Workspace sidebar, App Details, and Store listing must use `title`.
|
|
56
|
-
|
|
57
|
-
## Task guides
|
|
58
|
-
|
|
59
|
-
Read only the guide needed for this task. Relative links resolve in the skill bundle.
|
|
60
|
-
For hosted MCP, fetch the matching `notis://docs/notis-apps/references/<file>.md` URI
|
|
61
|
-
with resources/read or the available Notis resource-fetch tool; the root resource
|
|
62
|
-
also rewrites these links to their published URIs.
|
|
63
|
-
|
|
64
|
-
- [Release-only delivery](references/release.md)
|
|
65
|
-
- [How Apps Are Built](references/architecture.md)
|
|
66
|
-
- [Building an App](references/design.md)
|
|
67
|
-
- [SDK Hook Reference](references/sdk.md)
|
|
68
|
-
- [Anti-patterns -- NEVER do these](references/troubleshooting.md)
|
|
10
|
+
# Notis Apps
|
|
11
|
+
|
|
12
|
+
Build apps that feel native to Notis: compact, readable, responsive, and useful.
|
|
13
|
+
Use Vite + React, `@notis/sdk`, and the existing scaffold components. Use the Notis
|
|
14
|
+
CLI for app operations: `npx --package @notis_ai/cli@latest -- notis ...`.
|
|
15
|
+
|
|
16
|
+
## Build → inspect → fix → deliver
|
|
17
|
+
|
|
18
|
+
For every app UI create/edit task, read both [Design](references/design.md) and
|
|
19
|
+
[Delivery](references/release.md). Then:
|
|
20
|
+
|
|
21
|
+
1. **Understand the result.** Identify the main user action and the requested
|
|
22
|
+
change. For a reported visual bug, describe what is wrong in the actual
|
|
23
|
+
screen before editing. Preserve what already works.
|
|
24
|
+
2. **Start from native patterns.** Pull the existing app or choose the closest
|
|
25
|
+
Store scaffold. Use the appropriate page layout instead of inventing a new
|
|
26
|
+
visual system. Make routine choices yourself; ask only for missing decisions
|
|
27
|
+
that materially change the result.
|
|
28
|
+
3. **Build, look, improve.** Run build and verification, then actually inspect
|
|
29
|
+
the rendered app. Follow the short visual check in the design guide. Fix what
|
|
30
|
+
is wrong and recheck the affected screen; a passing build is not visual approval.
|
|
31
|
+
4. **Deliver the checked result.** Follow the delivery guide and existing user
|
|
32
|
+
authorization. Confirm the released result inside Notis before calling it
|
|
33
|
+
verified. Say plainly what is local, deployed, or still unverified.
|
|
34
|
+
|
|
35
|
+
## Keep these boundaries
|
|
36
|
+
|
|
37
|
+
- User and repository instructions take precedence, including preview-only,
|
|
38
|
+
no-deploy, and explicit-consent requirements. Store publication is separate.
|
|
39
|
+
- Preserve the exact app identity, account/team scope, permissions, and user data.
|
|
40
|
+
Reconcile an uncertain release instead of blindly retrying it.
|
|
41
|
+
- Use SDK hooks and declared tools. Let Notis own its sidebar, search, runtime,
|
|
42
|
+
and rendering boundary; do not query host DOM or recreate that chrome in the app.
|
|
43
|
+
|
|
44
|
+
## References — only as needed
|
|
45
|
+
|
|
46
|
+
- [SDK hooks](references/sdk.md): reads, edits, selection, and navigation.
|
|
47
|
+
- [App contracts](references/architecture.md): configuration, packaging, and data ownership.
|
|
48
|
+
- [Troubleshooting](references/troubleshooting.md): a specific failure or mismatch.
|
|
49
|
+
|
|
50
|
+
The CLI distributes this skill and its references from the canonical product
|
|
51
|
+
source. Do not maintain competing copies.
|
|
@@ -6,6 +6,14 @@ All Notis apps are built using the Notis CLI, either locally in a repo workspace
|
|
|
6
6
|
- the app is packaged as an ES module bundle
|
|
7
7
|
- the portal renders it as a React component inside the portal's React tree
|
|
8
8
|
|
|
9
|
+
Use standard React pages in `app/`, not Next.js or a custom server. The host
|
|
10
|
+
chooses a trusted shadow root or an isolated Store frame; do not create your own
|
|
11
|
+
iframe, query Portal-owned DOM, or install a window-global runtime. The host owns
|
|
12
|
+
theme injection and authentication. App code uses SDK hooks and the final tool
|
|
13
|
+
names discovered through the CLI, declared in `notis.config.ts` and enforced by
|
|
14
|
+
the backend. Runtime permissions stay least-authority; releasing an app does not
|
|
15
|
+
grant new capabilities.
|
|
16
|
+
|
|
9
17
|
## Architecture
|
|
10
18
|
|
|
11
19
|
```
|
|
@@ -71,9 +79,15 @@ Generated by `npx --package @notis_ai/cli@latest -- notis apps build` at `.notis
|
|
|
71
79
|
}
|
|
72
80
|
```
|
|
73
81
|
|
|
74
|
-
Use
|
|
82
|
+
Use the exact final names returned by tool discovery for explicit app tool declarations. App-specific TypeScript shapes for tool arguments and results live in the app code; the SDK exposes generic tool hooks alongside its native database hooks.
|
|
83
|
+
|
|
84
|
+
Routes are canonical: define navigation in `routes`, give every route an explicit
|
|
85
|
+
`slug`, and do not rely on legacy `views`. A configured `collection.sidebar` tree
|
|
86
|
+
belongs to the host, not app JSX.
|
|
75
87
|
|
|
76
88
|
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.
|
|
89
|
+
Never seed user-created notes, history, leads, or other private records. In source
|
|
90
|
+
config the explicit opt-in is `{ slug: 'templates', seedDocuments: true }`.
|
|
77
91
|
|
|
78
92
|
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.
|
|
79
93
|
|
|
@@ -108,6 +122,9 @@ Every row in the `databases` table carries `owner_app_id` (uuid FK to
|
|
|
108
122
|
deleting the app deletes its databases and their documents (`documents` cascade
|
|
109
123
|
from `databases`). Install, resource preparation, and Store updates stamp
|
|
110
124
|
`owner_app_id` automatically; standalone creation requires the `app` argument.
|
|
125
|
+
The app config references existing database slugs; it does not own the schema.
|
|
126
|
+
The database row is the schema source of truth. Treat deployed slugs as stable
|
|
127
|
+
contracts: rename display titles rather than changing slugs behind live callers.
|
|
111
128
|
|
|
112
129
|
### Storage (Supabase)
|
|
113
130
|
|
|
@@ -1,13 +1,56 @@
|
|
|
1
1
|
## Building an App
|
|
2
2
|
|
|
3
|
+
### Design defaults
|
|
4
|
+
|
|
5
|
+
- Start from the closest Store scaffold (`apps scaffolds list`, then `apps init
|
|
6
|
+
--from <slug>`), or preserve the existing app's good patterns.
|
|
7
|
+
- Make the main task obvious. Use compact spacing, readable text, plain page
|
|
8
|
+
titles, Phosphor icons, and the scaffold's buttons, filters, rows, and cards.
|
|
9
|
+
- Match Notis in light and dark mode. Use theme tokens and restrained accents;
|
|
10
|
+
prefer flat surfaces and selection tints over decorative boxes and shadows.
|
|
11
|
+
- Choose the right layout: `notis-app-shell` for ordinary content;
|
|
12
|
+
`notis-app-split`, `notis-app-pane-list`, and `notis-app-pane-detail` for a
|
|
13
|
+
full-viewport list and reader. Keep reading text comfortably sized. Let mobile
|
|
14
|
+
stack or adapt the content rather than squeeze a desktop layout onto a phone.
|
|
15
|
+
- Let Notis own navigation, folder trees, and search. Use `PageHeading`,
|
|
16
|
+
`NativeSelect`, `.list-row`, and `useTopBarSearch` instead of duplicating chrome.
|
|
17
|
+
- Prefer inline optimistic edits for simple changes, with rollback on failure.
|
|
18
|
+
Use a dialog for multi-field edits or destructive confirmation.
|
|
19
|
+
|
|
20
|
+
Build enforces the existing design rules and reports violations by file/line.
|
|
21
|
+
Use those diagnostics to fix specific problems; passing them does not establish
|
|
22
|
+
that the design is good. See [troubleshooting](troubleshooting.md) when needed.
|
|
23
|
+
|
|
24
|
+
### Look at the result
|
|
25
|
+
|
|
26
|
+
Run build and verification, then inspect screenshots of the affected view at a
|
|
27
|
+
normal desktop width and a phone width. For new layouts or theme changes, check
|
|
28
|
+
both themes. Keep the review focused on the task, not a new report or approval cycle:
|
|
29
|
+
|
|
30
|
+
- Does the layout use the available viewport correctly, including while loading?
|
|
31
|
+
- Do sizing, spacing, text, and scrolling look right? Is anything clipped or overflowing?
|
|
32
|
+
- Do loading placeholders match the real content instead of changing the layout?
|
|
33
|
+
- Does the main interaction work, and do empty/error states explain what to do?
|
|
34
|
+
|
|
35
|
+
Use temporary fixtures to expose slow reads, empty results, and errors where
|
|
36
|
+
relevant; do not alter real user records for a screenshot. Actually inspect the
|
|
37
|
+
images, fix what is wrong, and recheck the affected state. After an authorized
|
|
38
|
+
release, repeat the affected-view check inside Notis as described in
|
|
39
|
+
[Delivery](release.md). A standalone harness is not proof of the host layout.
|
|
40
|
+
|
|
3
41
|
### Step 1: Define the config
|
|
4
42
|
|
|
43
|
+
Use `~/.notis/apps/<slug>` by default, or pass the user's intended directory to
|
|
44
|
+
`apps init` / `apps pull`. Avoid a parent workspace that selects an unrelated CLI
|
|
45
|
+
profile. Keep the exact installed identity when editing; do not rename a machine
|
|
46
|
+
slug just to correct its display title.
|
|
47
|
+
|
|
5
48
|
Create `notis.config.ts` with:
|
|
6
49
|
- **name** -- Stable machine identity in lowercase kebab-case, such as `link-building`; do not use display casing here
|
|
7
50
|
- **title** -- Human-facing app name with deliberate casing, such as `Link Building`; preserve brands and acronyms exactly
|
|
8
51
|
- **databases** -- Slug references to existing Notis databases
|
|
9
52
|
- **routes** -- Route-first sidebar entries with explicit `slug`, optional `parentSlug`, and optional `collection.sidebar` tree config
|
|
10
|
-
- **tools** -- Final tool names the app can call at runtime.
|
|
53
|
+
- **tools** -- Final tool names the app can call at runtime. Discover tools with `notis tools search "<what you need>"`, inspect their schemas with `notis tools describe <tool>`, and copy the returned final names into this list. Examples include `LOCAL_NOTIS_DATABASE_QUERY`, `LOCAL_NOTIS_MONID_RUN`, `GMAIL_SEND_EMAIL`, `LOCAL_POSTFORME_CREATE_POST`, and `LOCAL_MCP_<SERVER>_<TOOL>`. App code calls each declared name directly through `useTool`; it does not wrap provider or MCP calls in `COMPOSIO_MULTI_EXECUTE_TOOL`. Access stays scoped to the signed-in user's own connections, native database tools stay scoped to the app's databases unless `capabilities.workspaceDatabases: 'read'` is granted, and metered tools use the CLI-equivalent credit-cap and fail-closed usage-billing path.
|
|
11
54
|
|
|
12
55
|
For collection-backed sidebars, use the route schema directly:
|
|
13
56
|
|
|
@@ -64,7 +107,7 @@ Build a client-side, multi-route app with one persistent `app/layout.tsx` shell.
|
|
|
64
107
|
|
|
65
108
|
| State | Required UI |
|
|
66
109
|
| --- | --- |
|
|
67
|
-
| First read, no successful data | Keep headings/navigation/layout visible; use content-shaped skeletons only in missing regions. No page spinner or whole-page `Loading...`. |
|
|
110
|
+
| First read, no successful data | Keep headings/navigation/layout visible; use content-shaped skeletons only in missing regions, with the same pane bounds as the loaded view. No page spinner or whole-page `Loading...`. |
|
|
68
111
|
| Cached view / successful empty result | Render synchronously from the shared SDK cache. Empty results are real cached results. |
|
|
69
112
|
| Background refresh | Keep current content and selection. Never replace populated content with a skeleton; do not drive the top-bar spinner from mount/refetch state. |
|
|
70
113
|
| Explicit Save / Upload / submitted search | Progress belongs in that button or affected section. Disable only the conflicting action. |
|
|
@@ -109,38 +152,6 @@ await upsertTask.call({
|
|
|
109
152
|
|
|
110
153
|
Do NOT pass Notion-style wrappers (`{select: {name: "Todo"}}`) when upserting.
|
|
111
154
|
|
|
112
|
-
### Design bar (enforced)
|
|
113
|
-
|
|
114
|
-
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.
|
|
115
|
-
|
|
116
|
-
Banned in `app/` and `components/` (form controls in `components/ui/{input,textarea,checkbox,switch,button}.tsx` are exempt):
|
|
117
|
-
|
|
118
|
-
- 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).
|
|
119
|
-
- `shadow-*` on panels, tiles, rows, or bubbles. Only a floating popover or menu may use `shadow-lg` together with `bg-popover`.
|
|
120
|
-
- Tailwind palette hues (`emerald-500`, `slate-200`, ...), hex colors, gradients, `backdrop-blur`, `font-serif`.
|
|
121
|
-
- Uppercase `tracking-wide` eyebrows and marketing headlines. Page titles are plain nouns matching the route ("Dashboard", "Meetings").
|
|
122
|
-
- Text below 12px (`text-[11px]`); use `text-xs` at minimum and `text-sm` for body.
|
|
123
|
-
- `Badge variant="outline"`, raw `<select>`, in-app search inputs, duplicate sidebars, untouched scaffold placeholder copy.
|
|
124
|
-
- 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).
|
|
125
|
-
|
|
126
|
-
Use instead:
|
|
127
|
-
|
|
128
|
-
- `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`.
|
|
129
|
-
- `.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.
|
|
130
|
-
- 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.
|
|
131
|
-
- `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.
|
|
132
|
-
- One hairline (`border-t` / `border-b border-border`) between major sections or large list entries is the only allowed line.
|
|
133
|
-
- 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`.
|
|
134
|
-
- Respect the portal theme in both modes. Never hardcode dark mode or an app palette.
|
|
135
|
-
- 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.
|
|
136
|
-
- 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.
|
|
137
|
-
|
|
138
|
-
### Sidebar invariants
|
|
139
|
-
|
|
140
|
-
- When a user asks for folders, sections, or hierarchy in the app sidebar, express that through `routes` and `collection.sidebar` in `notis.config.ts`.
|
|
141
|
-
- Treat an existing collection-tree sidebar as a locked structural requirement unless the user explicitly asks to change navigation architecture.
|
|
142
|
-
- 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.
|
|
143
|
-
|
|
144
155
|
### Step 3: Root layout
|
|
145
156
|
|
|
146
157
|
```tsx
|
|
@@ -1,93 +1,99 @@
|
|
|
1
|
-
##
|
|
2
|
-
|
|
3
|
-
Workspace runs released
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
`
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
`apps
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
valid output and still verifies.
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
###
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
## Delivery
|
|
2
|
+
|
|
3
|
+
Workspace runs released versions only. Local and cloud agents use the same
|
|
4
|
+
workflow. Run Notis commands through
|
|
5
|
+
`npx --package @notis_ai/cli@latest -- notis ...`.
|
|
6
|
+
|
|
7
|
+
A create/edit request normally authorizes updating that app after checks pass,
|
|
8
|
+
**unless user or repository policy requires explicit deployment consent**.
|
|
9
|
+
Preserve authorization already given. Read-only, preview-only, and no-deploy
|
|
10
|
+
instructions stop at local source, build, and stub verification: no remote
|
|
11
|
+
resource mutation, app activation, or live verification. Store publication always
|
|
12
|
+
needs separate explicit approval. Do not deploy just to obtain visual proof when
|
|
13
|
+
deployment is not authorized; report that the host check remains unverified.
|
|
14
|
+
|
|
15
|
+
## Update an app
|
|
16
|
+
|
|
17
|
+
1. **Check identity.** Inspect the effective CLI profile and `apps list --json`.
|
|
18
|
+
Preserve local edits, then pull the exact editable app ID and intended
|
|
19
|
+
personal/team scope. Keep its profile-scoped link, current version, and revision.
|
|
20
|
+
Never silently advance a stale checkout or create a duplicate to avoid a conflict.
|
|
21
|
+
2. **Build and inspect.** Edit the source, increment `notisAppVersion`, and update
|
|
22
|
+
`CHANGELOG.md`. Run `apps build` and automated `apps verify`, then do the
|
|
23
|
+
[visual check](design.md#look-at-the-result). For a new app, complete these
|
|
24
|
+
local checks before creating remote resources.
|
|
25
|
+
3. **Prepare only missing resources.** For an existing app, retain its identity.
|
|
26
|
+
For a new app, reconcile the exact canonical name, edit permission, and scope
|
|
27
|
+
against `apps list --json`; reuse one matching editable identity, stop on
|
|
28
|
+
ambiguity, or create only when none exists. Use `apps create "<display title>"
|
|
29
|
+
<dir>` (with a verified `--team-id` for team scope), and read back the same ID.
|
|
30
|
+
Create only necessary missing databases against that app. Verify ownership
|
|
31
|
+
and database IDs before changing schemas; read back changes. Breaking changes
|
|
32
|
+
require separate coordination. Never mutate user data merely to test the UI.
|
|
33
|
+
4. **Update Workspace.** Run `apps deploy` against that linked app. It builds and
|
|
34
|
+
verifies a frozen source/artifact snapshot before activation. `--skip-build`
|
|
35
|
+
accepts only unchanged valid output and still verifies. Use the supported
|
|
36
|
+
backend path; do not bypass checks or write directly to storage.
|
|
37
|
+
5. **Verify delivery.** Read back the app ID, integer version, and Portal URL with
|
|
38
|
+
`apps list --json`. Run `apps verify --mode live`, then open the released app
|
|
39
|
+
inside Notis and inspect the affected screen and main interaction. Confirm the
|
|
40
|
+
intended bundle/version, not just the existence of an app with the same name.
|
|
41
|
+
A successful live harness check alone is not visual proof inside Notis.
|
|
42
|
+
6. **Report accurately.** Give the app link and a brief description of what changed
|
|
43
|
+
and what was verified. Distinguish local-only, deployed and verified, deployed
|
|
44
|
+
but unverified, failed before activation, and outcome unknown. If create/deploy
|
|
45
|
+
has an uncertain outcome, reconcile its exact identity/version before retrying.
|
|
46
|
+
|
|
47
|
+
## What the checks prove
|
|
48
|
+
|
|
49
|
+
`build` validates the package, enforces design rules, and refreshes its embedded
|
|
50
|
+
SDK. Automated `verify` checks every route at desktop (1280px) and phone (390px)
|
|
51
|
+
widths, render errors, runtime calls, nested boxes, small text, lingering loading
|
|
52
|
+
placeholders, and horizontal overflow. It uses a temporary server and browser;
|
|
53
|
+
printed URLs or `--no-browser` are not passing verification. If tooling is missing,
|
|
54
|
+
install it with `npm exec --yes --package agent-browser@latest -- agent-browser install`.
|
|
55
|
+
|
|
56
|
+
Stub verification does not establish real account data, permissions, host layout,
|
|
57
|
+
or visual quality. Live verification exercises the authenticated runtime but still
|
|
58
|
+
uses the harness. The final installed-app check establishes the result inside Notis.
|
|
59
|
+
If that surface cannot be inspected, say so rather than claim it passed. No extra
|
|
60
|
+
approval round is needed for an already-authorized check.
|
|
61
|
+
|
|
62
|
+
`apps screenshot` supports declared scenarios and stub fixtures, including
|
|
63
|
+
`theme: 'dark'`; `--raw` gives uncomposited captures. Store listing screenshots are
|
|
64
|
+
not required for an ordinary Workspace update.
|
|
65
|
+
|
|
66
|
+
## Special cases — read only when relevant
|
|
67
|
+
|
|
68
|
+
### Unreleased container or stale checkout
|
|
69
|
+
|
|
70
|
+
An unreleased container has no source to pull. Recover its original local source,
|
|
71
|
+
or scaffold only if it cannot be recovered; verify the exact ID and scope and use
|
|
72
|
+
`apps link <app-id> <dir> --expected-version 0`. Reuse the container after a failed
|
|
73
|
+
first release; do not duplicate or automatically delete it. If another release
|
|
74
|
+
has appeared, pull it into a fresh directory and reapply the intended edits without
|
|
75
|
+
replacing its deployment base. Link/deploy guards must reject races and conflicts.
|
|
76
|
+
|
|
77
|
+
### Restore an older source
|
|
78
|
+
|
|
79
|
+
Pull the current release into a fresh checkout and the historical source into a
|
|
80
|
+
separate folder (`apps pull <id> <dir> --source-version <n>`). Replace source without
|
|
81
|
+
replacing the current `.notis` link/base, then check and deploy as a new release.
|
|
82
|
+
Preserve app/database/skill IDs. Never decrement versions or imply that source
|
|
83
|
+
restoration undoes user data or external actions.
|
|
84
|
+
|
|
85
|
+
### Release history and Store publication
|
|
86
|
+
|
|
87
|
+
Keep all release history in root `CHANGELOG.md`, newest first, with headings
|
|
88
|
+
`## [Release title] - YYYY-MM-DD` (or `{PR_MERGE_DATE}` while unpublished). Do not add
|
|
89
|
+
`versionNotes` to the config. App Details reads deployed history; the Store reads
|
|
90
|
+
its published snapshot. Local edits must not change the published listing.
|
|
91
|
+
|
|
92
|
+
`apps deploy` updates Workspace only. Use `apps publish --confirm-ready` only after
|
|
93
|
+
the user explicitly approves the current App Details and Store listing. Deploy the
|
|
94
|
+
exact approved source first. Respect listing completeness, visibility, version,
|
|
95
|
+
and pending-review guards. A public submission includes editable source, Store
|
|
96
|
+
assets, source-declared database schemas, and only explicitly opted-in starter
|
|
97
|
+
rows. Do not hand-edit `notis-listing.json` or strip files to pass review; fix the
|
|
98
|
+
source, redeploy, and resubmit. To start from a Store app, use `apps init --from
|
|
99
|
+
<slug>`; `apps pull` is for an accessible installed app, not a Store listing clone.
|
|
@@ -6,8 +6,9 @@ already installs `ShortcutProvider`; app code should not add a second provider.
|
|
|
6
6
|
| API | Signature | Description |
|
|
7
7
|
|-----|-----------|-------------|
|
|
8
8
|
| `useNotis()` | `() => { app, route, databases, collectionItem, resourceId, ready }` | App metadata, current route, selected collection item, decoded exact-resource id, ready state |
|
|
9
|
-
| `useTool<TArgs, TResult>(name)` | `(name: string) => { call, loading, error }` | Call a declared tool with app-defined argument/result types.
|
|
9
|
+
| `useTool<TArgs, TResult>(name)` | `(name: string) => { call, loading, error }` | Call a declared tool with app-defined argument/result types. Identified reads use `call(args, { readOnly: true, dedupe: true })`; never dedupe writes. See [cached-read ownership](design.md#instant-view-loading-contract-required). |
|
|
10
10
|
| `useTools()` | `() => { tools, loading }` | List available tools |
|
|
11
|
+
| `useDocuments(slug, opts?)` | `(slug: string, opts?) => { documents, loading, hasData, error, refetch }` | Query an app database. Bodies are included by default. For metadata-only lists, opt into `includeContent: false`; load the opened record with `useDocument` and defer any full-body search query until needed. Metadata and full-content query caches are separate. |
|
|
11
12
|
| `useNotisNavigation()` | `() => { toRoute, toDocument, toApp }` | Navigate between routes (including `toRoute(path, { resourceId })`), documents, or the app root |
|
|
12
13
|
| `useTopBarSearch(opts)` | `({ value, onChange, placeholder?, onSubmit? }) => { setLoading }` | Bind the current view to the Portal-owned top-bar search input |
|
|
13
14
|
| `useBackend()` | `() => { request }` | Raw backend request proxy with JWT auth |
|
|
@@ -1,26 +1,23 @@
|
|
|
1
|
-
## Anti-patterns -- NEVER do these
|
|
2
|
-
|
|
3
|
-
These are the most common mistakes agents make. Each one wastes time and produces broken results.
|
|
4
|
-
|
|
5
|
-
- **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.
|
|
6
|
-
- **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`.
|
|
7
|
-
- **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.
|
|
8
|
-
- **One local/cloud delivery contract** -- The default permits Workspace delivery after checks only when user/repository policy allows it. Explicit read-only, preview-only and no-deploy requests prohibit remote mutations. Neither authorizes Store publication.
|
|
9
|
-
- **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.
|
|
10
|
-
- **NEVER write raw `views/<slug>/index.js` files** -- Write standard React pages in `app/`.
|
|
11
|
-
- **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.
|
|
12
|
-
- **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.
|
|
13
|
-
- **NEVER explore server code or tool schemas to invent an alternative app workflow** -- Use the Notis CLI.
|
|
14
|
-
- **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.
|
|
15
|
-
- **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.
|
|
16
|
-
- **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.
|
|
17
|
-
|
|
18
1
|
## Troubleshooting
|
|
19
2
|
|
|
20
|
-
|
|
3
|
+
Start with the failing screen or operation. Do not invent a second app workflow;
|
|
4
|
+
use the [delivery guide](release.md) and supported Notis CLI commands.
|
|
21
5
|
|
|
22
|
-
- **
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- **
|
|
6
|
+
- **App looks wrong despite passing checks:** open it inside Notis and compare
|
|
7
|
+
the affected region with the request. Check loading and loaded states, viewport
|
|
8
|
+
sizing, scrolling, and theme. A standalone harness does not reproduce the host's
|
|
9
|
+
parent layout or shadow boundary. Do not assume the cause from a screenshot alone.
|
|
10
|
+
- **Build reports a design violation:** use the scaffold component or theme token
|
|
11
|
+
suggested by the diagnostic. Fix the reported file/line rather than hiding the
|
|
12
|
+
pattern elsewhere. Existing validator exceptions are for justified cases, not a
|
|
13
|
+
shortcut around visual review.
|
|
14
|
+
- **The configured sidebar is missing:** preserve `routes` and `collection.sidebar`;
|
|
15
|
+
investigate the host mismatch instead of duplicating the sidebar in app code.
|
|
16
|
+
- **The app shows old code or is missing from Workspace:** check the exact installed
|
|
17
|
+
app/version and requested bundle first. Local source edits and Desktop restarts
|
|
18
|
+
do not update a released app. Refresh after confirming the correct release exists.
|
|
19
|
+
- **Deploy transport failure:** run `notis doctor` and read back the exact app/version.
|
|
20
|
+
Reconcile the outcome before retrying; do not bypass the backend with storage writes.
|
|
21
|
+
- **Database query is empty or properties are undefined:** inspect the actual schema,
|
|
22
|
+
database ID, and returned property shape through the CLI. Keep types in the app,
|
|
23
|
+
guard optional fields, and distinguish an error from a successful empty result.
|
|
@@ -77,8 +77,8 @@ Default delivery below applies only when no more restrictive user or repository
|
|
|
77
77
|
instruction exists. Explicit preview-only/no-deploy requests and standing requirements
|
|
78
78
|
for explicit deployment consent override the default. Preserve that authority across
|
|
79
79
|
local and cloud runs. For local-only work, build and run stub verification; do not
|
|
80
|
-
create remote resources or activate an app.
|
|
81
|
-
|
|
80
|
+
create remote resources or activate an app. Use the CLI's documented
|
|
81
|
+
build/verification harness. Store publication remains
|
|
82
82
|
separately authorized.
|
|
83
83
|
|
|
84
84
|
### Tool access workflow
|
package/package.json
CHANGED
|
@@ -19,6 +19,8 @@ export interface UseDocumentsOptions {
|
|
|
19
19
|
offset?: number;
|
|
20
20
|
/** Fetch every page, starting at offset, instead of returning only one page. */
|
|
21
21
|
fetchAll?: boolean;
|
|
22
|
+
/** App-view lists can omit bodies until opening/searching a document. Default: true. */
|
|
23
|
+
includeContent?: boolean;
|
|
22
24
|
enabled?: boolean;
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -47,7 +49,7 @@ export function useDocuments(
|
|
|
47
49
|
): UseDocumentsResult {
|
|
48
50
|
const runtime = useNotisRuntime();
|
|
49
51
|
const query = useQuery<DocumentRecord[]>(
|
|
50
|
-
['documents', databaseSlug, options.filter ?? null, options.pageSize ?? null, options.offset ?? 0, Boolean(options.fetchAll)],
|
|
52
|
+
['documents', databaseSlug, options.filter ?? null, options.pageSize ?? null, options.offset ?? 0, Boolean(options.fetchAll), options.includeContent !== false],
|
|
51
53
|
async () => {
|
|
52
54
|
if (!runtime) throw new Error('Notis runtime not available');
|
|
53
55
|
const allDocuments: unknown[] = [];
|
|
@@ -58,6 +60,7 @@ export function useDocuments(
|
|
|
58
60
|
query: {
|
|
59
61
|
...(options.filter ?? {}),
|
|
60
62
|
...(options.pageSize !== undefined ? { page_size: options.pageSize } : {}),
|
|
63
|
+
...(options.includeContent === false ? { include_content: false } : {}),
|
|
61
64
|
},
|
|
62
65
|
...(offset > 0 ? { offset } : {}),
|
|
63
66
|
}, { dedupe: true, readOnly: true });
|
|
@@ -68,6 +68,8 @@ export function createQueryClient(options: {
|
|
|
68
68
|
maxEntries?: number;
|
|
69
69
|
now?: () => number;
|
|
70
70
|
schedule?: ReturnType<typeof createPrefetchQueue>;
|
|
71
|
+
/** Bounds idempotent reads only; never retries the underlying callback. */
|
|
72
|
+
readTimeoutMs?: number;
|
|
71
73
|
} = {}): NotisQueryClient {
|
|
72
74
|
const entries = new Map<string, Entry>();
|
|
73
75
|
const now = options.now ?? Date.now;
|
|
@@ -117,14 +119,20 @@ export function createQueryClient(options: {
|
|
|
117
119
|
const generation = ++value.generation;
|
|
118
120
|
const requestEpoch = epoch;
|
|
119
121
|
const canCommit = () => epoch === requestEpoch && value.generation === generation && entries.get(key) === value;
|
|
120
|
-
|
|
122
|
+
let timeout: ReturnType<typeof setTimeout>;
|
|
123
|
+
const deadline = new Promise<never>((_, reject) => {
|
|
124
|
+
timeout = setTimeout(() => reject(new Error('This read took too long. Please retry.')), options.readTimeoutMs ?? 30_000);
|
|
125
|
+
// Node-based verification must not stay alive for a retired browser read.
|
|
126
|
+
if (typeof timeout === 'object' && 'unref' in timeout) timeout.unref();
|
|
127
|
+
});
|
|
128
|
+
const request = Promise.race([Promise.resolve().then(read), deadline]).then((data) => {
|
|
121
129
|
if (canCommit()) publish(value, { ...value.snapshot, data, hasData: true, isFetching: false, error: null, updatedAt: now() });
|
|
122
130
|
return data;
|
|
123
131
|
}, (reason) => {
|
|
124
132
|
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
125
133
|
if (canCommit()) publish(value, { ...value.snapshot, isFetching: false, error });
|
|
126
134
|
throw error;
|
|
127
|
-
}).finally(() => { if (value.pending === request) value.pending = undefined; prune(); });
|
|
135
|
+
}).finally(() => { clearTimeout(timeout); if (value.pending === request) value.pending = undefined; prune(); });
|
|
128
136
|
value.pending = request;
|
|
129
137
|
publish(value, { ...value.snapshot, isFetching: true, error: null });
|
|
130
138
|
return request;
|