@notis_ai/cli 0.2.0-beta.32.1 → 0.2.0-beta.35.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.
Files changed (68) hide show
  1. package/README.md +41 -106
  2. package/dist/scaffolds/notes/app/globals.css +3 -0
  3. package/dist/scaffolds/notes/app/layout.tsx +6 -0
  4. package/dist/scaffolds/notes/app/page.tsx +1465 -0
  5. package/dist/scaffolds/notes/components/ui/badge.tsx +28 -0
  6. package/dist/scaffolds/notes/components/ui/button.tsx +53 -0
  7. package/dist/scaffolds/notes/components/ui/card.tsx +56 -0
  8. package/dist/scaffolds/notes/components.json +20 -0
  9. package/dist/scaffolds/notes/lib/utils.ts +6 -0
  10. package/dist/scaffolds/notes/notis.config.ts +31 -0
  11. package/dist/scaffolds/notes/package.json +31 -0
  12. package/dist/scaffolds/notes/postcss.config.mjs +8 -0
  13. package/dist/scaffolds/notes/tailwind.config.ts +58 -0
  14. package/dist/scaffolds/notes/tsconfig.json +22 -0
  15. package/dist/scaffolds/notes/vite.config.ts +10 -0
  16. package/dist/scaffolds.json +13 -0
  17. package/package.json +7 -3
  18. package/skills/notis-apps/SKILL.md +168 -0
  19. package/skills/notis-apps/cli.md +208 -0
  20. package/skills/notis-cli/SKILL.md +263 -0
  21. package/skills/notis-query/cli.md +40 -0
  22. package/src/cli.js +1 -1
  23. package/src/command-specs/apps.js +211 -46
  24. package/src/command-specs/helpers.js +0 -60
  25. package/src/command-specs/index.js +0 -6
  26. package/src/command-specs/meta.js +7 -6
  27. package/src/command-specs/tools.js +1 -33
  28. package/src/runtime/app-dev-server.js +30 -2
  29. package/src/runtime/app-platform.js +376 -24
  30. package/src/runtime/profiles.js +2 -2
  31. package/src/runtime/transport.js +2 -2
  32. package/template/.harness/index.html.tmpl +1 -50
  33. package/template/app/page.tsx +41 -6
  34. package/template/metadata/cover.png +0 -0
  35. package/template/metadata/screenshot-1.png +0 -0
  36. package/template/metadata/screenshot-2.png +0 -0
  37. package/template/metadata/screenshot-3.png +0 -0
  38. package/template/notis.config.ts +10 -6
  39. package/template/package.json +2 -2
  40. package/template/packages/{notis-sdk → sdk}/src/components/MultiSelectActionBar.tsx +2 -1
  41. package/template/packages/{notis-sdk → sdk}/src/components/MultiSelectCheckbox.tsx +2 -2
  42. package/template/packages/{notis-sdk → sdk}/src/components/MultiSelectDragOverlay.tsx +2 -2
  43. package/template/packages/{notis-sdk → sdk}/src/config.ts +1 -1
  44. package/template/packages/{notis-sdk → sdk}/src/hooks/useMultiSelect.ts +4 -3
  45. package/template/packages/sdk/src/hooks/useNotis.ts +30 -0
  46. package/template/packages/{notis-sdk → sdk}/src/hooks/useNotisNavigation.ts +3 -3
  47. package/template/packages/{notis-sdk → sdk}/src/hooks/useTool.ts +22 -7
  48. package/template/packages/{notis-sdk → sdk}/src/index.ts +3 -24
  49. package/template/packages/{notis-sdk → sdk}/src/provider.tsx +1 -1
  50. package/template/packages/sdk/src/runtime.ts +80 -0
  51. package/src/command-specs/auth.js +0 -178
  52. package/src/command-specs/db.js +0 -163
  53. package/template/packages/notis-sdk/src/helpers.ts +0 -131
  54. package/template/packages/notis-sdk/src/hooks/useAppState.ts +0 -50
  55. package/template/packages/notis-sdk/src/hooks/useCollectionItem.ts +0 -58
  56. package/template/packages/notis-sdk/src/hooks/useDatabase.ts +0 -88
  57. package/template/packages/notis-sdk/src/hooks/useDocument.ts +0 -61
  58. package/template/packages/notis-sdk/src/hooks/useNotis.ts +0 -33
  59. package/template/packages/notis-sdk/src/hooks/useUpsertDocument.ts +0 -58
  60. package/template/packages/notis-sdk/src/runtime.ts +0 -182
  61. /package/template/packages/{notis-sdk → sdk}/package.json +0 -0
  62. /package/template/packages/{notis-sdk → sdk}/src/hooks/useBackend.ts +0 -0
  63. /package/template/packages/{notis-sdk → sdk}/src/hooks/useTools.ts +0 -0
  64. /package/template/packages/{notis-sdk → sdk}/src/hooks/useTopBarSearch.ts +0 -0
  65. /package/template/packages/{notis-sdk → sdk}/src/styles.css +0 -0
  66. /package/template/packages/{notis-sdk → sdk}/src/ui.ts +0 -0
  67. /package/template/packages/{notis-sdk → sdk}/src/vite.ts +0 -0
  68. /package/template/packages/{notis-sdk → sdk}/tsconfig.json +0 -0
@@ -0,0 +1,28 @@
1
+ import * as React from 'react';
2
+ import { cva, type VariantProps } from 'class-variance-authority';
3
+
4
+ import { cn } from '@/lib/utils';
5
+
6
+ const badgeVariants = cva(
7
+ 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'border-transparent bg-primary/10 text-primary',
12
+ secondary: 'border-transparent bg-secondary text-secondary-foreground',
13
+ outline: 'text-foreground',
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: 'default',
18
+ },
19
+ },
20
+ );
21
+
22
+ export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
23
+
24
+ function Badge({ className, variant, ...props }: BadgeProps) {
25
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
26
+ }
27
+
28
+ export { Badge, badgeVariants };
@@ -0,0 +1,53 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '@/lib/utils';
6
+
7
+ const buttonVariants = cva(
8
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
13
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
14
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
15
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
16
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
17
+ link: 'text-primary underline-offset-4 hover:underline',
18
+ },
19
+ size: {
20
+ default: 'h-10 px-4 py-2',
21
+ sm: 'h-9 rounded-md px-3',
22
+ lg: 'h-11 rounded-md px-8',
23
+ icon: 'h-10 w-10',
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ variant: 'default',
28
+ size: 'default',
29
+ },
30
+ },
31
+ );
32
+
33
+ export interface ButtonProps
34
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
35
+ VariantProps<typeof buttonVariants> {
36
+ asChild?: boolean;
37
+ }
38
+
39
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
40
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
41
+ const Comp = asChild ? Slot : 'button';
42
+ return (
43
+ <Comp
44
+ className={cn(buttonVariants({ variant, size, className }))}
45
+ ref={ref}
46
+ {...props}
47
+ />
48
+ );
49
+ },
50
+ );
51
+ Button.displayName = 'Button';
52
+
53
+ export { Button, buttonVariants };
@@ -0,0 +1,56 @@
1
+ import * as React from 'react';
2
+
3
+ import { cn } from '@/lib/utils';
4
+
5
+ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
6
+ ({ className, ...props }, ref) => (
7
+ <div
8
+ ref={ref}
9
+ className={cn('rounded-2xl border bg-card text-card-foreground shadow-sm', className)}
10
+ {...props}
11
+ />
12
+ ),
13
+ );
14
+ Card.displayName = 'Card';
15
+
16
+ const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
17
+ ({ className, ...props }, ref) => (
18
+ <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
19
+ ),
20
+ );
21
+ CardHeader.displayName = 'CardHeader';
22
+
23
+ const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
24
+ ({ className, ...props }, ref) => (
25
+ <h3
26
+ ref={ref}
27
+ className={cn('text-xl font-semibold leading-none tracking-tight', className)}
28
+ {...props}
29
+ />
30
+ ),
31
+ );
32
+ CardTitle.displayName = 'CardTitle';
33
+
34
+ const CardDescription = React.forwardRef<
35
+ HTMLParagraphElement,
36
+ React.HTMLAttributes<HTMLParagraphElement>
37
+ >(({ className, ...props }, ref) => (
38
+ <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
39
+ ));
40
+ CardDescription.displayName = 'CardDescription';
41
+
42
+ const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
43
+ ({ className, ...props }, ref) => (
44
+ <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
45
+ ),
46
+ );
47
+ CardContent.displayName = 'CardContent';
48
+
49
+ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
50
+ ({ className, ...props }, ref) => (
51
+ <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
52
+ ),
53
+ );
54
+ CardFooter.displayName = 'CardFooter';
55
+
56
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.ts",
8
+ "css": "app/globals.css",
9
+ "baseColor": "zinc",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ }
20
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,31 @@
1
+ import { defineNotisApp } from '@notis/sdk/config';
2
+
3
+ export default defineNotisApp({
4
+ name: 'Notis Notes',
5
+ description: 'Browse notes by documents, table, or calendar with folders in the sidebar.',
6
+ icon: 'lucide:notebook-pen',
7
+ databases: ['note_folders', 'notes'],
8
+ routes: [
9
+ {
10
+ path: '/',
11
+ slug: 'notes',
12
+ name: 'Notes',
13
+ icon: 'lucide:notebook-pen',
14
+ default: true,
15
+ collection: {
16
+ database: 'note_folders',
17
+ titleProperty: 'Name',
18
+ parentProperty: 'Parent',
19
+ sidebar: {
20
+ mode: 'tree',
21
+ allowCreate: true,
22
+ },
23
+ },
24
+ },
25
+ ],
26
+ tools: [
27
+ 'notis_query_database',
28
+ 'notis_get_document',
29
+ 'notis_upsert_document',
30
+ ],
31
+ });
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "notis-notes-app",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build"
9
+ },
10
+ "dependencies": {
11
+ "@notis/sdk": "file:../../packages/notis-sdk",
12
+ "@radix-ui/react-slot": "^1.1.0",
13
+ "class-variance-authority": "^0.7.0",
14
+ "clsx": "^2.1.1",
15
+ "lucide-react": "^0.474.0",
16
+ "react": "^19.0.0",
17
+ "react-dom": "^19.0.0",
18
+ "tailwind-merge": "^2.6.0",
19
+ "tailwindcss-animate": "^1.0.7"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "@types/react": "^19.0.0",
24
+ "@types/react-dom": "^19.0.0",
25
+ "@vitejs/plugin-react": "^4.0.0",
26
+ "postcss": "^8.0.0",
27
+ "tailwindcss": "^3.4.0",
28
+ "typescript": "^5.0.0",
29
+ "vite": "^6.0.0"
30
+ }
31
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
@@ -0,0 +1,58 @@
1
+ import type { Config } from 'tailwindcss';
2
+ import tailwindAnimate from 'tailwindcss-animate';
3
+
4
+ const config: Config = {
5
+ darkMode: ['class'],
6
+ content: [
7
+ './app/**/*.{ts,tsx}',
8
+ './components/**/*.{ts,tsx}',
9
+ './lib/**/*.{ts,tsx}',
10
+ ],
11
+ theme: {
12
+ extend: {
13
+ colors: {
14
+ border: 'hsl(var(--border))',
15
+ input: 'hsl(var(--input))',
16
+ ring: 'hsl(var(--ring))',
17
+ background: 'hsl(var(--background))',
18
+ foreground: 'hsl(var(--foreground))',
19
+ primary: {
20
+ DEFAULT: 'hsl(var(--primary))',
21
+ foreground: 'hsl(var(--primary-foreground))',
22
+ },
23
+ secondary: {
24
+ DEFAULT: 'hsl(var(--secondary))',
25
+ foreground: 'hsl(var(--secondary-foreground))',
26
+ },
27
+ destructive: {
28
+ DEFAULT: 'hsl(var(--destructive))',
29
+ foreground: 'hsl(var(--destructive-foreground))',
30
+ },
31
+ muted: {
32
+ DEFAULT: 'hsl(var(--muted))',
33
+ foreground: 'hsl(var(--muted-foreground))',
34
+ },
35
+ accent: {
36
+ DEFAULT: 'hsl(var(--accent))',
37
+ foreground: 'hsl(var(--accent-foreground))',
38
+ },
39
+ popover: {
40
+ DEFAULT: 'hsl(var(--popover))',
41
+ foreground: 'hsl(var(--popover-foreground))',
42
+ },
43
+ card: {
44
+ DEFAULT: 'hsl(var(--card))',
45
+ foreground: 'hsl(var(--card-foreground))',
46
+ },
47
+ },
48
+ borderRadius: {
49
+ lg: 'var(--radius)',
50
+ md: 'calc(var(--radius) - 2px)',
51
+ sm: 'calc(var(--radius) - 4px)',
52
+ },
53
+ },
54
+ },
55
+ plugins: [tailwindAnimate],
56
+ };
57
+
58
+ export default config;
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "types": ["vite/client"],
16
+ "paths": {
17
+ "@/*": ["./*"]
18
+ }
19
+ },
20
+ "include": ["**/*.ts", "**/*.tsx"],
21
+ "exclude": ["node_modules"]
22
+ }
@@ -0,0 +1,10 @@
1
+ import { notisViteConfig } from '@notis/sdk/vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import appConfig from './notis.config';
4
+
5
+ const config = notisViteConfig(appConfig);
6
+
7
+ export default {
8
+ ...config,
9
+ plugins: [react(), ...(config.plugins || [])],
10
+ };
@@ -0,0 +1,13 @@
1
+ {
2
+ "source": "apps/*/notis.config.ts",
3
+ "scaffolds": [
4
+ {
5
+ "slug": "notes",
6
+ "name": "Notis Notes",
7
+ "description": "Browse notes by documents, table, or calendar with folders in the sidebar.",
8
+ "icon": "lucide:notebook-pen",
9
+ "categories": [],
10
+ "tagline": "Browse notes by documents, table, or calendar with folders in the sidebar."
11
+ }
12
+ ]
13
+ }
package/package.json CHANGED
@@ -1,21 +1,25 @@
1
1
  {
2
2
  "name": "@notis_ai/cli",
3
- "version": "0.2.0-beta.32.1",
4
- "description": "Agent-first Notis CLI for apps, databases, and generic tool execution",
3
+ "version": "0.2.0-beta.35.1",
4
+ "description": "Agent-first Notis CLI for apps and generic tool execution",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "notis": "bin/notis.js"
8
8
  },
9
9
  "files": [
10
10
  "bin/",
11
+ "dist/",
11
12
  "src/",
13
+ "skills/",
12
14
  "template/",
13
15
  "README.md"
14
16
  ],
15
17
  "scripts": {
18
+ "build": "node ./scripts/build-scaffolds.js",
16
19
  "docs:generate": "node ./scripts/generate-docs.js",
17
20
  "docs:check": "node ./scripts/generate-docs.js --check",
18
- "release:prepare": "node ./scripts/prepare-publish.js --apply",
21
+ "prepack": "npm run build",
22
+ "release:prepare": "npm run build && node ./scripts/prepare-publish.js --apply",
19
23
  "cli:set-mode": "node ./scripts/set-cli-mode.js",
20
24
  "test": "node --test"
21
25
  },
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: notis-apps
3
+ description: Design and package Notis apps. Use when users want an app that groups databases, routes, documents, automations, and skills into one installable Notis product.
4
+ ---
5
+
6
+ # Notis Apps Skill
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.
9
+
10
+ The Notis desktop app automatically installs and keeps this `notis-apps` skill updated alongside the `notis-cli` skill for local agents. No separate manual skill install is needed on desktop.
11
+
12
+ ## Platform Model
13
+
14
+ A Notis app is:
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
22
+
23
+ Use the Notis CLI for the app lifecycle:
24
+
25
+ ```bash
26
+ notis apps init
27
+ notis apps dev
28
+ notis apps build
29
+ notis apps verify
30
+ notis apps create
31
+ notis apps link
32
+ notis apps pull
33
+ notis apps deploy
34
+ notis apps doctor
35
+ ```
36
+
37
+ When working in a local repo workspace, prefer the repo-local CLI entrypoint if available:
38
+
39
+ ```bash
40
+ node packages/cli/bin/notis.js apps ...
41
+ ```
42
+
43
+ When working outside a repo with the desktop-installed CLI, use:
44
+
45
+ ```bash
46
+ notis apps ...
47
+ ```
48
+
49
+ ## Hard Rules
50
+
51
+ - Use React + Vite only. Do not use Next.js or a custom server for app projects.
52
+ - Build standard pages in `app/`. Do not write raw `views/<slug>/index.js` files.
53
+ - Keep navigation in `notis.config.ts` `routes`; each route needs a stable `slug`.
54
+ - Reference existing databases by slug in `notis.config.ts`; do not assume deploy creates databases automatically.
55
+ - Declare runtime tool access in `notis.config.ts` `tools`.
56
+ - Use `@notis/sdk` hooks instead of direct runtime access.
57
+ - Do not rely on `window.__NOTIS_RUNTIME__` or portal-owned DOM hooks.
58
+ - `notis apps deploy` updates an installed app. It does not publish to the public App Store.
59
+ - Public or team App Store publishing is a Portal review flow.
60
+ - Use Phosphor icon names with the `phosphor:` prefix. Do not use emoji icons.
61
+ - Prefer scaffolded shadcn components and portal theme tokens over custom visual systems.
62
+
63
+ ## App Workflow
64
+
65
+ 1. Discover existing apps if needed:
66
+
67
+ ```bash
68
+ notis apps list
69
+ ```
70
+
71
+ 2. Start from a scaffold or pull an existing app:
72
+
73
+ ```bash
74
+ notis apps scaffolds list
75
+ notis apps init "My App"
76
+ notis apps pull <app-id> ./my-app
77
+ ```
78
+
79
+ 3. Install dependencies and develop:
80
+
81
+ ```bash
82
+ npm install
83
+ notis apps dev
84
+ ```
85
+
86
+ 4. Build and verify:
87
+
88
+ ```bash
89
+ notis apps build
90
+ notis apps verify
91
+ ```
92
+
93
+ 5. Create or link the remote app, then deploy:
94
+
95
+ ```bash
96
+ notis apps create "My App" .
97
+ notis apps deploy
98
+ ```
99
+
100
+ or:
101
+
102
+ ```bash
103
+ notis apps link <app-id>
104
+ notis apps deploy
105
+ ```
106
+
107
+ ## App Structure
108
+
109
+ Expected project shape:
110
+
111
+ ```text
112
+ notis.config.ts
113
+ vite.config.ts
114
+ app/
115
+ components/
116
+ metadata/
117
+ .notis/output/
118
+ ```
119
+
120
+ `notis.config.ts` owns:
121
+
122
+ - app metadata: name, title, description, icon, categories, tagline
123
+ - database slug references
124
+ - route definitions
125
+ - tool access
126
+
127
+ Example:
128
+
129
+ ```ts
130
+ import { defineNotisApp } from '@notis/sdk/config';
131
+
132
+ export default defineNotisApp({
133
+ name: 'my-app',
134
+ title: 'My App',
135
+ description: 'A focused Notis app.',
136
+ icon: 'phosphor:squares-four',
137
+ databases: ['tasks'],
138
+ routes: [
139
+ {
140
+ path: '/',
141
+ slug: 'tasks',
142
+ name: 'Tasks',
143
+ icon: 'phosphor:check-square',
144
+ default: true,
145
+ },
146
+ ],
147
+ tools: ['notis-default-query', 'notis-default-upsert_tasks'],
148
+ });
149
+ ```
150
+
151
+ ## Runtime Usage
152
+
153
+ Use SDK hooks for data and tool access:
154
+
155
+ ```tsx
156
+ import { useTool } from '@notis/sdk';
157
+
158
+ const query = useTool('notis-default-query');
159
+ ```
160
+
161
+ 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.
162
+
163
+ ## Troubleshooting
164
+
165
+ - If deploy fails with backend connectivity errors, run `notis apps doctor` and check the configured API base.
166
+ - If editing an existing app, use `notis apps pull <app-id>` when source snapshots are available.
167
+ - If the Portal shows a stale bundle, hard refresh the Portal or deploy a new version.
168
+ - If an app route fails to load, run `notis apps verify` and inspect `.notis/output/manifest.json`.