mover-os 4.7.6 → 4.7.8
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/install.js +763 -70
- package/package.json +15 -3
- package/src/dashboard/build.js +1503 -0
- package/src/dashboard/dashboard.js +276 -0
- package/src/dashboard/index.js +319 -0
- package/src/dashboard/lib/activation-log.js +297 -0
- package/src/dashboard/lib/active-context-parser.js +177 -0
- package/src/dashboard/lib/agent-command.js +93 -0
- package/src/dashboard/lib/agent-detect.js +255 -0
- package/src/dashboard/lib/agent-detector.js +92 -0
- package/src/dashboard/lib/agent-session.js +462 -0
- package/src/dashboard/lib/anti-identity-detector.js +116 -0
- package/src/dashboard/lib/auto-learnings-parser.js +183 -0
- package/src/dashboard/lib/cli-usage-parser.js +92 -0
- package/src/dashboard/lib/config-parser.js +109 -0
- package/src/dashboard/lib/connect-recommender.js +131 -0
- package/src/dashboard/lib/correlations-parser.js +231 -0
- package/src/dashboard/lib/daily-note-resolver.js +211 -0
- package/src/dashboard/lib/date-utils.js +35 -0
- package/src/dashboard/lib/distribution-parser.js +78 -0
- package/src/dashboard/lib/dossier-parser.js +64 -0
- package/src/dashboard/lib/drift-history.js +83 -0
- package/src/dashboard/lib/drift-score.js +119 -0
- package/src/dashboard/lib/engine-health.js +170 -0
- package/src/dashboard/lib/engine-writer.js +1685 -0
- package/src/dashboard/lib/execution-plan.js +125 -0
- package/src/dashboard/lib/experiments-parser.js +429 -0
- package/src/dashboard/lib/feed-parser.js +294 -0
- package/src/dashboard/lib/forked-future.js +60 -0
- package/src/dashboard/lib/goal-forecast.js +304 -0
- package/src/dashboard/lib/goals-parser.js +67 -0
- package/src/dashboard/lib/health-protocols-parser.js +133 -0
- package/src/dashboard/lib/hook-activity.js +48 -0
- package/src/dashboard/lib/hook-indexer.js +169 -0
- package/src/dashboard/lib/hourly-activity-parser.js +143 -0
- package/src/dashboard/lib/identity-parser.js +85 -0
- package/src/dashboard/lib/ingestion.js +418 -0
- package/src/dashboard/lib/library-indexer-v2.js +212 -0
- package/src/dashboard/lib/library-indexer.js +105 -0
- package/src/dashboard/lib/log-activation.sh +61 -0
- package/src/dashboard/lib/memory-curator.js +97 -0
- package/src/dashboard/lib/memory-gardener.js +177 -0
- package/src/dashboard/lib/memory-gepa.js +102 -0
- package/src/dashboard/lib/memory-index.js +470 -0
- package/src/dashboard/lib/memory-rerank.js +72 -0
- package/src/dashboard/lib/memory-text.js +136 -0
- package/src/dashboard/lib/metrics-log-parser.js +76 -0
- package/src/dashboard/lib/moves-usage-parser.js +184 -0
- package/src/dashboard/lib/onboarding-forge.js +70 -0
- package/src/dashboard/lib/override-outcome-parser.js +68 -0
- package/src/dashboard/lib/override-summary.js +73 -0
- package/src/dashboard/lib/paths.js +192 -0
- package/src/dashboard/lib/pattern-manifest-loader.js +29 -0
- package/src/dashboard/lib/phantom-strategy.js +129 -0
- package/src/dashboard/lib/project-scanner.js +121 -0
- package/src/dashboard/lib/promise-wall.js +88 -0
- package/src/dashboard/lib/record-score.js +173 -0
- package/src/dashboard/lib/redaction.js +140 -0
- package/src/dashboard/lib/refusal-parser.js +44 -0
- package/src/dashboard/lib/regenerate-manifest.js +206 -0
- package/src/dashboard/lib/rewind-snapshots.js +689 -0
- package/src/dashboard/lib/roast-wall-parser.js +200 -0
- package/src/dashboard/lib/run-registry.js +226 -0
- package/src/dashboard/lib/safe-write.js +63 -0
- package/src/dashboard/lib/session-log-parser.js +145 -0
- package/src/dashboard/lib/session-time-parser.js +158 -0
- package/src/dashboard/lib/skill-index.js +171 -0
- package/src/dashboard/lib/skill-indexer.js +118 -0
- package/src/dashboard/lib/skill-recommender.js +689 -0
- package/src/dashboard/lib/strategy-parser.js +245 -0
- package/src/dashboard/lib/strategy-protocol-parser.js +135 -0
- package/src/dashboard/lib/streak-parser.js +95 -0
- package/src/dashboard/lib/suggested-now.js +254 -0
- package/src/dashboard/lib/tool-awareness.js +125 -0
- package/src/dashboard/lib/transcript-parser.js +331 -0
- package/src/dashboard/lib/vault-graph-parser.js +205 -0
- package/src/dashboard/lib/view-generator.js +163 -0
- package/src/dashboard/lib/voice-dna-parser.js +57 -0
- package/src/dashboard/lib/walkthrough-script.js +140 -0
- package/src/dashboard/lib/workflow-graph-parser.js +170 -0
- package/src/dashboard/lib/workflow-library-parser.js +146 -0
- package/src/dashboard/server.js +2024 -0
- package/src/dashboard/shortcut.js +0 -0
- package/src/dashboard/static/setup-poc.html +306 -0
- package/src/dashboard/static/walkthrough-poc.html +580 -0
- package/src/dashboard/styles.css +1201 -0
- package/src/dashboard/templates/index.html +278 -0
- package/src/dashboard/ui/dist/assets/hanken-grotesk-latin-ext-wght-normal-Dg-wlmqe.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/hanken-grotesk-latin-wght-normal-CaVRRdDk.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/hanken-grotesk-vietnamese-wght-normal-CHiFlh_0.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/hero.png +0 -0
- package/src/dashboard/ui/dist/assets/index-598CSGOZ.js +157 -0
- package/src/dashboard/ui/dist/assets/index-BP--M69H.css +1 -0
- package/src/dashboard/ui/dist/assets/index-CidzmwSW.js +34 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/src/dashboard/ui/dist/assets/mover-system.css +62 -0
- package/src/dashboard/ui/dist/assets/xterm-CqkleIqs.js +1 -0
- package/src/dashboard/ui/dist/icon.svg +4 -0
- package/src/dashboard/ui/dist/index.html +18 -0
- package/src/dashboard/ui/dist/manifest.webmanifest +1 -0
- package/src/dashboard/ui/dist/registerSW.js +1 -0
- package/src/dashboard/ui/dist/sw.js +1 -0
- package/src/dashboard/ui/dist/workbox-39fa566e.js +1 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Mover OS · Walkthrough</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600&family=Newsreader:opsz,wght@6..72,400;6..72,500&display=swap" rel="stylesheet">
|
|
10
|
+
<!-- Shared Mover OS tokens — canonical values live in styles/v6/v3.css.
|
|
11
|
+
The /assets/ path is served from ui/dist/assets/ by server.js. -->
|
|
12
|
+
<link rel="stylesheet" href="/assets/mover-system.css">
|
|
13
|
+
<style>
|
|
14
|
+
* { box-sizing: border-box; }
|
|
15
|
+
body { margin: 0; background: var(--m-bg); color: var(--m-ink);
|
|
16
|
+
font: 15px/1.6 var(--m-sans); -webkit-font-smoothing: antialiased; }
|
|
17
|
+
/* Warm wash identical to setup-poc.html — same atmosphere, different page. */
|
|
18
|
+
body::before { content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
|
|
19
|
+
background: radial-gradient(70% 50% at 82% 0%, var(--m-gold-glow), transparent 60%);
|
|
20
|
+
opacity: 0.5; }
|
|
21
|
+
|
|
22
|
+
.wrap { max-width: 680px; margin: 0 auto; padding: 32px 22px 116px; }
|
|
23
|
+
|
|
24
|
+
/* Header: brand left, status right (mirrors setup-poc.html exactly). */
|
|
25
|
+
header { display: flex; align-items: center; justify-content: space-between;
|
|
26
|
+
padding: 4px 0 22px; border-bottom: 1px solid var(--m-line); margin-bottom: 26px; }
|
|
27
|
+
.brand { font-family: var(--m-serif); font-size: 23px; color: var(--m-ink); }
|
|
28
|
+
.brand small { font-family: var(--m-sans); color: var(--m-ink-3); font-size: 12px;
|
|
29
|
+
margin-left: 11px; letter-spacing: .04em; }
|
|
30
|
+
.status { display: flex; align-items: center; gap: 8px; color: var(--m-ink-2);
|
|
31
|
+
font-size: 12px; font-variant-numeric: tabular-nums; }
|
|
32
|
+
.dot { width: 8px; height: 8px; border-radius: 50%; background: #4a4742; }
|
|
33
|
+
.dot.live { background: var(--m-good); box-shadow: 0 0 8px rgba(169,191,130,0.55); }
|
|
34
|
+
|
|
35
|
+
/* Progress bar — the walkthrough's key UI addition over setup: the user sees
|
|
36
|
+
how far through the 8 steps they are. Thin, gold, no percentage text. */
|
|
37
|
+
.progress-rail { height: 2px; background: var(--m-line); border-radius: 2px;
|
|
38
|
+
margin-bottom: 26px; overflow: hidden; }
|
|
39
|
+
.progress-fill { height: 100%; background: linear-gradient(90deg, var(--m-gold), var(--m-gold-2));
|
|
40
|
+
border-radius: 2px; width: 0%; transition: width .4s var(--m-ease); }
|
|
41
|
+
|
|
42
|
+
/* Step card: slightly wider left accent instead of border-color change — it
|
|
43
|
+
differentiates from the setup Q card while using the same base .card class. */
|
|
44
|
+
.card { background: var(--m-card); border: 1px solid var(--m-line); border-radius: 16px;
|
|
45
|
+
padding: 18px 20px; margin: 14px 0;
|
|
46
|
+
box-shadow: 0 1px 0 rgba(255,240,214,0.05) inset, 0 30px 60px -34px rgba(0,0,0,0.9);
|
|
47
|
+
animation: rise .24s var(--m-ease); }
|
|
48
|
+
.card.step { border-left: 3px solid var(--m-gold-line); }
|
|
49
|
+
.card.step.active { border-left-color: var(--m-gold); }
|
|
50
|
+
.card.step.done { border-left-color: var(--m-good); opacity: .7; }
|
|
51
|
+
|
|
52
|
+
/* Step anatomy: badge + title + body + doThis strip. */
|
|
53
|
+
.step-badge { font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
|
|
54
|
+
color: var(--m-ink-3); margin-bottom: 6px; }
|
|
55
|
+
.step-badge span { color: var(--m-gold-2); font-weight: 600; }
|
|
56
|
+
.step-title { font-family: var(--m-serif); font-size: 20px; line-height: 1.3;
|
|
57
|
+
color: var(--m-ink); margin-bottom: 8px; }
|
|
58
|
+
.step-body { color: var(--m-ink-2); font-size: 14px; line-height: 1.6; }
|
|
59
|
+
.step-do { margin-top: 14px; padding: 12px 14px; border-radius: 10px;
|
|
60
|
+
background: var(--m-gold-soft); border: 1px solid var(--m-gold-line);
|
|
61
|
+
font-size: 13px; color: var(--m-gold-2); }
|
|
62
|
+
.step-do::before { content: "Do this · "; font-weight: 600;
|
|
63
|
+
letter-spacing: .04em; color: var(--m-gold); }
|
|
64
|
+
.check-icon { float: right; color: var(--m-good); font-size: 16px; line-height: 1; margin-left: 8px; }
|
|
65
|
+
|
|
66
|
+
/* Agent response bubble — same .q style as setup-poc.html. */
|
|
67
|
+
.q { font-family: var(--m-serif); font-size: 19px; line-height: 1.45; color: var(--m-ink); }
|
|
68
|
+
.a { color: var(--m-ink-2); font-size: 14px; margin-top: 6px; }
|
|
69
|
+
|
|
70
|
+
/* Permission card (defensive — walkthrough is tool-free but we mirror setup). */
|
|
71
|
+
.perm { border-color: var(--m-gold-line); }
|
|
72
|
+
.perm .lbl { color: var(--m-gold-2); font-size: 10px; text-transform: uppercase;
|
|
73
|
+
letter-spacing: .14em; margin-bottom: 8px; }
|
|
74
|
+
.row { display: flex; gap: 10px; margin-top: 16px; }
|
|
75
|
+
|
|
76
|
+
/* Composer — fixed bottom, exact clone of setup-poc.html. */
|
|
77
|
+
button { font: inherit; border: 1px solid var(--m-line-2); background: rgba(255,240,214,0.03);
|
|
78
|
+
color: var(--m-ink); padding: 10px 17px; border-radius: 11px; cursor: pointer;
|
|
79
|
+
transition: border-color .15s, background .15s; }
|
|
80
|
+
button:hover:not(:disabled) { border-color: var(--m-gold-line); }
|
|
81
|
+
button.primary { background: linear-gradient(180deg, var(--m-gold-2), var(--m-gold));
|
|
82
|
+
color: var(--m-on-gold); border-color: transparent; font-weight: 600;
|
|
83
|
+
box-shadow: 0 0 24px -8px var(--m-gold-glow); }
|
|
84
|
+
button:disabled { opacity: .4; cursor: default; box-shadow: none; }
|
|
85
|
+
.composer { position: fixed; left: 0; right: 0; bottom: 0;
|
|
86
|
+
background: linear-gradient(transparent, var(--m-bg) 34%); padding: 18px 22px; }
|
|
87
|
+
.composer .inner { max-width: 680px; margin: 0 auto; display: flex; gap: 10px; }
|
|
88
|
+
textarea { flex: 1; background: var(--m-bg-2); border: 1px solid var(--m-line-2);
|
|
89
|
+
border-radius: 12px; color: var(--m-ink); padding: 13px; font: inherit; resize: none; height: 50px; }
|
|
90
|
+
textarea:focus { outline: none; border-color: var(--m-gold-line); }
|
|
91
|
+
|
|
92
|
+
/* Done state — shown when the last step is confirmed. */
|
|
93
|
+
.done-banner { text-align: center; padding: 48px 0 24px; }
|
|
94
|
+
.done-banner .headline { font-family: var(--m-serif); font-size: 28px; color: var(--m-ink);
|
|
95
|
+
margin-bottom: 10px; }
|
|
96
|
+
.done-banner .sub { color: var(--m-ink-2); font-size: 15px; max-width: 440px;
|
|
97
|
+
margin: 0 auto 28px; line-height: 1.6; }
|
|
98
|
+
.done-banner .cta { display: inline-flex; align-items: center; gap: 8px;
|
|
99
|
+
background: linear-gradient(180deg, var(--m-gold-2), var(--m-gold));
|
|
100
|
+
color: var(--m-on-gold); border: none; padding: 14px 28px; border-radius: 13px;
|
|
101
|
+
font-size: 15px; font-weight: 600; cursor: pointer;
|
|
102
|
+
box-shadow: 0 0 32px -8px var(--m-gold-glow); }
|
|
103
|
+
|
|
104
|
+
/* Thinking indicator — identical to setup-poc.html: gold orb + rotating verbs. */
|
|
105
|
+
.thinking { display: flex; align-items: center; gap: 14px; }
|
|
106
|
+
.thinking .orb { position: relative; width: 22px; height: 22px; flex: none; }
|
|
107
|
+
.thinking .orb span { position: absolute; inset: 0; border-radius: 50%;
|
|
108
|
+
border: 1.5px solid var(--m-gold-line); animation: ripple 1.9s var(--m-ease) infinite; }
|
|
109
|
+
.thinking .orb span:nth-child(2) { animation-delay: .63s; }
|
|
110
|
+
.thinking .orb span:nth-child(3) { animation-delay: 1.26s; }
|
|
111
|
+
.thinking .orb::after { content: ""; position: absolute; inset: 7px; border-radius: 50%;
|
|
112
|
+
background: var(--m-gold); box-shadow: 0 0 12px -1px var(--m-gold-glow);
|
|
113
|
+
animation: breathe 1.9s var(--m-ease) infinite; }
|
|
114
|
+
.thinking .verb { font-family: var(--m-serif); font-size: 18px; color: var(--m-gold-2); }
|
|
115
|
+
.thinking .verb em { font-style: normal; opacity: .55; animation: dots 1.4s ease-in-out infinite; }
|
|
116
|
+
.thinking .verb.swap { animation: fade .7s var(--m-ease); }
|
|
117
|
+
|
|
118
|
+
@keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
|
119
|
+
@keyframes ripple { from { transform: scale(.5); opacity: .85; } to { transform: scale(1.4); opacity: 0; } }
|
|
120
|
+
@keyframes breathe { 0%,100% { opacity: .5; transform: scale(.82); } 50% { opacity: 1; transform: scale(1.1); } }
|
|
121
|
+
@keyframes dots { 0%,100% { opacity: .25; } 50% { opacity: .7; } }
|
|
122
|
+
@keyframes fade { from { opacity: 0; transform: translateY(2px); } to { opacity: 1; transform: none; } }
|
|
123
|
+
@media (prefers-reduced-motion: reduce) {
|
|
124
|
+
.thinking .orb span, .thinking .orb::after, .thinking .verb em, .thinking .verb.swap,
|
|
125
|
+
.progress-fill { animation: none; transition: none; }
|
|
126
|
+
.thinking .orb::after { opacity: .85; }
|
|
127
|
+
}
|
|
128
|
+
</style>
|
|
129
|
+
</head>
|
|
130
|
+
<body>
|
|
131
|
+
<div class="wrap">
|
|
132
|
+
<header>
|
|
133
|
+
<div class="brand">Mover OS<small>Walkthrough · local</small></div>
|
|
134
|
+
<div class="status"><span class="dot" id="dot"></span><span id="st">connecting…</span></div>
|
|
135
|
+
</header>
|
|
136
|
+
<!-- Progress rail: thin gold fill advances with each step completion. -->
|
|
137
|
+
<div class="progress-rail"><div class="progress-fill" id="prog"></div></div>
|
|
138
|
+
<!-- Feed: step cards + agent response cards append here in order. -->
|
|
139
|
+
<div id="feed"></div>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="composer"><div class="inner">
|
|
142
|
+
<textarea id="inp" placeholder="Your response…" disabled></textarea>
|
|
143
|
+
<button class="primary" id="send" disabled>Send</button>
|
|
144
|
+
<button id="endBtn" disabled>End</button>
|
|
145
|
+
</div></div>
|
|
146
|
+
|
|
147
|
+
<script>
|
|
148
|
+
// ── Walkthrough step data — kept inline so this page is a standalone file
|
|
149
|
+
// with no build step. Server-side authoritative copy: lib/walkthrough-script.js.
|
|
150
|
+
// SYNC RULE: when walkthrough-script.js STEPS changes, mirror it here.
|
|
151
|
+
const STEPS = [
|
|
152
|
+
{
|
|
153
|
+
id: "welcome", step: 1,
|
|
154
|
+
title: "You're set up. Now let's operate.",
|
|
155
|
+
body: "Setup extracted who you are and built your Engine. This walkthrough teaches you how to RUN it. Eight steps, eight minutes. No reading — only doing.",
|
|
156
|
+
doThis: "Type \"ready\" to begin.",
|
|
157
|
+
checkpoint: "User says they are ready or asks a question. Either counts — they are engaged.",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
id: "first-workflow", step: 2,
|
|
161
|
+
title: "Drive the system with workflows.",
|
|
162
|
+
body: "This is not a chatbot. You run commands. The core command is /morning — it loads your Engine files, checks your energy, and sets your Single Test for the session. Without /morning, you are flying blind.",
|
|
163
|
+
doThis: "Open a Claude Code session (a terminal, type `claude`). Run: /morning",
|
|
164
|
+
checkpoint: "User confirms they ran /morning, or asks where Claude Code is.",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: "one-chat", step: 3,
|
|
168
|
+
title: "One session. Not ten tabs.",
|
|
169
|
+
body: "Every new Claude Code window starts from zero — no memory of the last one. Your Engine files ARE the persistent memory. Opening a new window mid-task means re-loading all that context, which costs tokens and minutes.",
|
|
170
|
+
doThis: "For today: stay in the Claude Code session where you ran /morning. Do not open a new window unless you run /morning first.",
|
|
171
|
+
checkpoint: "User acknowledges the habit. No file action needed.",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "compact", step: 4,
|
|
175
|
+
title: "Compact before context breaks down.",
|
|
176
|
+
body: "After a long session, responses get shallower — the model is losing the thread. /compact compresses the conversation while re-loading your Engine context. Not 'start over' — 'keep going with a clear head'. Use it when responses feel off.",
|
|
177
|
+
doThis: "In your Claude Code session: type /compact. If the session is short, run it anyway so you know what it does.",
|
|
178
|
+
checkpoint: "User ran /compact, or confirms they understand when to use it.",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: "verify-files", step: 5,
|
|
182
|
+
title: "The files are the truth. Not the chat.",
|
|
183
|
+
body: "The chat transcript is temporary. Every workflow writes its output to a file — Daily Note, Active_Context.md, plan.md. If it's only in the chat, it's gone when you close the session. Always verify: did that land in a file?",
|
|
184
|
+
doThis: "Open Obsidian. Navigate to 02_Areas/Engine/. Confirm Active_Context.md exists and has content from setup.",
|
|
185
|
+
checkpoint: "User confirms they saw Active_Context.md with content. That is proof the system is working.",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: "discover", step: 6,
|
|
189
|
+
title: "Every workflow tells you what comes next.",
|
|
190
|
+
body: "You do not need to memorize 25 commands. Every workflow ends with a handoff — a specific suggestion for what to run next. Today that chain is: /morning → work → /log → /analyse-day → /plan-tomorrow. You ran /morning. /log is next.",
|
|
191
|
+
doThis: "In your Claude Code session: type /log — the end-of-session capture. Read its output and note what it suggests you run after.",
|
|
192
|
+
checkpoint: "User ran /log and reports what it suggested. Any answer is correct — they have seen the handoff pattern live.",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "daily-loop", step: 7,
|
|
196
|
+
title: "The loop that keeps the system alive.",
|
|
197
|
+
body: "Run this every day and the system compounds. Skip it and it decays. The loop: /morning to start → work → /log to capture → /analyse-day to audit → /plan-tomorrow to set tomorrow. This is not optional ceremony. This is the product.",
|
|
198
|
+
doThis: "Say the loop back from memory — just the five steps.",
|
|
199
|
+
checkpoint: "User gives some version of: morning, work, log, analyse-day, plan-tomorrow.",
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
id: "first-plan", step: 8,
|
|
203
|
+
title: "Close the loop before you leave.",
|
|
204
|
+
body: "Do not walk away without a plan for tomorrow. /plan-tomorrow pulls from your strategy, backlogs, and today's work. It gives you a clear first task for tomorrow morning. This is the bridge from 'I installed this' to 'I am actually using this'.",
|
|
205
|
+
doThis: "In your Claude Code session: type /plan-tomorrow. Confirm you have a plan for tomorrow when it finishes.",
|
|
206
|
+
checkpoint: "User confirms they ran /plan-tomorrow and have a plan for tomorrow. Done-gate.",
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
const STEP_COUNT = STEPS.length;
|
|
210
|
+
|
|
211
|
+
// ── DOM refs ──────────────────────────────────────────────────────────────────
|
|
212
|
+
const feed = document.getElementById('feed');
|
|
213
|
+
const inp = document.getElementById('inp');
|
|
214
|
+
const sendBtn = document.getElementById('send');
|
|
215
|
+
const endBtn = document.getElementById('endBtn');
|
|
216
|
+
const dot = document.getElementById('dot');
|
|
217
|
+
const st = document.getElementById('st');
|
|
218
|
+
const prog = document.getElementById('prog');
|
|
219
|
+
|
|
220
|
+
// ── State ─────────────────────────────────────────────────────────────────────
|
|
221
|
+
let token = null, sessionId = null, es = null, busy = false;
|
|
222
|
+
let currentStepIndex = 0; // 0-based index into STEPS
|
|
223
|
+
let completedSteps = new Set();
|
|
224
|
+
|
|
225
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
226
|
+
function card(cls) {
|
|
227
|
+
const d = document.createElement('div');
|
|
228
|
+
d.className = 'card ' + (cls || '');
|
|
229
|
+
feed.appendChild(d);
|
|
230
|
+
window.scrollTo(0, document.body.scrollHeight);
|
|
231
|
+
return d;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function setBusy(b) {
|
|
235
|
+
busy = b;
|
|
236
|
+
sendBtn.disabled = b || !sessionId;
|
|
237
|
+
inp.disabled = b || !sessionId;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function setStatus(text, live) {
|
|
241
|
+
st.textContent = text;
|
|
242
|
+
dot.className = 'dot' + (live ? ' live' : '');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function setProgress(stepsDone) {
|
|
246
|
+
prog.style.width = Math.round((stepsDone / STEP_COUNT) * 100) + '%';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function post(path, body) {
|
|
250
|
+
const r = await fetch(path, {
|
|
251
|
+
method: 'POST',
|
|
252
|
+
headers: { 'Content-Type': 'application/json', 'X-Mover-Token': token },
|
|
253
|
+
body: JSON.stringify(body),
|
|
254
|
+
});
|
|
255
|
+
return r.json();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ── Thinking indicator — identical to setup-poc.html ─────────────────────────
|
|
259
|
+
const THINK_VERBS = [
|
|
260
|
+
"Following your lead", "Checking that off", "Noting it down",
|
|
261
|
+
"Moving you forward", "Reading your progress", "Advancing the step",
|
|
262
|
+
"Tracking that", "Lining up the next one", "Good — processing",
|
|
263
|
+
"Marking it done", "Pulling up the next step", "Almost there",
|
|
264
|
+
];
|
|
265
|
+
let thinkingEl = null, thinkTimer = null, lastVerb = -1;
|
|
266
|
+
|
|
267
|
+
function nextVerb() {
|
|
268
|
+
let i;
|
|
269
|
+
do { i = Math.floor(Math.random() * THINK_VERBS.length); }
|
|
270
|
+
while (i === lastVerb && THINK_VERBS.length > 1);
|
|
271
|
+
lastVerb = i;
|
|
272
|
+
return THINK_VERBS[i];
|
|
273
|
+
}
|
|
274
|
+
function paintVerb() {
|
|
275
|
+
if (!thinkingEl) return;
|
|
276
|
+
const v = thinkingEl.querySelector('.verb');
|
|
277
|
+
v.innerHTML = nextVerb() + '<em>…</em>';
|
|
278
|
+
v.classList.remove('swap'); void v.offsetWidth; v.classList.add('swap');
|
|
279
|
+
}
|
|
280
|
+
function showThinking() {
|
|
281
|
+
if (thinkingEl) return;
|
|
282
|
+
thinkingEl = card('thinking');
|
|
283
|
+
thinkingEl.innerHTML = '<div class="orb"><span></span><span></span><span></span></div><div class="verb"></div>';
|
|
284
|
+
paintVerb();
|
|
285
|
+
thinkTimer = setInterval(paintVerb, 2300);
|
|
286
|
+
window.scrollTo(0, document.body.scrollHeight);
|
|
287
|
+
}
|
|
288
|
+
function hideThinking() {
|
|
289
|
+
if (thinkTimer) { clearInterval(thinkTimer); thinkTimer = null; }
|
|
290
|
+
if (thinkingEl) { thinkingEl.remove(); thinkingEl = null; }
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ── Step cards ────────────────────────────────────────────────────────────────
|
|
294
|
+
// Each step is rendered as a card BEFORE the agent turn so the user can read
|
|
295
|
+
// the instruction while the agent is composing. The card state updates as steps
|
|
296
|
+
// are confirmed: pending → active → done.
|
|
297
|
+
|
|
298
|
+
const stepCardEls = {}; // id → card DOM element
|
|
299
|
+
|
|
300
|
+
function renderStepCard(step, state) {
|
|
301
|
+
// state: 'pending' | 'active' | 'done'
|
|
302
|
+
const existing = stepCardEls[step.id];
|
|
303
|
+
if (existing) {
|
|
304
|
+
// Update class on the existing card.
|
|
305
|
+
existing.className = 'card step ' + state;
|
|
306
|
+
if (state === 'done') {
|
|
307
|
+
const chk = existing.querySelector('.check-icon');
|
|
308
|
+
if (chk) chk.style.display = 'inline';
|
|
309
|
+
}
|
|
310
|
+
return existing;
|
|
311
|
+
}
|
|
312
|
+
const c = card('step ' + state);
|
|
313
|
+
c.innerHTML = `
|
|
314
|
+
<div class="step-badge">Step <span>${step.step}</span> of ${STEP_COUNT}</div>
|
|
315
|
+
<div class="step-title">${escHtml(step.title)}</div>
|
|
316
|
+
<div class="step-body">${escHtml(step.body)}</div>
|
|
317
|
+
<div class="step-do">${escHtml(step.doThis)}<span class="check-icon" style="display:none">✓</span></div>
|
|
318
|
+
`;
|
|
319
|
+
stepCardEls[step.id] = c;
|
|
320
|
+
return c;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function escHtml(str) {
|
|
324
|
+
return String(str)
|
|
325
|
+
.replace(/&/g, '&')
|
|
326
|
+
.replace(/</g, '<')
|
|
327
|
+
.replace(/>/g, '>')
|
|
328
|
+
.replace(/"/g, '"');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ── Session boot ──────────────────────────────────────────────────────────────
|
|
332
|
+
async function start() {
|
|
333
|
+
const h = await (await fetch('/api/health')).json();
|
|
334
|
+
token = h.token;
|
|
335
|
+
|
|
336
|
+
// Boot the session with the walkthrough-specific system prompt.
|
|
337
|
+
// Mirrors walkthrough-script.js WALKTHROUGH_SYSTEM — keep in sync.
|
|
338
|
+
// agent-session.js start() already accepts opts.systemPrompt (line 176).
|
|
339
|
+
const WALKTHROUGH_SYSTEM = [
|
|
340
|
+
"You are the Mover OS walkthrough guide.",
|
|
341
|
+
"Your ONLY job: teach the user to operate the system in 8 steps, taking no more than 8 minutes total.",
|
|
342
|
+
"Do NOT re-explain what Mover OS is, what Engine files are, or what setup covered.",
|
|
343
|
+
"Each turn: acknowledge the user's last action in ONE sentence, then give the next step's instruction clearly.",
|
|
344
|
+
"Keep every response under 4 sentences. No exceptions.",
|
|
345
|
+
"Do NOT use numbered lists, bullet points, or headings in your responses. Prose only.",
|
|
346
|
+
"Do NOT run any tools and do NOT read files — you have no tools in this session.",
|
|
347
|
+
"You may reference file names (like Active_Context.md or plan.md) but never their contents.",
|
|
348
|
+
"Tone: warm but fast. A co-founder showing someone the system in a short video call, not a teacher at a whiteboard.",
|
|
349
|
+
"When the user confirms they completed step 8 (running /plan-tomorrow and having a plan for tomorrow), emit the exact token [[WALKTHROUGH_COMPLETE]] on its own line, then write one sentence that says they are ready to operate the system.",
|
|
350
|
+
"CRITICAL: Only emit [[WALKTHROUGH_COMPLETE]] after the user has confirmed step 8. Not before.",
|
|
351
|
+
].join(" ");
|
|
352
|
+
|
|
353
|
+
const r = await post('/api/session/start', {
|
|
354
|
+
agent: 'claude-code',
|
|
355
|
+
systemPrompt: WALKTHROUGH_SYSTEM,
|
|
356
|
+
});
|
|
357
|
+
if (!r.ok) { setStatus('start failed: ' + r.error, false); return; }
|
|
358
|
+
sessionId = r.sessionId;
|
|
359
|
+
setStatus('session live', true);
|
|
360
|
+
endBtn.disabled = false;
|
|
361
|
+
|
|
362
|
+
// Subscribe to SSE stream from the agent.
|
|
363
|
+
es = new EventSource('/api/session/events?id=' + encodeURIComponent(sessionId));
|
|
364
|
+
es.onmessage = (m) => { let ev; try { ev = JSON.parse(m.data); } catch (_) { return; } handle(ev); };
|
|
365
|
+
es.onerror = () => setStatus('stream dropped', false);
|
|
366
|
+
|
|
367
|
+
// Render first step card and send the opening prompt to the agent.
|
|
368
|
+
const firstStep = STEPS[0];
|
|
369
|
+
renderStepCard(firstStep, 'active');
|
|
370
|
+
setBusy(true);
|
|
371
|
+
showThinking();
|
|
372
|
+
|
|
373
|
+
// The opening agent turn provides the guide persona framing + step 1 instruction.
|
|
374
|
+
// We pass the full step as context so the agent can give a tight, grounded response.
|
|
375
|
+
const openingPrompt = buildStepPrompt(firstStep, true);
|
|
376
|
+
await post('/api/session/send', { sessionId, text: openingPrompt });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Build the internal prompt the dashboard sends to the agent for each step.
|
|
380
|
+
// The user never sees this — they see the agent's natural-language response.
|
|
381
|
+
function buildStepPrompt(step, isFirst) {
|
|
382
|
+
const prefix = isFirst
|
|
383
|
+
? 'Begin the walkthrough. The user just completed setup.'
|
|
384
|
+
: 'The user completed the previous step.';
|
|
385
|
+
return [
|
|
386
|
+
prefix,
|
|
387
|
+
`Current step: ${step.step}/${STEP_COUNT} — "${step.title}".`,
|
|
388
|
+
`What to teach: ${step.body}`,
|
|
389
|
+
`The action for the user: ${step.doThis}`,
|
|
390
|
+
`How to verify: ${step.checkpoint}`,
|
|
391
|
+
'Give your response now. Max 4 sentences.',
|
|
392
|
+
].join(' ');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Build the "advance to next step" prompt sent when a step is confirmed done.
|
|
396
|
+
function buildAdvancePrompt(nextStep) {
|
|
397
|
+
return [
|
|
398
|
+
`The user confirmed step ${nextStep.step - 1} is done.`,
|
|
399
|
+
`Next step: ${nextStep.step}/${STEP_COUNT} — "${nextStep.title}".`,
|
|
400
|
+
`What to teach: ${nextStep.body}`,
|
|
401
|
+
`The action for the user: ${nextStep.doThis}`,
|
|
402
|
+
`How to verify: ${nextStep.checkpoint}`,
|
|
403
|
+
'Give your response now. Max 4 sentences.',
|
|
404
|
+
].join(' ');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ── Sentinel detection ────────────────────────────────────────────────────────
|
|
408
|
+
// When the agent confirms step 8, it emits [[WALKTHROUGH_COMPLETE]] exactly
|
|
409
|
+
// as setup-poc.html emits [[SETUP_COMPLETE]]. We split the text, render the
|
|
410
|
+
// pre-sentinel portion, and fire the done banner — so the banner is driven by
|
|
411
|
+
// the agent's confirmation, not solely by client-side step counting.
|
|
412
|
+
const DONE_SENTINEL = '[[WALKTHROUGH_COMPLETE]]';
|
|
413
|
+
let walkthroughDone = false;
|
|
414
|
+
|
|
415
|
+
// ── Event handler ─────────────────────────────────────────────────────────────
|
|
416
|
+
function handle(ev) {
|
|
417
|
+
if (ev.kind === 'assistant') {
|
|
418
|
+
hideThinking();
|
|
419
|
+
// Check for the completion sentinel (may arrive with surrounding text).
|
|
420
|
+
if (!walkthroughDone && ev.text.indexOf(DONE_SENTINEL) >= 0) {
|
|
421
|
+
walkthroughDone = true;
|
|
422
|
+
const parts = ev.text.split(DONE_SENTINEL);
|
|
423
|
+
const pre = (parts[0] || '').trim();
|
|
424
|
+
const closing = parts.slice(1).join(' ').trim();
|
|
425
|
+
// Render whatever the agent said before the sentinel.
|
|
426
|
+
if (pre) {
|
|
427
|
+
const c = card(); const q = document.createElement('div'); q.className = 'q';
|
|
428
|
+
q.textContent = pre; c.appendChild(q);
|
|
429
|
+
}
|
|
430
|
+
// Show the done banner — closing line becomes the sub-text if present.
|
|
431
|
+
showDone(closing);
|
|
432
|
+
setBusy(true);
|
|
433
|
+
if (es) es.close();
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
const c = card();
|
|
437
|
+
const q = document.createElement('div');
|
|
438
|
+
q.className = 'q';
|
|
439
|
+
q.textContent = ev.text;
|
|
440
|
+
c.appendChild(q);
|
|
441
|
+
}
|
|
442
|
+
else if (ev.kind === 'result') {
|
|
443
|
+
hideThinking();
|
|
444
|
+
if (!walkthroughDone) { setBusy(false); inp.focus(); }
|
|
445
|
+
}
|
|
446
|
+
else if (ev.kind === 'permission') {
|
|
447
|
+
// Defensive: walkthrough is tool-free so this should not fire.
|
|
448
|
+
// Forward to GUI as a plain-English choice (mirrors setup-poc.html).
|
|
449
|
+
hideThinking();
|
|
450
|
+
const id = ev.requestId;
|
|
451
|
+
const c = card('perm');
|
|
452
|
+
c.innerHTML = '<div class="lbl">Permission requested</div>';
|
|
453
|
+
const q = document.createElement('div'); q.className = 'q';
|
|
454
|
+
q.textContent = (JSON.stringify(ev.request) || '').slice(0, 240);
|
|
455
|
+
c.appendChild(q);
|
|
456
|
+
const row = document.createElement('div'); row.className = 'row';
|
|
457
|
+
const allow = document.createElement('button'); allow.className = 'primary'; allow.textContent = 'Allow';
|
|
458
|
+
const deny = document.createElement('button'); deny.textContent = 'Deny';
|
|
459
|
+
const decide = (d) => {
|
|
460
|
+
allow.disabled = true; deny.disabled = true;
|
|
461
|
+
const note = document.createElement('span');
|
|
462
|
+
note.className = 'a'; note.style.marginLeft = '8px'; note.textContent = d + ' sent…';
|
|
463
|
+
row.appendChild(note);
|
|
464
|
+
post('/api/session/approve', { sessionId, requestId: id, decision: d });
|
|
465
|
+
};
|
|
466
|
+
allow.onclick = () => decide('allow');
|
|
467
|
+
deny.onclick = () => decide('deny');
|
|
468
|
+
row.appendChild(allow); row.appendChild(deny); c.appendChild(row);
|
|
469
|
+
}
|
|
470
|
+
else if (ev.kind === 'closed' || ev.kind === 'error') {
|
|
471
|
+
hideThinking();
|
|
472
|
+
setStatus('session ended', false);
|
|
473
|
+
setBusy(true);
|
|
474
|
+
endBtn.disabled = true;
|
|
475
|
+
if (es) es.close();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ── User sends a response ─────────────────────────────────────────────────────
|
|
480
|
+
// The walkthrough is conversational: the user types their response, the agent
|
|
481
|
+
// interprets whether the step checkpoint is met, and we advance the step card.
|
|
482
|
+
// Client advances the step card OPTIMISTICALLY on send (snappy UI). The agent's
|
|
483
|
+
// response provides the natural-language bridge. On step 8, the agent emits
|
|
484
|
+
// [[WALKTHROUGH_COMPLETE]] which triggers showDone() via handle(). As a safety
|
|
485
|
+
// net, if the sentinel never arrives (e.g. agent session fault after all steps
|
|
486
|
+
// are client-confirmed), showDone() fires after a 12s grace window.
|
|
487
|
+
|
|
488
|
+
sendBtn.onclick = async () => {
|
|
489
|
+
const t = inp.value.trim();
|
|
490
|
+
if (!t || busy) return;
|
|
491
|
+
|
|
492
|
+
// Show user's response in the feed.
|
|
493
|
+
const uc = card();
|
|
494
|
+
const ua = document.createElement('div'); ua.className = 'a';
|
|
495
|
+
ua.textContent = 'You: ' + t;
|
|
496
|
+
uc.appendChild(ua);
|
|
497
|
+
inp.value = '';
|
|
498
|
+
|
|
499
|
+
// Mark current step done.
|
|
500
|
+
const currentStep = STEPS[currentStepIndex];
|
|
501
|
+
if (currentStep) {
|
|
502
|
+
completedSteps.add(currentStep.id);
|
|
503
|
+
renderStepCard(currentStep, 'done');
|
|
504
|
+
setProgress(completedSteps.size);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Advance to next step.
|
|
508
|
+
currentStepIndex++;
|
|
509
|
+
const nextStep = STEPS[currentStepIndex];
|
|
510
|
+
|
|
511
|
+
if (!nextStep) {
|
|
512
|
+
// All client-side steps confirmed. Send final turn to agent so it can
|
|
513
|
+
// emit [[WALKTHROUGH_COMPLETE]]. Safety net: if sentinel never arrives,
|
|
514
|
+
// show done banner after 12s (covers agent latency on slow connections).
|
|
515
|
+
setBusy(true);
|
|
516
|
+
showThinking();
|
|
517
|
+
const finalTurn = t + '\n\n[SYSTEM: The user confirmed step 8 is complete. Emit [[WALKTHROUGH_COMPLETE]] on its own line, then write one closing sentence that says they are ready to operate the system.]';
|
|
518
|
+
await post('/api/session/send', { sessionId, text: finalTurn });
|
|
519
|
+
// Safety net — fire if sentinel never lands.
|
|
520
|
+
setTimeout(() => {
|
|
521
|
+
if (!walkthroughDone) { showDone(); if (es) es.close(); }
|
|
522
|
+
}, 12000);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Render the next step card immediately (user sees where they're going).
|
|
527
|
+
renderStepCard(nextStep, 'active');
|
|
528
|
+
|
|
529
|
+
// Send the user's message + advance prompt to the agent in one turn.
|
|
530
|
+
// The agent sees the user's actual words AND the step context.
|
|
531
|
+
const agentTurn = t + '\n\n[SYSTEM: ' + buildAdvancePrompt(nextStep) + ']';
|
|
532
|
+
setBusy(true);
|
|
533
|
+
showThinking();
|
|
534
|
+
await post('/api/session/send', { sessionId, text: agentTurn });
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
inp.addEventListener('keydown', (e) => {
|
|
538
|
+
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendBtn.click(); }
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
endBtn.onclick = async () => {
|
|
542
|
+
if (es) es.close();
|
|
543
|
+
await post('/api/session/end', { sessionId });
|
|
544
|
+
setStatus('ended', false);
|
|
545
|
+
setBusy(true);
|
|
546
|
+
endBtn.disabled = true;
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// ── Done state ────────────────────────────────────────────────────────────────
|
|
550
|
+
// closingLine: optional string from the agent post-sentinel (one "you're ready" sentence).
|
|
551
|
+
// rendered: internal guard so the banner renders exactly once.
|
|
552
|
+
let _doneRendered = false;
|
|
553
|
+
function showDone(closingLine) {
|
|
554
|
+
if (_doneRendered) return; // already shown — safety net or double-call guard
|
|
555
|
+
_doneRendered = true;
|
|
556
|
+
walkthroughDone = true;
|
|
557
|
+
setStatus('walkthrough complete', true);
|
|
558
|
+
const DEFAULT_SUB = 'You know how to drive it. Run <strong>/morning</strong> every session, follow the handoffs, and the system compounds every day.';
|
|
559
|
+
const banner = document.createElement('div');
|
|
560
|
+
banner.className = 'done-banner';
|
|
561
|
+
banner.innerHTML = `
|
|
562
|
+
<div class="headline">Engine is live.</div>
|
|
563
|
+
<div class="sub">${closingLine ? escHtml(closingLine) : DEFAULT_SUB}</div>
|
|
564
|
+
<button class="cta" onclick="window.location.href='/'">
|
|
565
|
+
Open Mover Studio
|
|
566
|
+
</button>
|
|
567
|
+
`;
|
|
568
|
+
feed.appendChild(banner);
|
|
569
|
+
window.scrollTo(0, document.body.scrollHeight);
|
|
570
|
+
// Hide the composer — walkthrough is complete.
|
|
571
|
+
document.querySelector('.composer').style.display = 'none';
|
|
572
|
+
// End the session gracefully (fire-and-forget — no await needed here).
|
|
573
|
+
post('/api/session/end', { sessionId }).catch(() => {});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// ── Boot ──────────────────────────────────────────────────────────────────────
|
|
577
|
+
start();
|
|
578
|
+
</script>
|
|
579
|
+
</body>
|
|
580
|
+
</html>
|