@notis_ai/cli 0.2.1 → 0.2.3

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 (117) hide show
  1. package/README.md +106 -170
  2. package/config/notis_app_boundary_rules.json +50 -0
  3. package/dist/scaffolds/notis-database/app/globals.css +44 -0
  4. package/dist/scaffolds/notis-database/app/layout.tsx +6 -0
  5. package/dist/scaffolds/notis-database/app/page.tsx +1091 -0
  6. package/dist/scaffolds/notis-database/components/ui/badge.tsx +28 -0
  7. package/dist/scaffolds/notis-database/components/ui/button.tsx +53 -0
  8. package/dist/scaffolds/notis-database/components/ui/card.tsx +56 -0
  9. package/dist/scaffolds/notis-database/components/ui/table.tsx +120 -0
  10. package/dist/scaffolds/notis-database/components.json +20 -0
  11. package/dist/scaffolds/notis-database/index.html +12 -0
  12. package/dist/scaffolds/notis-database/lib/types.ts +134 -0
  13. package/dist/scaffolds/notis-database/lib/utils.ts +6 -0
  14. package/dist/scaffolds/notis-database/metadata/cover.png +0 -0
  15. package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
  16. package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
  17. package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
  18. package/dist/scaffolds/notis-database/notis.config.ts +27 -0
  19. package/dist/scaffolds/notis-database/package.json +31 -0
  20. package/dist/scaffolds/notis-database/postcss.config.mjs +8 -0
  21. package/dist/scaffolds/notis-database/src/dev-main.tsx +23 -0
  22. package/dist/scaffolds/notis-database/src/mock-runtime.ts +557 -0
  23. package/dist/scaffolds/notis-database/tailwind.config.ts +59 -0
  24. package/dist/scaffolds/notis-database/tsconfig.json +23 -0
  25. package/dist/scaffolds/notis-database/vite.config.ts +22 -0
  26. package/dist/scaffolds/notis-notes/app/globals.css +3 -0
  27. package/dist/scaffolds/notis-notes/app/layout.tsx +6 -0
  28. package/dist/scaffolds/notis-notes/app/page.tsx +2168 -0
  29. package/dist/scaffolds/notis-notes/components/ui/badge.tsx +28 -0
  30. package/dist/scaffolds/notis-notes/components/ui/button.tsx +53 -0
  31. package/dist/scaffolds/notis-notes/components/ui/card.tsx +56 -0
  32. package/dist/scaffolds/notis-notes/components.json +20 -0
  33. package/dist/scaffolds/notis-notes/lib/utils.ts +6 -0
  34. package/dist/scaffolds/notis-notes/metadata/cover.png +0 -0
  35. package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
  36. package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
  37. package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
  38. package/dist/scaffolds/notis-notes/notis.config.ts +36 -0
  39. package/dist/scaffolds/notis-notes/package.json +31 -0
  40. package/dist/scaffolds/notis-notes/postcss.config.mjs +8 -0
  41. package/dist/scaffolds/notis-notes/tailwind.config.ts +58 -0
  42. package/dist/scaffolds/notis-notes/tsconfig.json +23 -0
  43. package/dist/scaffolds/notis-notes/vite.config.ts +10 -0
  44. package/dist/scaffolds/notis-random/README.md +33 -0
  45. package/dist/scaffolds/notis-random/app/globals.css +11 -0
  46. package/dist/scaffolds/notis-random/app/history/page.tsx +66 -0
  47. package/dist/scaffolds/notis-random/app/layout.tsx +7 -0
  48. package/dist/scaffolds/notis-random/app/page.tsx +222 -0
  49. package/dist/scaffolds/notis-random/index.html +12 -0
  50. package/dist/scaffolds/notis-random/lib/notis-tools.ts +109 -0
  51. package/dist/scaffolds/notis-random/lib/rng.ts +42 -0
  52. package/dist/scaffolds/notis-random/lib/roll-record.ts +102 -0
  53. package/dist/scaffolds/notis-random/lib/utils.ts +25 -0
  54. package/dist/scaffolds/notis-random/metadata/cover.png +0 -0
  55. package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
  56. package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
  57. package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
  58. package/dist/scaffolds/notis-random/notis.config.ts +43 -0
  59. package/dist/scaffolds/notis-random/package.json +32 -0
  60. package/dist/scaffolds/notis-random/postcss.config.mjs +6 -0
  61. package/dist/scaffolds/notis-random/src/dev-main.tsx +70 -0
  62. package/dist/scaffolds/notis-random/src/mock-runtime.ts +128 -0
  63. package/dist/scaffolds/notis-random/tailwind.config.ts +43 -0
  64. package/dist/scaffolds/notis-random/tsconfig.json +23 -0
  65. package/dist/scaffolds/notis-random/vite.config.ts +11 -0
  66. package/dist/scaffolds.json +36 -0
  67. package/package.json +8 -3
  68. package/skills/notis-apps/SKILL.md +162 -0
  69. package/skills/notis-apps/cli.md +208 -0
  70. package/skills/notis-cli/SKILL.md +260 -0
  71. package/skills/notis-query/cli.md +39 -0
  72. package/src/cli.js +31 -3
  73. package/src/command-specs/apps.js +248 -56
  74. package/src/command-specs/helpers.js +72 -104
  75. package/src/command-specs/index.js +0 -6
  76. package/src/command-specs/meta.js +14 -13
  77. package/src/command-specs/tools.js +196 -115
  78. package/src/runtime/app-boundary-validator.js +65 -14
  79. package/src/runtime/app-dev-server.js +32 -4
  80. package/src/runtime/app-platform.js +404 -24
  81. package/src/runtime/profiles.js +12 -6
  82. package/src/runtime/transport.js +124 -39
  83. package/template/.harness/index.html.tmpl +1 -50
  84. package/template/app/page.tsx +41 -6
  85. package/template/metadata/cover.png +0 -0
  86. package/template/metadata/screenshot-1.png +0 -0
  87. package/template/metadata/screenshot-2.png +0 -0
  88. package/template/metadata/screenshot-3.png +0 -0
  89. package/template/notis.config.ts +10 -6
  90. package/template/package.json +2 -2
  91. package/template/packages/{notis-sdk → sdk}/package.json +6 -0
  92. package/template/packages/{notis-sdk → sdk}/src/components/MultiSelectActionBar.tsx +2 -1
  93. package/template/packages/{notis-sdk → sdk}/src/components/MultiSelectCheckbox.tsx +2 -2
  94. package/template/packages/{notis-sdk → sdk}/src/components/MultiSelectDragOverlay.tsx +2 -2
  95. package/template/packages/{notis-sdk → sdk}/src/config.ts +1 -1
  96. package/template/packages/{notis-sdk → sdk}/src/hooks/useDatabase.ts +1 -13
  97. package/template/packages/{notis-sdk → sdk}/src/hooks/useMultiSelect.ts +4 -3
  98. package/template/packages/{notis-sdk → sdk}/src/hooks/useNotis.ts +4 -3
  99. package/template/packages/{notis-sdk → sdk}/src/hooks/useNotisNavigation.ts +3 -3
  100. package/template/packages/{notis-sdk → sdk}/src/hooks/useTool.ts +22 -7
  101. package/template/packages/{notis-sdk → sdk}/src/hooks/useUpsertDocument.ts +0 -8
  102. package/template/packages/{notis-sdk → sdk}/src/index.ts +2 -15
  103. package/template/packages/{notis-sdk → sdk}/src/provider.tsx +1 -1
  104. package/template/packages/{notis-sdk → sdk}/src/runtime.ts +5 -26
  105. package/src/command-specs/auth.js +0 -178
  106. package/src/command-specs/db.js +0 -163
  107. package/template/packages/notis-sdk/src/helpers.ts +0 -131
  108. package/template/packages/notis-sdk/src/hooks/useAppState.ts +0 -50
  109. package/template/packages/notis-sdk/src/hooks/useCollectionItem.ts +0 -58
  110. package/template/packages/notis-sdk/src/hooks/useDocument.ts +0 -61
  111. /package/template/packages/{notis-sdk → sdk}/src/hooks/useBackend.ts +0 -0
  112. /package/template/packages/{notis-sdk → sdk}/src/hooks/useTools.ts +0 -0
  113. /package/template/packages/{notis-sdk → sdk}/src/hooks/useTopBarSearch.ts +0 -0
  114. /package/template/packages/{notis-sdk → sdk}/src/styles.css +0 -0
  115. /package/template/packages/{notis-sdk → sdk}/src/ui.ts +0 -0
  116. /package/template/packages/{notis-sdk → sdk}/src/vite.ts +0 -0
  117. /package/template/packages/{notis-sdk → sdk}/tsconfig.json +0 -0
@@ -0,0 +1,128 @@
1
+ import type { NotisRuntime } from '@notis/sdk';
2
+
3
+ interface MockDoc {
4
+ id: string;
5
+ title: string;
6
+ properties: Record<string, unknown>;
7
+ icon?: string | null;
8
+ databaseSlug?: string;
9
+ createdAt?: string | null;
10
+ lastEditedTime?: string | null;
11
+ contentBlocknote?: Array<Record<string, unknown>> | null;
12
+ contentMarkdown?: string | null;
13
+ plainText?: string | null;
14
+ }
15
+
16
+ const STATE: Record<string, MockDoc[]> = { rolls: [] };
17
+
18
+ declare global {
19
+ interface Window {
20
+ __NOTIS_RANDOM_RUNTIME__?: NotisRuntime;
21
+ }
22
+ }
23
+
24
+ export function installMockRuntime(initialRoute: 'home' | 'history' = 'home'): NotisRuntime {
25
+ const runtime: NotisRuntime = {
26
+ app: {
27
+ id: 'dev-notis-random',
28
+ name: 'Random Number Generator',
29
+ icon: 'phosphor:dice-five',
30
+ description: 'Dev preview — data is in-memory only.',
31
+ },
32
+ route: currentRoute(initialRoute),
33
+ context: {},
34
+
35
+ listTools: async () => [
36
+ {
37
+ name: 'LOCAL_NOTIS_DATABASE_QUERY',
38
+ inputSchema: { type: 'object', properties: { database_slug: { type: 'string' } } },
39
+ },
40
+ {
41
+ name: 'LOCAL_NOTIS_DATABASE_UPSERT_ROLLS',
42
+ inputSchema: { type: 'object', properties: {} },
43
+ },
44
+ ],
45
+ callTool: async <TResult = unknown>(name: string, args?: Record<string, unknown>): Promise<TResult> => {
46
+ if (name === 'LOCAL_NOTIS_DATABASE_QUERY') {
47
+ const request = args ?? {};
48
+ const databaseSlug = typeof request.database_slug === 'string' ? request.database_slug : 'rolls';
49
+ const query = request.query && typeof request.query === 'object'
50
+ ? request.query as Record<string, unknown>
51
+ : {};
52
+ const offset = typeof request.offset === 'number' ? request.offset : 0;
53
+ const all = STATE[databaseSlug] ?? [];
54
+ const sliced = all.slice(offset);
55
+ const pageSize = typeof query.page_size === 'number' ? query.page_size : undefined;
56
+ return { documents: typeof pageSize === 'number' ? sliced.slice(0, pageSize) : sliced } as TResult;
57
+ }
58
+
59
+ if (name === 'LOCAL_NOTIS_DATABASE_UPSERT_ROLLS') {
60
+ const request = args ?? {};
61
+ const docs = STATE.rolls ?? [];
62
+ const now = new Date().toISOString();
63
+ const documentId = typeof request.document_id === 'string' ? request.document_id : undefined;
64
+ const existing = documentId ? docs.find((d) => d.id === documentId) : undefined;
65
+ const controlKeys = new Set(['document_id', 'title']);
66
+ const properties = Object.fromEntries(
67
+ Object.entries(request).filter(([key]) => !controlKeys.has(key)),
68
+ );
69
+ const doc: MockDoc = {
70
+ id: existing?.id ?? `dev-rolls-${Date.now()}`,
71
+ title: typeof request.title === 'string' ? request.title : existing?.title ?? 'Untitled',
72
+ properties: { ...(existing?.properties ?? {}), ...properties },
73
+ databaseSlug: 'rolls',
74
+ icon: null,
75
+ createdAt: existing?.createdAt ?? now,
76
+ lastEditedTime: now,
77
+ };
78
+ if (existing) docs[docs.indexOf(existing)] = doc;
79
+ else docs.unshift(doc);
80
+ STATE.rolls = docs;
81
+ return { status: 'success', document: doc } as TResult;
82
+ }
83
+
84
+ throw new Error(`Tool calls unavailable in dev preview (${name}).`);
85
+ },
86
+
87
+ request: async () => {
88
+ throw new Error('Backend requests unavailable in dev preview.');
89
+ },
90
+ };
91
+
92
+ window.__NOTIS_RANDOM_RUNTIME__ = runtime;
93
+ return runtime;
94
+ }
95
+
96
+ export function setMockRoute(route: 'home' | 'history') {
97
+ if (window.__NOTIS_RANDOM_RUNTIME__) {
98
+ window.__NOTIS_RANDOM_RUNTIME__.route = currentRoute(route);
99
+ }
100
+ }
101
+
102
+ function currentRoute(route: 'home' | 'history') {
103
+ if (route === 'history') {
104
+ return {
105
+ slug: 'history',
106
+ path: '/history',
107
+ name: 'History',
108
+ icon: null,
109
+ parentSlug: null,
110
+ default: false,
111
+ collection: {
112
+ database: 'rolls',
113
+ titleProperty: 'Value',
114
+ parentProperty: null,
115
+ sidebar: { mode: 'flat-list' as const, allowCreate: false },
116
+ },
117
+ };
118
+ }
119
+ return {
120
+ slug: 'home',
121
+ path: '/',
122
+ name: 'Generator',
123
+ icon: 'phosphor:sparkle',
124
+ parentSlug: null,
125
+ default: true,
126
+ collection: null,
127
+ };
128
+ }
@@ -0,0 +1,43 @@
1
+ import type { Config } from 'tailwindcss';
2
+ import tailwindAnimate from 'tailwindcss-animate';
3
+
4
+ const config: Config = {
5
+ darkMode: ['class'],
6
+ content: ['./app/**/*.{ts,tsx}', './lib/**/*.{ts,tsx}'],
7
+ theme: {
8
+ extend: {
9
+ colors: {
10
+ border: 'hsl(var(--border))',
11
+ background: 'hsl(var(--background))',
12
+ foreground: 'hsl(var(--foreground))',
13
+ muted: {
14
+ DEFAULT: 'hsl(var(--muted))',
15
+ foreground: 'hsl(var(--muted-foreground))',
16
+ },
17
+ primary: {
18
+ DEFAULT: 'hsl(var(--primary))',
19
+ foreground: 'hsl(var(--primary-foreground))',
20
+ },
21
+ accent: {
22
+ DEFAULT: 'hsl(var(--accent))',
23
+ foreground: 'hsl(var(--accent-foreground))',
24
+ },
25
+ card: {
26
+ DEFAULT: 'hsl(var(--card))',
27
+ foreground: 'hsl(var(--card-foreground))',
28
+ },
29
+ },
30
+ borderRadius: {
31
+ lg: 'var(--radius)',
32
+ md: 'calc(var(--radius) - 2px)',
33
+ sm: 'calc(var(--radius) - 4px)',
34
+ },
35
+ fontFamily: {
36
+ mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'monospace'],
37
+ },
38
+ },
39
+ },
40
+ plugins: [tailwindAnimate],
41
+ };
42
+
43
+ export default config;
@@ -0,0 +1,23 @@
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
+ "preserveSymlinks": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "react-jsx",
16
+ "types": ["vite/client", "node"],
17
+ "paths": {
18
+ "@/*": ["./*"]
19
+ }
20
+ },
21
+ "include": ["**/*.ts", "**/*.tsx"],
22
+ "exclude": ["node_modules", "dist"]
23
+ }
@@ -0,0 +1,11 @@
1
+ import { notisViteConfig } from '@notis/sdk/vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import { defineConfig, type UserConfig } from 'vite';
4
+ import appConfig from './notis.config';
5
+
6
+ const libConfig = notisViteConfig(appConfig) as unknown as UserConfig;
7
+
8
+ export default defineConfig({
9
+ ...libConfig,
10
+ plugins: [react(), ...((libConfig.plugins as UserConfig['plugins']) ?? [])],
11
+ });
@@ -0,0 +1,36 @@
1
+ {
2
+ "source": "apps/*/notis.config.ts",
3
+ "scaffolds": [
4
+ {
5
+ "slug": "notis-database",
6
+ "name": "Databases",
7
+ "description": "Read-only catalog and schema explorer for every database in your Notis workspace.",
8
+ "icon": "phosphor:database",
9
+ "categories": [
10
+ "Product & Engineering",
11
+ "Operations"
12
+ ],
13
+ "tagline": "Explore every database and schema in your Notis workspace."
14
+ },
15
+ {
16
+ "slug": "notis-notes",
17
+ "name": "Notis Notes",
18
+ "description": "Browse notes by documents, table, or calendar with folders in the sidebar.",
19
+ "icon": "phosphor:note-pencil",
20
+ "categories": [
21
+ "Productivity"
22
+ ],
23
+ "tagline": "Folder-based notes with gallery, table, and calendar views."
24
+ },
25
+ {
26
+ "slug": "notis-random",
27
+ "name": "Random Number Generator",
28
+ "description": "Generate random numbers with configurable bounds, and keep a history of everything you rolled.",
29
+ "icon": "phosphor:dice-five",
30
+ "categories": [
31
+ "Personal"
32
+ ],
33
+ "tagline": "Roll dice, keep history."
34
+ }
35
+ ]
36
+ }
package/package.json CHANGED
@@ -1,21 +1,26 @@
1
1
  {
2
2
  "name": "@notis_ai/cli",
3
- "version": "0.2.1",
4
- "description": "Agent-first Notis CLI for apps, databases, and generic tool execution",
3
+ "version": "0.2.3",
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
+ "config/",
12
+ "dist/",
11
13
  "src/",
14
+ "skills/",
12
15
  "template/",
13
16
  "README.md"
14
17
  ],
15
18
  "scripts": {
19
+ "build": "node ./scripts/build-scaffolds.js",
16
20
  "docs:generate": "node ./scripts/generate-docs.js",
17
21
  "docs:check": "node ./scripts/generate-docs.js --check",
18
- "release:prepare": "node ./scripts/prepare-publish.js --apply",
22
+ "prepack": "npm run build",
23
+ "release:prepare": "npm run build && node ./scripts/prepare-publish.js --apply",
19
24
  "cli:set-mode": "node ./scripts/set-cli-mode.js",
20
25
  "test": "node --test"
21
26
  },
@@ -0,0 +1,162 @@
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
+ Run the Notis CLI through NPX, for example `npx --package @notis_ai/cli@latest -- notis apps list`. Notis Desktop keeps the CLI auth profile current. This `notis-apps` skill is delivered through normal Notis skill sync for the signed-in user, alongside other curated skills.
11
+
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
+ npx --package @notis_ai/cli@latest -- notis apps init
27
+ npx --package @notis_ai/cli@latest -- notis apps dev
28
+ npx --package @notis_ai/cli@latest -- notis apps build
29
+ npx --package @notis_ai/cli@latest -- notis apps verify
30
+ npx --package @notis_ai/cli@latest -- notis apps create
31
+ npx --package @notis_ai/cli@latest -- notis apps link
32
+ npx --package @notis_ai/cli@latest -- notis apps pull
33
+ npx --package @notis_ai/cli@latest -- notis apps deploy
34
+ npx --package @notis_ai/cli@latest -- notis apps doctor
35
+ ```
36
+
37
+ Use this command form everywhere:
38
+
39
+ ```bash
40
+ npx --package @notis_ai/cli@latest -- notis apps ...
41
+ ```
42
+
43
+ ## Hard Rules
44
+
45
+ - Use React + Vite only. Do not use Next.js or a custom server for app projects.
46
+ - Build standard pages in `app/`. Do not write raw `views/<slug>/index.js` files.
47
+ - Keep navigation in `notis.config.ts` `routes`; each route needs a stable `slug`.
48
+ - Reference existing databases by slug in `notis.config.ts`; do not assume deploy creates databases automatically.
49
+ - Declare runtime tool access in `notis.config.ts` `tools`.
50
+ - Use `@notis/sdk` hooks instead of direct runtime access.
51
+ - Do not rely on `window.__NOTIS_RUNTIME__` or portal-owned DOM hooks.
52
+ - `npx --package @notis_ai/cli@latest -- notis apps deploy` updates an installed app. It does not publish to the public App Store.
53
+ - Public or team App Store publishing is a Portal review flow.
54
+ - Use Phosphor icon names with the `phosphor:` prefix. Do not use emoji icons.
55
+ - Prefer scaffolded shadcn components and portal theme tokens over custom visual systems.
56
+
57
+ ## App Workflow
58
+
59
+ 1. Discover existing apps if needed:
60
+
61
+ ```bash
62
+ npx --package @notis_ai/cli@latest -- notis apps list
63
+ ```
64
+
65
+ 2. Start from a scaffold or pull an existing app:
66
+
67
+ ```bash
68
+ npx --package @notis_ai/cli@latest -- notis apps scaffolds list
69
+ npx --package @notis_ai/cli@latest -- notis apps init "My App"
70
+ npx --package @notis_ai/cli@latest -- notis apps pull <app-id> ./my-app
71
+ ```
72
+
73
+ 3. Install dependencies and develop:
74
+
75
+ ```bash
76
+ npm install
77
+ npx --package @notis_ai/cli@latest -- notis apps dev
78
+ ```
79
+
80
+ 4. Build and verify:
81
+
82
+ ```bash
83
+ npx --package @notis_ai/cli@latest -- notis apps build
84
+ npx --package @notis_ai/cli@latest -- notis apps verify
85
+ ```
86
+
87
+ 5. Create or link the remote app, then deploy:
88
+
89
+ ```bash
90
+ npx --package @notis_ai/cli@latest -- notis apps create "My App" .
91
+ npx --package @notis_ai/cli@latest -- notis apps deploy
92
+ ```
93
+
94
+ or:
95
+
96
+ ```bash
97
+ npx --package @notis_ai/cli@latest -- notis apps link <app-id>
98
+ npx --package @notis_ai/cli@latest -- notis apps deploy
99
+ ```
100
+
101
+ ## App Structure
102
+
103
+ Expected project shape:
104
+
105
+ ```text
106
+ notis.config.ts
107
+ vite.config.ts
108
+ app/
109
+ components/
110
+ metadata/
111
+ .notis/output/
112
+ ```
113
+
114
+ `notis.config.ts` owns:
115
+
116
+ - app metadata: name, title, description, icon, categories, tagline
117
+ - database slug references
118
+ - route definitions
119
+ - tool access
120
+
121
+ Example:
122
+
123
+ ```ts
124
+ import { defineNotisApp } from '@notis/sdk/config';
125
+
126
+ export default defineNotisApp({
127
+ name: 'my-app',
128
+ title: 'My App',
129
+ description: 'A focused Notis app.',
130
+ icon: 'phosphor:squares-four',
131
+ databases: ['tasks'],
132
+ routes: [
133
+ {
134
+ path: '/',
135
+ slug: 'tasks',
136
+ name: 'Tasks',
137
+ icon: 'phosphor:check-square',
138
+ default: true,
139
+ },
140
+ ],
141
+ tools: ['LOCAL_NOTIS_DATABASE_QUERY', 'LOCAL_NOTIS_DATABASE_UPSERT_TASKS'],
142
+ });
143
+ ```
144
+
145
+ ## Runtime Usage
146
+
147
+ Use SDK hooks for data and tool access:
148
+
149
+ ```tsx
150
+ import { useTool } from '@notis/sdk';
151
+
152
+ const query = useTool('LOCAL_NOTIS_DATABASE_QUERY');
153
+ ```
154
+
155
+ 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.
156
+
157
+ ## Troubleshooting
158
+
159
+ - If deploy fails with backend connectivity errors, run `npx --package @notis_ai/cli@latest -- notis apps doctor` and check the configured API base.
160
+ - If editing an existing app, use `npx --package @notis_ai/cli@latest -- notis apps pull <app-id>` when source snapshots are available.
161
+ - If the Portal shows a stale bundle, hard refresh the Portal or deploy a new version.
162
+ - If an app route fails to load, run `npx --package @notis_ai/cli@latest -- notis apps verify` and inspect `.notis/output/manifest.json`.
@@ -0,0 +1,208 @@
1
+ # Notis CLI — App Development Workflow
2
+
3
+ When running outside the Notis container, use the `notis` CLI to work with Notis Apps locally.
4
+
5
+ Notis apps are Vite + React projects using `@notis/sdk`. The workflow is init, dev, build, verify, create/link, pull, deploy, and doctor.
6
+
7
+ Important: `notis apps deploy` updates the linked installed app. It is not an app-store publishing flow.
8
+
9
+ ## Setup
10
+
11
+ Install and sign into Notis Desktop to keep the CLI profile current. Run the CLI through NPX, for example `npx --package @notis_ai/cli@latest -- notis apps list`. Related skills such as `notis-cli` and `notis-apps` are delivered through normal Notis skill sync for the signed-in user.
12
+
13
+ For CI, hosted agents, or internal scripts, pass a non-persisted token with `NOTIS_JWT=<token>` and use `--api-base <server-url>` when targeting a non-default server.
14
+
15
+ ## Core Workflow
16
+
17
+ 1. Scaffold a new app:
18
+
19
+ ```bash
20
+ npx --package @notis_ai/cli@latest -- notis apps init
21
+ ```
22
+
23
+ 2. Or pull an installed app's saved source snapshot:
24
+
25
+ ```bash
26
+ npx --package @notis_ai/cli@latest -- notis apps pull <app-id> ./my-app
27
+ cd ./my-app
28
+ npm install
29
+ ```
30
+
31
+ 3. Develop locally with live reload:
32
+
33
+ ```bash
34
+ npx --package @notis_ai/cli@latest -- notis apps dev
35
+ ```
36
+
37
+ 4. Build the production artifact:
38
+
39
+ ```bash
40
+ npx --package @notis_ai/cli@latest -- notis apps build
41
+ ```
42
+
43
+ 5. Verify the built artifact headlessly:
44
+
45
+ ```bash
46
+ npx --package @notis_ai/cli@latest -- notis apps verify
47
+ ```
48
+
49
+ 6. Link the project if it was not created or pulled from an app, then deploy:
50
+
51
+ ```bash
52
+ npx --package @notis_ai/cli@latest -- notis apps link <app-id>
53
+ npx --package @notis_ai/cli@latest -- notis apps deploy
54
+ ```
55
+
56
+ ## Commands
57
+
58
+ ### `npx --package @notis_ai/cli@latest -- notis doctor`
59
+
60
+ Run a quick CLI health check for config, auth, and API reachability.
61
+
62
+ When to use: Use this before relying on the CLI in automation or after changing environments.
63
+
64
+ Examples:
65
+ - `npx --package @notis_ai/cli@latest -- notis doctor`
66
+ - `npx --package @notis_ai/cli@latest -- notis doctor --json`
67
+
68
+ ### `npx --package @notis_ai/cli@latest -- notis apps list`
69
+
70
+ List apps the current profile can access.
71
+
72
+ When to use: Discover existing apps before linking or deploying.
73
+
74
+ Examples:
75
+ - `npx --package @notis_ai/cli@latest -- notis apps list`
76
+ - `npx --package @notis_ai/cli@latest -- notis apps list --json`
77
+
78
+ ### `npx --package @notis_ai/cli@latest -- notis apps init <name> [dir]`
79
+
80
+ Scaffold a new Notis app project.
81
+
82
+ When to use: Start a new Notis app. Use --from with a bundled scaffold when one is close to the desired app; otherwise creates the bare Vite + React project.
83
+
84
+ Options:
85
+ - `--from <slug>` — Start from a bundled scaffold listed by `notis apps scaffolds list`.
86
+
87
+ Examples:
88
+ - `npx --package @notis_ai/cli@latest -- notis apps scaffolds list`
89
+ - `npx --package @notis_ai/cli@latest -- notis apps init "Mind the Flo"`
90
+ - `npx --package @notis_ai/cli@latest -- notis apps init "My CRM" --from notis-database`
91
+ - `npx --package @notis_ai/cli@latest -- notis apps init "My App" ./my-app`
92
+
93
+ ### `npx --package @notis_ai/cli@latest -- notis apps scaffolds list`
94
+
95
+ List bundled Notis app scaffolds.
96
+
97
+ When to use: Discover the fixed scaffold catalog shipped with the CLI before starting a new app.
98
+
99
+ Examples:
100
+ - `npx --package @notis_ai/cli@latest -- notis apps scaffolds list`
101
+ - `npx --package @notis_ai/cli@latest -- notis apps init "My App" --from notis-database`
102
+
103
+ ### `npx --package @notis_ai/cli@latest -- notis apps create <name> [dir]`
104
+
105
+ Create a new remote Notis app and optionally link a local project to it.
106
+
107
+ When to use: Provision a fresh remote app before the first deploy. Pass a project directory to link it immediately.
108
+
109
+ Examples:
110
+ - `npx --package @notis_ai/cli@latest -- notis apps create "My App"`
111
+ - `npx --package @notis_ai/cli@latest -- notis apps create "My App" .`
112
+
113
+ ### `npx --package @notis_ai/cli@latest -- notis apps dev [dir]`
114
+
115
+ Develop Notis apps inside the Electron desktop Portal with automatic local bundle reloads.
116
+
117
+ When to use: Run this inside a single app or a monorepo root with apps/<name>/notis.config.ts. It discovers every app, starts the local bundle server, registers desktop-local dev sessions, and opens the Electron Portal to the local development app.
118
+
119
+ Options:
120
+ - `--port <number>` — Local bundle server port (default: 5173).
121
+ - `--no-open` — Do not auto-open the desktop Portal local development app.
122
+
123
+ Examples:
124
+ - `npx --package @notis_ai/cli@latest -- notis apps dev`
125
+ - `npx --package @notis_ai/cli@latest -- notis apps dev ./my-app`
126
+ - `npx --package @notis_ai/cli@latest -- notis apps dev ./workspace --port 5200`
127
+
128
+ ### `npx --package @notis_ai/cli@latest -- notis apps build [dir]`
129
+
130
+ Build and package the app into .notis/output/.
131
+
132
+ When to use: Prepare the app for verification or deployment.
133
+
134
+ Examples:
135
+ - `npx --package @notis_ai/cli@latest -- notis apps build`
136
+ - `npx --package @notis_ai/cli@latest -- notis apps build ./my-app`
137
+
138
+ ### `npx --package @notis_ai/cli@latest -- notis apps verify [dir]`
139
+
140
+ Headless render smoke each route in a stub-runtime harness.
141
+
142
+ When to use: After notis apps build, before deploy. Catches render-time crashes and missing runtime calls that the build step cannot detect.
143
+
144
+ Options:
145
+ - `--routes <slugs>` — Comma-separated route slugs. Default: every route in manifest.
146
+ - `--port <n>` — Loopback port. Default: auto-pick.
147
+ - `--skip-build` — Skip notis apps build; reuse existing .notis/output/.
148
+ - `--mode <mode>` — stub | live. Default stub. Live posts to /portal_views/runtime_query with the CLI JWT.
149
+ - `--no-browser` — Start the harness server and print URLs; do not drive agent-browser.
150
+ - `--keep-open` — Leave server + browser session running after report (for manual triage).
151
+
152
+ Examples:
153
+ - `npx --package @notis_ai/cli@latest -- notis apps verify`
154
+ - `npx --package @notis_ai/cli@latest -- notis apps verify --routes notes`
155
+ - `npx --package @notis_ai/cli@latest -- notis apps verify --mode live`
156
+ - `npx --package @notis_ai/cli@latest -- notis apps verify --no-browser # start the harness, drive agent-browser yourself`
157
+
158
+ ### `npx --package @notis_ai/cli@latest -- notis apps link <app-id> [dir]`
159
+
160
+ Link a local project to a remote Notis app.
161
+
162
+ When to use: Connect a local project to an existing app for deployment.
163
+
164
+ Examples:
165
+ - `npx --package @notis_ai/cli@latest -- notis apps link abc123`
166
+ - `npx --package @notis_ai/cli@latest -- notis apps link abc123 ./my-app`
167
+
168
+ ### `npx --package @notis_ai/cli@latest -- notis apps pull <app-id> [dir]`
169
+
170
+ Download a Notis app source snapshot into a local project folder.
171
+
172
+ When to use: Edit an installed app locally. Pulls the persisted source, links the directory to the app/version, then continue with npm install, notis apps dev, notis apps build, and notis apps deploy.
173
+
174
+ Options:
175
+ - `--force` — Overwrite a non-empty target directory.
176
+ - `--version <n>` — Pull a specific app source version (default: latest).
177
+
178
+ Examples:
179
+ - `npx --package @notis_ai/cli@latest -- notis apps pull abc123`
180
+ - `npx --package @notis_ai/cli@latest -- notis apps pull abc123 ./my-app --force`
181
+
182
+ ### `npx --package @notis_ai/cli@latest -- notis apps deploy [dir]`
183
+
184
+ Build and upload the app to the linked Notis app.
185
+
186
+ When to use: Ship the installed app to production for the linked user/team app. Requires a linked app (notis apps link). This command does not publish to the app store.
187
+
188
+ Options:
189
+ - `--app-id <id>` — Override linked app ID.
190
+ - `--skip-build` — Skip the build step (use existing .notis/output/).
191
+ - `--direct` — Upload directly to Supabase storage, bypassing the backend server. Auto-fallback on network errors.
192
+
193
+ Examples:
194
+ - `npx --package @notis_ai/cli@latest -- notis apps deploy`
195
+ - `npx --package @notis_ai/cli@latest -- notis apps deploy --skip-build`
196
+ - `npx --package @notis_ai/cli@latest -- notis apps deploy --app-id abc123`
197
+ - `npx --package @notis_ai/cli@latest -- notis apps deploy --direct`
198
+
199
+ ### `npx --package @notis_ai/cli@latest -- notis apps doctor [dir]`
200
+
201
+ Check project health and readiness.
202
+
203
+ When to use: Diagnose issues with a Notis app project.
204
+
205
+ Examples:
206
+ - `npx --package @notis_ai/cli@latest -- notis apps doctor`
207
+ - `npx --package @notis_ai/cli@latest -- notis apps doctor ./my-app`
208
+