@notis_ai/cli 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -5
- package/dist/scaffolds/notis-database/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-database/notis.config.ts +0 -1
- package/dist/scaffolds/notis-database/src/mock-runtime.ts +1 -0
- package/dist/scaffolds/notis-journal/CHANGELOG.md +25 -0
- package/dist/scaffolds/notis-journal/app/globals.css +37 -0
- package/dist/scaffolds/notis-journal/app/insights/page.tsx +513 -0
- package/dist/scaffolds/notis-journal/app/journal-core.tsx +362 -0
- package/dist/scaffolds/notis-journal/app/journal-ui.tsx +337 -0
- package/dist/scaffolds/notis-journal/app/layout.tsx +6 -0
- package/dist/scaffolds/notis-journal/app/page.tsx +485 -0
- package/dist/scaffolds/notis-journal/components/ui/badge.tsx +28 -0
- package/dist/scaffolds/notis-journal/components/ui/button.tsx +53 -0
- package/dist/scaffolds/notis-journal/components/ui/card.tsx +56 -0
- package/dist/scaffolds/notis-journal/components.json +20 -0
- package/dist/scaffolds/notis-journal/index.html +12 -0
- package/dist/scaffolds/notis-journal/lib/utils.ts +6 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +132 -0
- package/dist/scaffolds/notis-journal/notis.config.ts +93 -0
- package/dist/scaffolds/notis-journal/package.json +34 -0
- package/dist/scaffolds/notis-journal/packages/sdk/package.json +36 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +60 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +145 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +229 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +64 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +56 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +83 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +43 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +220 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +186 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +15 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +56 -0
- package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +15 -0
- package/dist/scaffolds/notis-journal/postcss.config.mjs +8 -0
- package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +120 -0
- package/dist/scaffolds/notis-journal/src/dev-main.tsx +58 -0
- package/dist/scaffolds/notis-journal/src/mock-runtime.ts +197 -0
- package/dist/scaffolds/notis-journal/tailwind.config.ts +58 -0
- package/dist/scaffolds/notis-journal/tsconfig.json +23 -0
- package/dist/scaffolds/notis-journal/vite.config.ts +10 -0
- package/dist/scaffolds/notis-notes/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-notes/app/page.tsx +325 -543
- package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-notes/notis.config.ts +0 -1
- package/dist/scaffolds/notis-random/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-random/notis.config.ts +0 -1
- package/dist/scaffolds/notis-random/src/mock-runtime.ts +1 -0
- package/dist/scaffolds.json +11 -0
- package/package.json +3 -3
- package/skills/notis-apps/SKILL.md +64 -6
- package/skills/notis-apps/cli.md +38 -2
- package/skills/notis-cli/SKILL.md +20 -2
- package/skills/notis-query/cli.md +1 -1
- package/src/command-specs/apps.js +529 -16
- package/src/command-specs/index.js +1 -1
- package/src/command-specs/meta.js +8 -2
- package/src/command-specs/tools.js +50 -37
- package/src/runtime/agent-browser.js +273 -1
- package/src/runtime/app-changelog.js +79 -0
- package/src/runtime/app-dev-server.js +113 -6
- package/src/runtime/app-dev-sessions.js +176 -12
- package/src/runtime/app-platform.js +200 -39
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/desktop-auth.js +93 -0
- package/src/runtime/profiles.js +37 -15
- package/src/runtime/store-screenshot.js +138 -0
- package/src/runtime/transport.js +21 -82
- package/template/.harness/index.html.tmpl +128 -6
- package/template/CHANGELOG.md +5 -0
- package/template/app/page.tsx +11 -41
- package/template/notis.config.ts +0 -1
- package/template/package-lock.json +4137 -0
- package/template/package.json +1 -0
- package/template/packages/sdk/package.json +4 -0
- package/template/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/template/packages/sdk/src/components/Markdown.tsx +60 -0
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +7 -2
- package/template/packages/sdk/src/config.ts +74 -0
- package/template/packages/sdk/src/documents.ts +229 -0
- package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/template/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/template/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/template/packages/sdk/src/hooks/useMultiSelect.ts +38 -2
- package/template/packages/sdk/src/hooks/useUpsertDocument.ts +54 -9
- package/template/packages/sdk/src/index.ts +30 -1
- package/template/packages/sdk/src/runtime.ts +76 -17
- package/template/packages/sdk/src/styles.css +148 -0
- package/dist/scaffolds/notis-database/metadata/cover.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/cover.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-random/metadata/cover.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
- package/template/metadata/cover.png +0 -0
- package/template/packages/sdk/src/hooks/useDatabase.ts +0 -76
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notis theme CSS variables.
|
|
3
|
+
*
|
|
4
|
+
* Import this in your app layout:
|
|
5
|
+
*
|
|
6
|
+
* import '@notis/sdk/styles.css';
|
|
7
|
+
*
|
|
8
|
+
* The portal injects the live Notis theme variables at render time.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
@tailwind base;
|
|
12
|
+
@tailwind components;
|
|
13
|
+
@tailwind utilities;
|
|
14
|
+
|
|
15
|
+
@layer base {
|
|
16
|
+
* {
|
|
17
|
+
@apply border-border;
|
|
18
|
+
}
|
|
19
|
+
[data-notis-app-root] {
|
|
20
|
+
color: hsl(var(--foreground));
|
|
21
|
+
display: block;
|
|
22
|
+
min-height: 100%;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@layer components {
|
|
27
|
+
.notis-app-shell {
|
|
28
|
+
@apply mx-auto w-full max-w-[var(--portal-page-max-width)] px-4 py-6 md:px-4 md:py-8;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.notis-app-surface {
|
|
32
|
+
@apply rounded-2xl border border-border bg-card text-card-foreground shadow-sm;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.notis-app-section {
|
|
36
|
+
@apply p-5 md:p-6;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* Markdown value-type rendering (see the `Markdown` component). Plain CSS on
|
|
42
|
+
* theme variables so it works inside the portal shadow root and the dev
|
|
43
|
+
* harness without depending on the app's Tailwind content scan.
|
|
44
|
+
*/
|
|
45
|
+
.notis-markdown {
|
|
46
|
+
font-size: 14px;
|
|
47
|
+
line-height: 1.65;
|
|
48
|
+
color: hsl(var(--foreground));
|
|
49
|
+
overflow-wrap: break-word;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.notis-markdown--sm {
|
|
53
|
+
font-size: 13px;
|
|
54
|
+
line-height: 1.55;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.notis-markdown > :first-child {
|
|
58
|
+
margin-top: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.notis-markdown > :last-child {
|
|
62
|
+
margin-bottom: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.notis-markdown p {
|
|
66
|
+
margin: 0.625em 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.notis-markdown h1,
|
|
70
|
+
.notis-markdown h2,
|
|
71
|
+
.notis-markdown h3,
|
|
72
|
+
.notis-markdown h4,
|
|
73
|
+
.notis-markdown h5,
|
|
74
|
+
.notis-markdown h6 {
|
|
75
|
+
margin: 1.25em 0 0.5em;
|
|
76
|
+
font-weight: 650;
|
|
77
|
+
letter-spacing: -0.01em;
|
|
78
|
+
line-height: 1.3;
|
|
79
|
+
color: hsl(var(--foreground));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.notis-markdown h1 { font-size: 1.5em; }
|
|
83
|
+
.notis-markdown h2 { font-size: 1.25em; }
|
|
84
|
+
.notis-markdown h3 { font-size: 1.1em; }
|
|
85
|
+
.notis-markdown h4,
|
|
86
|
+
.notis-markdown h5,
|
|
87
|
+
.notis-markdown h6 { font-size: 1em; }
|
|
88
|
+
|
|
89
|
+
.notis-markdown ul,
|
|
90
|
+
.notis-markdown ol {
|
|
91
|
+
margin: 0.625em 0;
|
|
92
|
+
padding-left: 1.5em;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.notis-markdown ul { list-style-type: disc; }
|
|
96
|
+
.notis-markdown ol { list-style-type: decimal; }
|
|
97
|
+
|
|
98
|
+
.notis-markdown li {
|
|
99
|
+
margin: 0.25em 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.notis-markdown li > ul,
|
|
103
|
+
.notis-markdown li > ol {
|
|
104
|
+
margin: 0.25em 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.notis-markdown ul.contains-task-list {
|
|
108
|
+
list-style: none;
|
|
109
|
+
padding-left: 0.375em;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.notis-markdown li.task-list-item input[type='checkbox'] {
|
|
113
|
+
margin-right: 0.5em;
|
|
114
|
+
accent-color: hsl(var(--primary));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.notis-markdown blockquote {
|
|
118
|
+
margin: 0.75em 0;
|
|
119
|
+
padding: 0.125em 0 0.125em 0.875em;
|
|
120
|
+
border-left: 3px solid hsl(var(--border));
|
|
121
|
+
color: hsl(var(--muted-foreground));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.notis-markdown a {
|
|
125
|
+
color: hsl(var(--primary));
|
|
126
|
+
text-decoration: underline;
|
|
127
|
+
text-decoration-color: hsl(var(--primary) / 0.35);
|
|
128
|
+
text-underline-offset: 2px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.notis-markdown a:hover {
|
|
132
|
+
text-decoration-color: hsl(var(--primary));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.notis-markdown code {
|
|
136
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
137
|
+
font-size: 0.875em;
|
|
138
|
+
padding: 0.125em 0.375em;
|
|
139
|
+
border-radius: 0.25rem;
|
|
140
|
+
background: hsl(var(--muted));
|
|
141
|
+
color: hsl(var(--foreground));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.notis-markdown pre {
|
|
145
|
+
margin: 0.75em 0;
|
|
146
|
+
padding: 0.75em 1em;
|
|
147
|
+
border-radius: 0.5rem;
|
|
148
|
+
background: hsl(var(--muted));
|
|
149
|
+
overflow-x: auto;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.notis-markdown pre code {
|
|
153
|
+
padding: 0;
|
|
154
|
+
background: transparent;
|
|
155
|
+
font-size: 0.875em;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.notis-markdown hr {
|
|
159
|
+
margin: 1.25em 0;
|
|
160
|
+
border: 0;
|
|
161
|
+
border-top: 1px solid hsl(var(--border));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.notis-markdown img {
|
|
165
|
+
max-width: 100%;
|
|
166
|
+
border-radius: 0.5rem;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.notis-markdown table {
|
|
170
|
+
margin: 0.75em 0;
|
|
171
|
+
width: 100%;
|
|
172
|
+
border-collapse: collapse;
|
|
173
|
+
font-size: 0.95em;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.notis-markdown th,
|
|
177
|
+
.notis-markdown td {
|
|
178
|
+
padding: 0.375em 0.75em;
|
|
179
|
+
border: 1px solid hsl(var(--border));
|
|
180
|
+
text-align: left;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.notis-markdown th {
|
|
184
|
+
background: hsl(var(--muted));
|
|
185
|
+
font-weight: 600;
|
|
186
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @notis/sdk/ui - Re-exported shadcn components for Notis apps.
|
|
3
|
+
*
|
|
4
|
+
* Apps include shadcn via their own `components.json` and `@/components/ui/*`
|
|
5
|
+
* imports. This entrypoint is provided as a convenience for apps that want to
|
|
6
|
+
* import directly from the SDK without setting up shadcn locally.
|
|
7
|
+
*
|
|
8
|
+
* For now, this is a placeholder. The scaffold template sets up shadcn
|
|
9
|
+
* directly in the app project, so components come from `@/components/ui/*`.
|
|
10
|
+
* This file can be expanded later to re-export a curated set of components
|
|
11
|
+
* pre-themed for Notis.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Placeholder -- apps use shadcn directly via their project's components/ui/
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite config builder for Notis apps.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* ```ts
|
|
6
|
+
* // vite.config.ts
|
|
7
|
+
* import { notisViteConfig } from '@notis/sdk/vite';
|
|
8
|
+
* import appConfig from './notis.config';
|
|
9
|
+
* export default notisViteConfig(appConfig);
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Produces a library-mode ES module bundle with React externalized.
|
|
13
|
+
* Output: .notis/output/bundle/app.js + app.css
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { NotisAppConfig } from './config';
|
|
17
|
+
|
|
18
|
+
export function notisViteConfig(appConfig: NotisAppConfig) {
|
|
19
|
+
return {
|
|
20
|
+
plugins: [
|
|
21
|
+
// @vitejs/plugin-react is added by the consumer's vite.config.ts
|
|
22
|
+
// or auto-detected. We provide the config shape only.
|
|
23
|
+
],
|
|
24
|
+
build: {
|
|
25
|
+
lib: {
|
|
26
|
+
entry: '.notis/_entry.tsx',
|
|
27
|
+
formats: ['es'] as const,
|
|
28
|
+
fileName: () => 'app.js',
|
|
29
|
+
},
|
|
30
|
+
rollupOptions: {
|
|
31
|
+
external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime', 'react/jsx-dev-runtime'],
|
|
32
|
+
output: {
|
|
33
|
+
globals: {
|
|
34
|
+
react: 'window.React',
|
|
35
|
+
'react-dom': 'window.ReactDOM',
|
|
36
|
+
'react-dom/client': 'window.ReactDOMClient',
|
|
37
|
+
'react/jsx-runtime': 'window.React',
|
|
38
|
+
},
|
|
39
|
+
assetFileNames: 'app[extname]',
|
|
40
|
+
inlineDynamicImports: true,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
outDir: '.notis/output/bundle',
|
|
44
|
+
emptyOutDir: true,
|
|
45
|
+
cssCodeSplit: false,
|
|
46
|
+
},
|
|
47
|
+
resolve: {
|
|
48
|
+
alias: {
|
|
49
|
+
'@': process.cwd(),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
define: {
|
|
53
|
+
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: journal-onboarding
|
|
3
|
+
description: "Set up the Journal morning and evening automations and run their check-ins. Use when onboarding someone to the Journal daily practice, changing their check-in schedule, or when a Journal automation asks to run the morning or evening check-in."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Journal Daily Practice
|
|
7
|
+
|
|
8
|
+
The Journal is a five-minute daily practice captured entirely in conversation. Notis asks, the user answers, and the `journal_entries` database fills itself — the app only displays. There are two rituals:
|
|
9
|
+
|
|
10
|
+
- **Morning check-in** — waking mood (1–7 pleasant scale + one adjective), how they're feeling, energy (1–10), motivation (1–10), three gratitudes, an intention for the day, and a daily affirmation.
|
|
11
|
+
- **Evening reflection** — whole-day mood (1–7 pleasant scale + one adjective), the day's highlight, what the day taught them, a gentle catch-up of anything the morning missed, and an optional free-form entry.
|
|
12
|
+
|
|
13
|
+
## Choose the Mode
|
|
14
|
+
|
|
15
|
+
- Use **setup mode** when the user is onboarding, asks to configure Journal check-ins, or wants to change the schedule.
|
|
16
|
+
- Use **morning-check mode** only when the request or automation prompt names it.
|
|
17
|
+
- Use **evening-check mode** only when the request or automation prompt names it.
|
|
18
|
+
- Never enter setup mode from a check-in run.
|
|
19
|
+
|
|
20
|
+
## Tone (applies to every mode)
|
|
21
|
+
|
|
22
|
+
Write like a warm, attentive companion — closer to a good therapist than to a form. Concrete rules:
|
|
23
|
+
|
|
24
|
+
- Sound like a person, not a survey. Weave the questions into one or two short messages instead of a numbered interrogation.
|
|
25
|
+
- Reference what they actually said, today or on recent days, when it helps ("this morning you said the demo had you excited — how did it go?").
|
|
26
|
+
- Never guilt-trip about missed days or missing fields. A skipped question is an answer; move on gracefully.
|
|
27
|
+
- Accept partial answers. Save what was given; the evening pass picks up morning leftovers, and anything still missing after that just stays blank.
|
|
28
|
+
- Keep it light: this is a check-in, not a medical assessment. Never diagnose, never analyze the user unprompted.
|
|
29
|
+
|
|
30
|
+
## The Pleasant Scale
|
|
31
|
+
|
|
32
|
+
Both moods use the same 1–7 scale (like Apple's State of Mind): 1 very unpleasant, 2 unpleasant, 3 slightly unpleasant, 4 neutral, 5 slightly pleasant, 6 pleasant, 7 very pleasant.
|
|
33
|
+
|
|
34
|
+
- Ask for it conversationally ("where does this morning land, 1 to 7, unpleasant to pleasant?") and store the integer.
|
|
35
|
+
- If the user answers with words only ("pretty good"), map it yourself (pretty good ≈ 5–6), confirm implicitly by restating ("logging that as a 6"), and store it.
|
|
36
|
+
- The adjective is the user's own word ("foggy", "electric", "flat") — store it lower-case in the matching `... Mood Word` property. If they give a sentence, pull the strongest adjective and keep the sentence in `Morning Feeling` (morning) or the free entry (evening).
|
|
37
|
+
|
|
38
|
+
## Writing to the Database
|
|
39
|
+
|
|
40
|
+
The native Notis database with slug `journal_entries` holds **one entry per local calendar day**:
|
|
41
|
+
|
|
42
|
+
- Find today's entry with `LOCAL_NOTIS_DATABASE_QUERY` and a structured filter on **Date** for the user's local day. Ignore archived entries. If duplicates exist, use the most recently updated one and mention the duplicate briefly; never merge or delete.
|
|
43
|
+
- Create it only if it does not exist, via `LOCAL_NOTIS_DATABASE_UPSERT_JOURNAL_ENTRIES` with `Name` = `Journal — YYYY-MM-DD` (local date) and `Date` = that day.
|
|
44
|
+
- Update by passing only the properties the user just answered. Never overwrite a field that already has a value unless the user explicitly corrects it.
|
|
45
|
+
- Properties: `Morning Mood` (1–7), `Morning Mood Word`, `Morning Feeling`, `Energy` (1–10), `Motivation` (1–10), `Gratitude 1..3`, `Intention`, `Affirmation`, `Day Mood` (1–7), `Day Mood Word`, `Highlight`, `Lesson`.
|
|
46
|
+
- The optional free-form entry goes in the document body: pass `content_markdown` (append to existing content rather than replacing it; set `replace` only when the body is empty).
|
|
47
|
+
|
|
48
|
+
## Setup Mode
|
|
49
|
+
|
|
50
|
+
### 1. Recommend the Routine
|
|
51
|
+
|
|
52
|
+
Briefly explain the two automations before asking for times:
|
|
53
|
+
|
|
54
|
+
- A **morning check-in** (recommend ~08:00) captures the waking mood, energy, motivation, gratitudes, intention, and affirmation while the day is still fresh.
|
|
55
|
+
- An **evening reflection** (recommend ~21:30) captures how the day actually felt, its highlight and lesson, catches up anything the morning missed, and offers space for a free entry.
|
|
56
|
+
|
|
57
|
+
Make clear both times are theirs to choose.
|
|
58
|
+
|
|
59
|
+
### 2. Ask for the Schedule
|
|
60
|
+
|
|
61
|
+
One compact question collecting: morning time, evening time, days of the week (default every day), timezone (unless confidently known), and delivery channel only when ambiguous. Do not create anything while any of these is unresolved.
|
|
62
|
+
|
|
63
|
+
### 3. Check Existing Setup
|
|
64
|
+
|
|
65
|
+
Use `LOCAL_NOTIS_LIST_REMINDERS` and `LOCAL_NOTIS_LIST_AUTOMATIONS` before writing anything — one full page of up to 100 items each, inspected locally, directly in the current assistant (no sub-agents, no repeated searches).
|
|
66
|
+
|
|
67
|
+
- Match existing items by purpose, not only name. Update a matching Journal morning or evening item instead of duplicating it.
|
|
68
|
+
- If an old Journal **reminder** exists from a previous version of this practice, replace it: create the morning automation and remove only that confirmed Journal reminder.
|
|
69
|
+
- Preserve unrelated reminders and automations.
|
|
70
|
+
|
|
71
|
+
### 4. Confirm Before Creating
|
|
72
|
+
|
|
73
|
+
Recap both local schedules, days, timezone, channel, and what each check-in will ask. Obtain explicit confirmation before creating or updating anything.
|
|
74
|
+
|
|
75
|
+
### 5. Create the Two Automations
|
|
76
|
+
|
|
77
|
+
Use `LOCAL_NOTIS_INSERT_AUTOMATION` (or `LOCAL_NOTIS_UPDATE_AUTOMATION` for a matching existing item) for **both** check-ins:
|
|
78
|
+
|
|
79
|
+
- Names: `Journal morning check-in` and `Journal evening reflection`.
|
|
80
|
+
- `schedule` triggers with standard five-field cron expressions derived from the confirmed local times and days. Recurrence lives only in `cron_expression`.
|
|
81
|
+
- Delivery only via `channel` and, when required, `channel_account_id`.
|
|
82
|
+
- **Enable cross-run context on both automations.** The evening reflection must see the morning conversation so it can follow up on what the user actually said — this consolidated thread is a core feature of the practice, not an option.
|
|
83
|
+
- Morning prompt, exactly:
|
|
84
|
+
|
|
85
|
+
`Run /journal-onboarding in morning-check mode: hold this morning's Journal check-in and save the answers to today's journal_entries entry.`
|
|
86
|
+
|
|
87
|
+
- Evening prompt, exactly:
|
|
88
|
+
|
|
89
|
+
`Run /journal-onboarding in evening-check mode: review today's journal_entries entry, hold the evening reflection, follow up on what the morning conversation actually said, and save the answers.`
|
|
90
|
+
|
|
91
|
+
Do not put scheduling or delivery instructions inside the prompts.
|
|
92
|
+
|
|
93
|
+
### 6. Report the Result
|
|
94
|
+
|
|
95
|
+
Return a concise summary: both local schedules, the delivery channel, created vs updated for each item, and the automation IDs or portal links. The native tools store recurring schedules as UTC cron expressions — convert from the user's IANA timezone, then verify the returned crons and the Portal-rendered local times. That read-back completes setup verification; do not launch extra agents or investigate scheduler internals.
|
|
96
|
+
|
|
97
|
+
## Morning-Check Mode
|
|
98
|
+
|
|
99
|
+
1. Find (or note the absence of) today's entry as described in *Writing to the Database*.
|
|
100
|
+
2. Open with one short, warm greeting that folds in the first questions. Cover, across at most two messages: waking mood (scale + their word), how they're feeling, energy and motivation (1–10 each), three gratitudes, what would make today great, and a daily affirmation.
|
|
101
|
+
3. If they stall on the affirmation, offer to shape one from what they just said — their words, not a canned quote.
|
|
102
|
+
4. Create or update today's entry with everything captured. Partial is fine; say nothing about the gaps (the evening picks them up).
|
|
103
|
+
5. Close in one line that reflects their intention back ("holding you to that walk — talk tonight").
|
|
104
|
+
|
|
105
|
+
## Evening-Check Mode
|
|
106
|
+
|
|
107
|
+
1. Read today's entry. Its filled fields plus the morning conversation (available through the automation's shared context) are your material.
|
|
108
|
+
2. Open by connecting to the morning — reference a real detail, then ask the evening questions. Example shape: "Evening — this morning you were grateful for the calm before the demo. Did that calm hold? How did the whole day feel, 1 to 7, and what one word would you give it?"
|
|
109
|
+
3. Cover: day mood (scale + adjective), highlight of the day, what the day taught them.
|
|
110
|
+
4. Then gently flag what the morning missed, grouped in one soft sentence ("we never got to your three gratitudes this morning — anything from today count?"). Ask only for genuinely missing fields; never re-ask populated ones.
|
|
111
|
+
5. Offer the optional free entry last, as an open door, not a task ("anything else about today you want kept? I'll write it down word for word").
|
|
112
|
+
6. Save all answers (properties + `content_markdown` for the free entry). If no entry exists at all, create it and run the whole check-in as a compact evening version.
|
|
113
|
+
7. If every field is already complete, just reflect the day back in a sentence and say goodnight — no questions.
|
|
114
|
+
|
|
115
|
+
## Guardrails
|
|
116
|
+
|
|
117
|
+
- Never create a second copy of the same automation, reminder, or daily entry.
|
|
118
|
+
- Never schedule anything without confirmed times, timezone, days, and destination.
|
|
119
|
+
- Never invent values the user did not give; never delete entries; never rewrite an existing free entry (append only).
|
|
120
|
+
- Never turn a check-in prompt into instructions to create another automation.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StrictMode, useEffect, useState } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { NotisProvider } from '@notis/sdk';
|
|
4
|
+
import AppShell from '../app/layout';
|
|
5
|
+
import JournalPage from '../app/page';
|
|
6
|
+
import InsightsPage from '../app/insights/page';
|
|
7
|
+
import { installMockRuntime } from './mock-runtime';
|
|
8
|
+
|
|
9
|
+
const runtime = installMockRuntime();
|
|
10
|
+
|
|
11
|
+
function DevPreview() {
|
|
12
|
+
const [route, setRoute] = useState<'journal' | 'insights'>('journal');
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const onNav = (e: Event) => {
|
|
16
|
+
const detail = (e as CustomEvent).detail as { path?: string };
|
|
17
|
+
if (detail?.path?.includes('insights')) setRoute('insights');
|
|
18
|
+
else if (detail?.path === '/') setRoute('journal');
|
|
19
|
+
};
|
|
20
|
+
window.addEventListener('mock-navigate', onNav);
|
|
21
|
+
return () => window.removeEventListener('mock-navigate', onNav);
|
|
22
|
+
}, []);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div data-dev-preview className="min-h-screen bg-background text-foreground">
|
|
26
|
+
<div className="flex items-center gap-2 border-b border-border bg-card px-4 py-2 text-xs">
|
|
27
|
+
<span className="font-semibold text-muted-foreground">Dev preview</span>
|
|
28
|
+
{(['journal', 'insights'] as const).map((r) => (
|
|
29
|
+
<button
|
|
30
|
+
key={r}
|
|
31
|
+
type="button"
|
|
32
|
+
onClick={() => setRoute(r)}
|
|
33
|
+
className={
|
|
34
|
+
'rounded-md px-2.5 py-1 font-medium capitalize ' +
|
|
35
|
+
(route === r ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted')
|
|
36
|
+
}
|
|
37
|
+
>
|
|
38
|
+
{r}
|
|
39
|
+
</button>
|
|
40
|
+
))}
|
|
41
|
+
</div>
|
|
42
|
+
{route === 'journal' ? <JournalPage /> : <InsightsPage />}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const root = document.getElementById('root');
|
|
48
|
+
if (!root) throw new Error('#root element missing from index.html');
|
|
49
|
+
|
|
50
|
+
createRoot(root).render(
|
|
51
|
+
<StrictMode>
|
|
52
|
+
<NotisProvider runtime={runtime}>
|
|
53
|
+
<AppShell>
|
|
54
|
+
<DevPreview />
|
|
55
|
+
</AppShell>
|
|
56
|
+
</NotisProvider>
|
|
57
|
+
</StrictMode>,
|
|
58
|
+
);
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { NotisRuntime } from '@notis/sdk';
|
|
2
|
+
|
|
3
|
+
/** Build Notion-shape property values, mirroring what the portal runtime returns. */
|
|
4
|
+
function text(content: string) {
|
|
5
|
+
return { type: 'rich_text', rich_text: [{ type: 'text', text: { content } }] };
|
|
6
|
+
}
|
|
7
|
+
function date(iso: string) {
|
|
8
|
+
return { type: 'date', date: { start: iso, end: '', timezone: null } };
|
|
9
|
+
}
|
|
10
|
+
function number(n: number) {
|
|
11
|
+
return { type: 'number', number: n };
|
|
12
|
+
}
|
|
13
|
+
function title(content: string) {
|
|
14
|
+
return { type: 'title', title: [{ type: 'text', text: { content } }] };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function dayISO(offset: number): string {
|
|
18
|
+
const d = new Date();
|
|
19
|
+
d.setDate(d.getDate() - offset);
|
|
20
|
+
d.setHours(8, 0, 0, 0);
|
|
21
|
+
return d.toISOString();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Seed = {
|
|
25
|
+
offset: number;
|
|
26
|
+
morningMood?: number;
|
|
27
|
+
morningWord?: string;
|
|
28
|
+
feeling?: string;
|
|
29
|
+
energy?: number;
|
|
30
|
+
motivation?: number;
|
|
31
|
+
gratitudes?: [string, string, string] | [string, string] | [string];
|
|
32
|
+
intention?: string;
|
|
33
|
+
affirmation?: string;
|
|
34
|
+
dayMood?: number;
|
|
35
|
+
dayWord?: string;
|
|
36
|
+
highlight?: string;
|
|
37
|
+
lesson?: string;
|
|
38
|
+
freeEntry?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const SEEDS: Seed[] = [
|
|
42
|
+
{
|
|
43
|
+
offset: 0,
|
|
44
|
+
morningMood: 6,
|
|
45
|
+
morningWord: 'rested',
|
|
46
|
+
feeling: 'Calm and a little excited about the demo this afternoon.',
|
|
47
|
+
energy: 8,
|
|
48
|
+
motivation: 9,
|
|
49
|
+
gratitudes: [
|
|
50
|
+
'A slow coffee on the balcony before anyone was awake',
|
|
51
|
+
'Camille laughing at my terrible pun last night',
|
|
52
|
+
'That my back finally feels normal again',
|
|
53
|
+
],
|
|
54
|
+
intention: 'One deep-work block on the launch page before opening messages.',
|
|
55
|
+
affirmation: 'I am allowed to do one thing at a time.',
|
|
56
|
+
// Evening not captured yet — today.
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
offset: 1,
|
|
60
|
+
morningMood: 4,
|
|
61
|
+
morningWord: 'foggy',
|
|
62
|
+
feeling: 'Slept badly, brain still booting.',
|
|
63
|
+
energy: 4,
|
|
64
|
+
motivation: 6,
|
|
65
|
+
gratitudes: [
|
|
66
|
+
'Rain on the window while working',
|
|
67
|
+
'The boulangerie was still open at 19:30',
|
|
68
|
+
'A friend checking in for no reason',
|
|
69
|
+
],
|
|
70
|
+
intention: 'Keep the day small: ship the fix, walk at lunch.',
|
|
71
|
+
affirmation: 'I am steady even on slow days.',
|
|
72
|
+
dayMood: 6,
|
|
73
|
+
dayWord: 'redeemed',
|
|
74
|
+
highlight: 'The afternoon walk turned into an hour of ideas with Théo.',
|
|
75
|
+
lesson: 'A rough morning predicts nothing about the afternoon.',
|
|
76
|
+
freeEntry:
|
|
77
|
+
'Started the day convinced it was a write-off. By 16:00 the fix was shipped and the walk with Théo unknotted the roadmap question that has been bugging me for a week. Note to self: leave the house earlier.',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
offset: 2,
|
|
81
|
+
morningMood: 7,
|
|
82
|
+
morningWord: 'sunny',
|
|
83
|
+
feeling: 'Woke up before the alarm, ready to go.',
|
|
84
|
+
energy: 9,
|
|
85
|
+
motivation: 9,
|
|
86
|
+
gratitudes: [
|
|
87
|
+
'Eight hours of sleep, finally',
|
|
88
|
+
'The new espresso beans',
|
|
89
|
+
'A clear calendar until noon',
|
|
90
|
+
],
|
|
91
|
+
intention: 'Finish the investor update and actually send it.',
|
|
92
|
+
affirmation: 'I am building something worth the patience it takes.',
|
|
93
|
+
dayMood: 7,
|
|
94
|
+
dayWord: 'unstoppable',
|
|
95
|
+
highlight: 'Sent the update and got two warm replies within the hour.',
|
|
96
|
+
lesson: 'Momentum compounds when I front-load the scary task.',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
offset: 3,
|
|
100
|
+
morningMood: 3,
|
|
101
|
+
morningWord: 'heavy',
|
|
102
|
+
feeling: 'Anxious about the support backlog.',
|
|
103
|
+
energy: 3,
|
|
104
|
+
motivation: 4,
|
|
105
|
+
gratitudes: [
|
|
106
|
+
'Tea instead of a third coffee',
|
|
107
|
+
'The cat sleeping on my desk all morning',
|
|
108
|
+
],
|
|
109
|
+
intention: 'Answer ten tickets, then stop counting.',
|
|
110
|
+
affirmation: 'I am more than my inbox.',
|
|
111
|
+
dayMood: 4,
|
|
112
|
+
dayWord: 'okay',
|
|
113
|
+
highlight: 'Closed the hardest ticket with an actual fix, not a workaround.',
|
|
114
|
+
lesson: 'Naming the dread out loud makes it about half as loud.',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
offset: 4,
|
|
118
|
+
morningMood: 5,
|
|
119
|
+
morningWord: 'curious',
|
|
120
|
+
feeling: 'Mildly under-slept but interested in the day.',
|
|
121
|
+
energy: 6,
|
|
122
|
+
motivation: 7,
|
|
123
|
+
gratitudes: [
|
|
124
|
+
'Morning light in the kitchen',
|
|
125
|
+
'A podcast that made the dishes disappear',
|
|
126
|
+
'Knowing exactly what today is for',
|
|
127
|
+
],
|
|
128
|
+
intention: 'Prototype the stats page before lunch.',
|
|
129
|
+
affirmation: 'I am learning fast enough.',
|
|
130
|
+
dayMood: 6,
|
|
131
|
+
dayWord: 'satisfying',
|
|
132
|
+
highlight: 'The prototype clicked on the third try.',
|
|
133
|
+
lesson: 'The second draft is where the good ideas live.',
|
|
134
|
+
freeEntry: 'Quiet, focused day. The kind that does not make stories but makes progress.',
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
function buildDocuments() {
|
|
139
|
+
return SEEDS.map((s, i) => {
|
|
140
|
+
const iso = dayISO(s.offset);
|
|
141
|
+
const properties: Record<string, unknown> = {
|
|
142
|
+
Name: title(`Journal — ${iso.slice(0, 10)}`),
|
|
143
|
+
Date: date(iso),
|
|
144
|
+
};
|
|
145
|
+
if (s.morningMood != null) properties['Morning Mood'] = number(s.morningMood);
|
|
146
|
+
if (s.morningWord) properties['Morning Mood Word'] = text(s.morningWord);
|
|
147
|
+
if (s.feeling) properties['Morning Feeling'] = text(s.feeling);
|
|
148
|
+
if (s.energy != null) properties['Energy'] = number(s.energy);
|
|
149
|
+
if (s.motivation != null) properties['Motivation'] = number(s.motivation);
|
|
150
|
+
(s.gratitudes ?? []).forEach((g, gi) => {
|
|
151
|
+
properties[`Gratitude ${gi + 1}`] = text(g);
|
|
152
|
+
});
|
|
153
|
+
if (s.intention) properties['Intention'] = text(s.intention);
|
|
154
|
+
if (s.affirmation) properties['Affirmation'] = text(s.affirmation);
|
|
155
|
+
if (s.dayMood != null) properties['Day Mood'] = number(s.dayMood);
|
|
156
|
+
if (s.dayWord) properties['Day Mood Word'] = text(s.dayWord);
|
|
157
|
+
if (s.highlight) properties['Highlight'] = text(s.highlight);
|
|
158
|
+
if (s.lesson) properties['Lesson'] = text(s.lesson);
|
|
159
|
+
return {
|
|
160
|
+
id: `mock_${i}`,
|
|
161
|
+
title: `Journal — ${iso.slice(0, 10)}`,
|
|
162
|
+
content_markdown: s.freeEntry ?? '',
|
|
163
|
+
created_time: iso,
|
|
164
|
+
properties,
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function installMockRuntime(): NotisRuntime {
|
|
170
|
+
const documents = buildDocuments();
|
|
171
|
+
return {
|
|
172
|
+
app: { id: 'mock', name: '5 Minutes Journal', slug: 'notis-journal', icon: 'phosphor:notebook' } as never,
|
|
173
|
+
route: { path: '/', slug: 'journal', name: 'Journal' } as never,
|
|
174
|
+
databases: [] as never,
|
|
175
|
+
context: {},
|
|
176
|
+
navigate: (payload) => {
|
|
177
|
+
// eslint-disable-next-line no-console
|
|
178
|
+
console.log('[mock navigate]', payload);
|
|
179
|
+
window.dispatchEvent(new CustomEvent('mock-navigate', { detail: payload }));
|
|
180
|
+
},
|
|
181
|
+
registerTopBarSearch: () => {},
|
|
182
|
+
setTopBarSearchValue: () => {},
|
|
183
|
+
setTopBarSearchLoading: () => {},
|
|
184
|
+
async listTools() {
|
|
185
|
+
return [];
|
|
186
|
+
},
|
|
187
|
+
async callTool(name: string) {
|
|
188
|
+
if (name === 'LOCAL_NOTIS_DATABASE_QUERY') {
|
|
189
|
+
return { documents } as never;
|
|
190
|
+
}
|
|
191
|
+
return {} as never;
|
|
192
|
+
},
|
|
193
|
+
async request() {
|
|
194
|
+
return {};
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|