mover-os 4.7.7 → 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.
Files changed (107) hide show
  1. package/install.js +688 -68
  2. package/package.json +15 -3
  3. package/src/dashboard/build.js +1503 -0
  4. package/src/dashboard/dashboard.js +276 -0
  5. package/src/dashboard/index.js +319 -0
  6. package/src/dashboard/lib/activation-log.js +297 -0
  7. package/src/dashboard/lib/active-context-parser.js +177 -0
  8. package/src/dashboard/lib/agent-command.js +93 -0
  9. package/src/dashboard/lib/agent-detect.js +255 -0
  10. package/src/dashboard/lib/agent-detector.js +92 -0
  11. package/src/dashboard/lib/agent-session.js +462 -0
  12. package/src/dashboard/lib/anti-identity-detector.js +116 -0
  13. package/src/dashboard/lib/auto-learnings-parser.js +183 -0
  14. package/src/dashboard/lib/cli-usage-parser.js +92 -0
  15. package/src/dashboard/lib/config-parser.js +109 -0
  16. package/src/dashboard/lib/connect-recommender.js +131 -0
  17. package/src/dashboard/lib/correlations-parser.js +231 -0
  18. package/src/dashboard/lib/daily-note-resolver.js +211 -0
  19. package/src/dashboard/lib/date-utils.js +35 -0
  20. package/src/dashboard/lib/distribution-parser.js +78 -0
  21. package/src/dashboard/lib/dossier-parser.js +64 -0
  22. package/src/dashboard/lib/drift-history.js +83 -0
  23. package/src/dashboard/lib/drift-score.js +119 -0
  24. package/src/dashboard/lib/engine-health.js +170 -0
  25. package/src/dashboard/lib/engine-writer.js +1685 -0
  26. package/src/dashboard/lib/execution-plan.js +125 -0
  27. package/src/dashboard/lib/experiments-parser.js +429 -0
  28. package/src/dashboard/lib/feed-parser.js +294 -0
  29. package/src/dashboard/lib/forked-future.js +60 -0
  30. package/src/dashboard/lib/goal-forecast.js +304 -0
  31. package/src/dashboard/lib/goals-parser.js +67 -0
  32. package/src/dashboard/lib/health-protocols-parser.js +133 -0
  33. package/src/dashboard/lib/hook-activity.js +48 -0
  34. package/src/dashboard/lib/hook-indexer.js +169 -0
  35. package/src/dashboard/lib/hourly-activity-parser.js +143 -0
  36. package/src/dashboard/lib/identity-parser.js +85 -0
  37. package/src/dashboard/lib/ingestion.js +418 -0
  38. package/src/dashboard/lib/library-indexer-v2.js +212 -0
  39. package/src/dashboard/lib/library-indexer.js +105 -0
  40. package/src/dashboard/lib/log-activation.sh +61 -0
  41. package/src/dashboard/lib/memory-curator.js +97 -0
  42. package/src/dashboard/lib/memory-gardener.js +177 -0
  43. package/src/dashboard/lib/memory-gepa.js +102 -0
  44. package/src/dashboard/lib/memory-index.js +470 -0
  45. package/src/dashboard/lib/memory-rerank.js +72 -0
  46. package/src/dashboard/lib/memory-text.js +136 -0
  47. package/src/dashboard/lib/metrics-log-parser.js +76 -0
  48. package/src/dashboard/lib/moves-usage-parser.js +184 -0
  49. package/src/dashboard/lib/onboarding-forge.js +70 -0
  50. package/src/dashboard/lib/override-outcome-parser.js +68 -0
  51. package/src/dashboard/lib/override-summary.js +73 -0
  52. package/src/dashboard/lib/paths.js +192 -0
  53. package/src/dashboard/lib/pattern-manifest-loader.js +29 -0
  54. package/src/dashboard/lib/phantom-strategy.js +129 -0
  55. package/src/dashboard/lib/project-scanner.js +121 -0
  56. package/src/dashboard/lib/promise-wall.js +88 -0
  57. package/src/dashboard/lib/record-score.js +173 -0
  58. package/src/dashboard/lib/redaction.js +140 -0
  59. package/src/dashboard/lib/refusal-parser.js +44 -0
  60. package/src/dashboard/lib/regenerate-manifest.js +206 -0
  61. package/src/dashboard/lib/rewind-snapshots.js +689 -0
  62. package/src/dashboard/lib/roast-wall-parser.js +200 -0
  63. package/src/dashboard/lib/run-registry.js +226 -0
  64. package/src/dashboard/lib/safe-write.js +63 -0
  65. package/src/dashboard/lib/session-log-parser.js +145 -0
  66. package/src/dashboard/lib/session-time-parser.js +158 -0
  67. package/src/dashboard/lib/skill-index.js +171 -0
  68. package/src/dashboard/lib/skill-indexer.js +118 -0
  69. package/src/dashboard/lib/skill-recommender.js +689 -0
  70. package/src/dashboard/lib/strategy-parser.js +245 -0
  71. package/src/dashboard/lib/strategy-protocol-parser.js +135 -0
  72. package/src/dashboard/lib/streak-parser.js +95 -0
  73. package/src/dashboard/lib/suggested-now.js +254 -0
  74. package/src/dashboard/lib/tool-awareness.js +125 -0
  75. package/src/dashboard/lib/transcript-parser.js +331 -0
  76. package/src/dashboard/lib/vault-graph-parser.js +205 -0
  77. package/src/dashboard/lib/view-generator.js +163 -0
  78. package/src/dashboard/lib/voice-dna-parser.js +57 -0
  79. package/src/dashboard/lib/walkthrough-script.js +140 -0
  80. package/src/dashboard/lib/workflow-graph-parser.js +170 -0
  81. package/src/dashboard/lib/workflow-library-parser.js +146 -0
  82. package/src/dashboard/server.js +2024 -0
  83. package/src/dashboard/shortcut.js +0 -0
  84. package/src/dashboard/static/setup-poc.html +306 -0
  85. package/src/dashboard/static/walkthrough-poc.html +580 -0
  86. package/src/dashboard/styles.css +1201 -0
  87. package/src/dashboard/templates/index.html +278 -0
  88. package/src/dashboard/ui/dist/assets/hanken-grotesk-latin-ext-wght-normal-Dg-wlmqe.woff2 +0 -0
  89. package/src/dashboard/ui/dist/assets/hanken-grotesk-latin-wght-normal-CaVRRdDk.woff2 +0 -0
  90. package/src/dashboard/ui/dist/assets/hanken-grotesk-vietnamese-wght-normal-CHiFlh_0.woff2 +0 -0
  91. package/src/dashboard/ui/dist/assets/hero.png +0 -0
  92. package/src/dashboard/ui/dist/assets/index-598CSGOZ.js +157 -0
  93. package/src/dashboard/ui/dist/assets/index-BP--M69H.css +1 -0
  94. package/src/dashboard/ui/dist/assets/index-CidzmwSW.js +34 -0
  95. package/src/dashboard/ui/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  96. package/src/dashboard/ui/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  97. package/src/dashboard/ui/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  98. package/src/dashboard/ui/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  99. package/src/dashboard/ui/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  100. package/src/dashboard/ui/dist/assets/mover-system.css +62 -0
  101. package/src/dashboard/ui/dist/assets/xterm-CqkleIqs.js +1 -0
  102. package/src/dashboard/ui/dist/icon.svg +4 -0
  103. package/src/dashboard/ui/dist/index.html +18 -0
  104. package/src/dashboard/ui/dist/manifest.webmanifest +1 -0
  105. package/src/dashboard/ui/dist/registerSW.js +1 -0
  106. package/src/dashboard/ui/dist/sw.js +1 -0
  107. package/src/dashboard/ui/dist/workbox-39fa566e.js +1 -0
@@ -0,0 +1,255 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * agent-detect.js — D7 agent detection for the onboarding connect step.
5
+ *
6
+ * Scope: claude / codex / gemini ONLY — the three adapters defined in
7
+ * agent-session.js ADAPTERS. Wider agent survey (Cursor, Windsurf, etc.)
8
+ * lives in agent-detector.js (the v5 dashboard Library tab surface).
9
+ *
10
+ * Contract per agent:
11
+ * id — adapter key matching ADAPTERS in agent-session.js
12
+ * present — binary is on PATH (binary probe, non-interactive, 800ms cap)
13
+ * authed — best-effort, non-interactive auth signal (see per-vendor notes)
14
+ * runnable — present AND the ADAPTERS entry is fully wired (not unsupported)
15
+ *
16
+ * Auth signals (non-interactive only — footgun §4: never spawn interactive login):
17
+ * claude : ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, Claude Code keychain
18
+ * credentials on macOS, or ~/.claude/.credentials.json on Windows/Linux.
19
+ * codex : verify-next adapter — authed=false (cannot probe without spawning).
20
+ * gemini : verify-next adapter — authed=false (footgun §4: --acp can prompt
21
+ * for login from a non-terminal parent; never probe here).
22
+ *
23
+ * Never spawns an interactive process. Only uses:
24
+ * - execFile("sh", ["-c", "command -v '<bin>'"], { timeout: 800 }) — async
25
+ * - process.env lookup + credential files/keychain reads
26
+ *
27
+ * Wired adapters (from ADAPTERS in agent-session.js, 2026-06-18):
28
+ * claude-code → wired (cmd + args + normalize + encodeTurn)
29
+ * codex → unsupported (verify-next)
30
+ * gemini → unsupported (verify-next)
31
+ */
32
+
33
+ const fs = require("fs");
34
+ const os = require("os");
35
+ const path = require("path");
36
+ const { execFile, execFileSync } = require("child_process");
37
+
38
+ // Mirror the ADAPTERS wiring state from agent-session.js.
39
+ // Only source of truth for "is this adapter ready to run?"
40
+ // Update this table when a verify-next adapter is promoted.
41
+ const ADAPTER_WIRED = {
42
+ "claude-code": true, // verified spike 2026-06-18
43
+ "codex": false, // verify-next (SPEC §2)
44
+ "gemini": false, // verify-next (SPEC §2)
45
+ };
46
+
47
+ // ─── PATH probe ──────────────────────────────────────────────────────────────
48
+
49
+ /**
50
+ * Returns a Promise<boolean> — true if `bin` is callable on PATH.
51
+ * Uses `command -v` via sh — POSIX, cheap, no interactive spawn.
52
+ * 800 ms cap matches agent-detector.js convention (fast enough for
53
+ * the onboarding step, non-blocking for SSE streams).
54
+ *
55
+ * SF-2: single-quote escaping — every single-quote in `bin` is replaced
56
+ * with the '\'' sequence so a caller-supplied name can never inject shell
57
+ * metacharacters ($/backtick/backslash are inert inside single quotes).
58
+ */
59
+ function binaryPresent(bin) {
60
+ if (!bin || typeof bin !== "string") return Promise.resolve(false);
61
+ // Escape any single-quotes inside the binary name: ' → '\''
62
+ const safeBin = bin.replace(/'/g, "'\\''");
63
+ return new Promise((resolve) => {
64
+ const child = execFile(
65
+ "sh",
66
+ ["-c", `command -v '${safeBin}'`],
67
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 800 },
68
+ (err, stdout) => {
69
+ if (err) return resolve(false);
70
+ resolve(Boolean(stdout && stdout.trim()));
71
+ }
72
+ );
73
+ // Belt-and-suspenders: also resolve false if the child emits 'error'
74
+ // before the callback fires (e.g. ENOENT for sh — extremely unlikely).
75
+ child.on("error", () => resolve(false));
76
+ });
77
+ }
78
+
79
+ function readClaudeOAuthToken() {
80
+ if (process.env.CLAUDE_CODE_OAUTH_TOKEN) return process.env.CLAUDE_CODE_OAUTH_TOKEN;
81
+
82
+ if (process.platform === "darwin") {
83
+ try {
84
+ const out = execFileSync(
85
+ "security",
86
+ ["find-generic-password", "-s", "Claude Code-credentials", "-w"],
87
+ { encoding: "utf8", timeout: 1200, stdio: ["ignore", "pipe", "ignore"] }
88
+ ).trim();
89
+ if (out) {
90
+ try {
91
+ const token = JSON.parse(out)?.claudeAiOauth?.accessToken;
92
+ if (token) return token;
93
+ } catch (_) {
94
+ if (/^ey/.test(out)) return out;
95
+ }
96
+ }
97
+ } catch (_) {}
98
+ }
99
+
100
+ try {
101
+ const cf = path.join(os.homedir(), ".claude", ".credentials.json");
102
+ if (fs.existsSync(cf)) {
103
+ const token = JSON.parse(fs.readFileSync(cf, "utf8"))?.claudeAiOauth?.accessToken;
104
+ if (token) return token;
105
+ }
106
+ } catch (_) {}
107
+
108
+ try {
109
+ const macCf = path.join(os.homedir(), "Library", "Application Support", "Claude", "config.json");
110
+ if (fs.existsSync(macCf)) {
111
+ const cfg = JSON.parse(fs.readFileSync(macCf, "utf8"));
112
+ for (const key of ["oauth:accessToken", "oauthAccessToken", "accessToken"]) {
113
+ if (cfg[key] && /^ey/.test(String(cfg[key]))) return cfg[key];
114
+ }
115
+ }
116
+ } catch (_) {}
117
+
118
+ return null;
119
+ }
120
+
121
+ // ─── per-vendor descriptors ──────────────────────────────────────────────────
122
+
123
+ const VENDORS = [
124
+ {
125
+ id: "claude-code",
126
+ name: "Claude",
127
+ bin: "claude",
128
+ /**
129
+ * Auth: subscription-OAuth is the intended mode (SPEC §2). Do not assume
130
+ * "binary exists" means "signed in"; first-run uses this signal to avoid
131
+ * a failed embedded spawn and show the sign-in recovery card up front.
132
+ */
133
+ auth() {
134
+ const apiKey = process.env.ANTHROPIC_API_KEY;
135
+ const keySet = Boolean(apiKey && apiKey.trim());
136
+ const token = readClaudeOAuthToken();
137
+ return {
138
+ authed: keySet || Boolean(token),
139
+ mode: keySet ? "api-key" : "subscription-oauth",
140
+ note: keySet
141
+ ? "ANTHROPIC_API_KEY detected — check Claude subscription billing mode"
142
+ : token
143
+ ? "Claude CLI sign-in detected"
144
+ : "Claude CLI installed but no saved sign-in found — run `claude` then `/login`",
145
+ };
146
+ },
147
+ },
148
+ {
149
+ id: "codex",
150
+ name: "Codex",
151
+ bin: "codex",
152
+ /**
153
+ * Auth: verify-next adapter — we deliberately do NOT probe.
154
+ * `codex app-server` (the JSON-RPC transport) hasn't been spiked;
155
+ * probing auth non-interactively isn't safe until we understand the
156
+ * CLI's login flow. Conservative: authed=false until adapter is wired.
157
+ */
158
+ auth() {
159
+ return {
160
+ authed: false,
161
+ mode: "verify-next",
162
+ note: "Codex adapter is verify-next — spike `codex app-server` before enabling",
163
+ };
164
+ },
165
+ },
166
+ {
167
+ id: "gemini",
168
+ name: "Gemini",
169
+ bin: "gemini",
170
+ /**
171
+ * Auth: footgun §4 — `gemini --acp` can prompt for login when spawned from
172
+ * a non-terminal parent. Never probe. authed=false until adapter is wired
173
+ * AND we have a non-interactive auth-check mechanism confirmed.
174
+ */
175
+ auth() {
176
+ return {
177
+ authed: false,
178
+ mode: "verify-next",
179
+ note: "Gemini adapter is verify-next — confirm non-interactive auth path before enabling",
180
+ };
181
+ },
182
+ },
183
+ ];
184
+
185
+ // ─── main export ─────────────────────────────────────────────────────────────
186
+
187
+ /**
188
+ * detectAgents() → Promise<Array<AgentStatus>>
189
+ *
190
+ * MF-3: now async — three vendor PATH probes run concurrently via
191
+ * Promise.all, so total wall-clock time ≈ max(probe) ≈ 800ms instead
192
+ * of 3 × 800ms = 2.4s. Does not block the Node event loop.
193
+ *
194
+ * AgentStatus {
195
+ * id: string — adapter key (matches ADAPTERS in agent-session.js)
196
+ * name: string — display label
197
+ * present: boolean — binary on PATH
198
+ * authed: boolean — best-effort non-interactive auth signal (see above)
199
+ * runnable: boolean — present AND adapter is wired AND auth is ready
200
+ * authMode: string — "subscription-oauth" | "api-key" | "verify-next"
201
+ * authNote: string — one-line explanation for the UI
202
+ * }
203
+ */
204
+ async function detectAgents() {
205
+ // Fan-out: all three PATH probes run concurrently.
206
+ const presenceResults = await Promise.all(VENDORS.map((v) => binaryPresent(v.bin)));
207
+
208
+ return VENDORS.map((v, i) => {
209
+ const present = presenceResults[i];
210
+ const authInfo = v.auth();
211
+ const adapterWired = ADAPTER_WIRED[v.id] === true;
212
+ // runnable requires the binary, a wired adapter, and a non-interactive auth signal.
213
+ // For verify-next adapters, presence is informative only.
214
+ const runnable = present && adapterWired && authInfo.authed;
215
+ return {
216
+ id: v.id,
217
+ name: v.name,
218
+ present,
219
+ authed: adapterWired ? authInfo.authed : false,
220
+ runnable,
221
+ authMode: authInfo.mode,
222
+ authNote: authInfo.note,
223
+ };
224
+ });
225
+ }
226
+
227
+ module.exports = { detectAgents };
228
+
229
+ // ─── self-test (node agent-detect.js) ────────────────────────────────────────
230
+ if (require.main === module) {
231
+ (async () => {
232
+ const results = await detectAgents();
233
+ console.log("\nD7 Agent Detection — " + new Date().toISOString());
234
+ console.log("─".repeat(62));
235
+ for (const r of results) {
236
+ const tick = (b) => (b ? "✓" : "✗");
237
+ console.log(
238
+ `${r.name.padEnd(10)} present=${tick(r.present)} authed=${tick(r.authed)} runnable=${tick(r.runnable)}`
239
+ );
240
+ console.log(` mode: ${r.authMode}`);
241
+ console.log(` note: ${r.authNote}`);
242
+ console.log();
243
+ }
244
+ const runnable = results.filter((r) => r.runnable);
245
+ const present = results.filter((r) => r.present && !r.runnable);
246
+ const none = results.every((r) => !r.present);
247
+ if (runnable.length) {
248
+ console.log("STATUS: READY — runnable adapters: " + runnable.map((r) => r.id).join(", "));
249
+ } else if (present.length) {
250
+ console.log("STATUS: PARTIAL — binaries present but adapters not wired: " + present.map((r) => r.id).join(", "));
251
+ } else if (none) {
252
+ console.log("STATUS: NONE — no supported agent binary found on PATH");
253
+ }
254
+ })();
255
+ }
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+
3
+ const path = require("path");
4
+ const os = require("os");
5
+ const { execFileSync } = require("child_process");
6
+ const { exists, safeStat } = require("./paths");
7
+
8
+ // Detect which AI agents are installed on the user's machine.
9
+ // `configured` = ~/.<dir> present (means USER OR MOVER OS set it up)
10
+ // `installed` = configured AND the actual CLI binary is on PATH
11
+ // `runnable` = installed AND the agent supports CLI invocation (capability flag)
12
+ //
13
+ // The old design treated any config directory as "installed", which made
14
+ // amp/aider/amazon-q show up as installed for users who never installed them
15
+ // (Mover OS itself sometimes creates those directories as side-effects).
16
+ // Codex bug-hunt (2026-05-13) flagged this — now we require an actual binary
17
+ // probe via `command -v <cli>` to count as installed.
18
+
19
+ const AGENTS = [
20
+ { id: "claude-code", name: "Claude Code", configPaths: [".claude"], cliBin: "claude", capability: true, hookSupport: true },
21
+ { id: "codex", name: "Codex CLI", configPaths: [".codex"], cliBin: "codex", capability: true, hookSupport: true },
22
+ { id: "gemini", name: "Gemini CLI", configPaths: [".gemini"], cliBin: "gemini", capability: true, hookSupport: true },
23
+ { id: "cursor", name: "Cursor", configPaths: [".cursor", "Library/Application Support/Cursor"], cliBin: null, capability: false, hookSupport: false },
24
+ { id: "windsurf", name: "Windsurf", configPaths: [".windsurf", "Library/Application Support/Windsurf"], cliBin: null, capability: false, hookSupport: false },
25
+ { id: "amp", name: "Amp", configPaths: [".amp"], cliBin: "amp", capability: true, hookSupport: false },
26
+ { id: "aider", name: "Aider", configPaths: [".aider", ".aider.conf.yml"], cliBin: "aider", capability: true, hookSupport: false },
27
+ { id: "amazon-q", name: "Amazon Q", configPaths: [".aws/amazonq"], cliBin: "q", capability: true, hookSupport: false },
28
+ { id: "cline", name: "Cline", configPaths: [".cline"], cliBin: null, capability: false, hookSupport: false },
29
+ { id: "continue", name: "Continue", configPaths: [".continue"], cliBin: null, capability: false, hookSupport: false }
30
+ ];
31
+
32
+ function hasBinary(cmd) {
33
+ if (!cmd) return false;
34
+ try {
35
+ // `command -v` is POSIX, faster than spawning the actual CLI just to probe.
36
+ // We bound it tightly via execFileSync so a hung path entry can't stall the dashboard.
37
+ const out = execFileSync("sh", ["-c", `command -v ${JSON.stringify(cmd)}`], {
38
+ encoding: "utf8",
39
+ stdio: ["ignore", "pipe", "ignore"],
40
+ timeout: 800
41
+ });
42
+ return Boolean(out && out.trim());
43
+ } catch (_) {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ function detectAgents() {
49
+ const home = os.homedir();
50
+ const out = AGENTS.map(a => {
51
+ let configured = false;
52
+ let configPath = null;
53
+ let lastUsed = null;
54
+ for (const cp of a.configPaths) {
55
+ const p = path.join(home, cp);
56
+ if (exists(p)) {
57
+ configured = true;
58
+ configPath = p;
59
+ const stat = safeStat(p);
60
+ if (stat) lastUsed = stat.mtime;
61
+ break;
62
+ }
63
+ }
64
+ // `installed` requires both a config directory AND a callable binary.
65
+ // GUI-only tools (Cursor, Windsurf, Cline, Continue) have no cliBin —
66
+ // for those, configured-and-on-disk counts as installed.
67
+ const binaryFound = a.cliBin ? hasBinary(a.cliBin) : configured;
68
+ const installed = configured && binaryFound;
69
+ return {
70
+ id: a.id,
71
+ name: a.name,
72
+ configured,
73
+ installed,
74
+ runnable: installed && a.capability,
75
+ hookSupport: a.hookSupport,
76
+ configPath,
77
+ cliBin: a.cliBin || null,
78
+ lastUsed
79
+ };
80
+ });
81
+
82
+ return {
83
+ available: true,
84
+ agents: out,
85
+ installedCount: out.filter(a => a.installed).length,
86
+ runnableCount: out.filter(a => a.runnable).length,
87
+ hookSupportedCount: out.filter(a => a.installed && a.hookSupport).length,
88
+ primary: out.find(a => a.runnable) || null
89
+ };
90
+ }
91
+
92
+ module.exports = { detectAgents };