mover-os 4.0.0

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.

Potentially problematic release.


This version of mover-os might be problematic. Click here for more details.

Files changed (149) hide show
  1. package/README.md +201 -0
  2. package/install.js +1424 -0
  3. package/package.json +35 -0
  4. package/src/hooks/context-staleness.sh +46 -0
  5. package/src/hooks/dirty-tree-guard.sh +33 -0
  6. package/src/hooks/engine-protection.sh +43 -0
  7. package/src/hooks/git-safety.sh +47 -0
  8. package/src/hooks/pre-compact-backup.sh +177 -0
  9. package/src/hooks/session-log-reminder.sh +64 -0
  10. package/src/skills/THIRD-PARTY-LICENSES.md +53 -0
  11. package/src/skills/agent-code-reviewer/SKILL.md +41 -0
  12. package/src/skills/agent-content-researcher/SKILL.md +59 -0
  13. package/src/skills/agent-research-analyst/SKILL.md +53 -0
  14. package/src/skills/agent-security-auditor/SKILL.md +42 -0
  15. package/src/skills/agent-strategy-analyst/SKILL.md +54 -0
  16. package/src/skills/defuddle/SKILL.md +41 -0
  17. package/src/skills/find-bugs/SKILL.md +75 -0
  18. package/src/skills/find-skills/SKILL.md +133 -0
  19. package/src/skills/frontend-design/LICENSE.txt +177 -0
  20. package/src/skills/frontend-design/SKILL.md +42 -0
  21. package/src/skills/human-writer/SKILL.md +185 -0
  22. package/src/skills/json-canvas/SKILL.md +656 -0
  23. package/src/skills/marketingskills/.claude-plugin/marketplace.json +45 -0
  24. package/src/skills/marketingskills/README.md +204 -0
  25. package/src/skills/marketingskills/skills/ab-test-setup/SKILL.md +508 -0
  26. package/src/skills/marketingskills/skills/analytics-tracking/SKILL.md +539 -0
  27. package/src/skills/marketingskills/skills/competitor-alternatives/SKILL.md +750 -0
  28. package/src/skills/marketingskills/skills/copy-editing/SKILL.md +439 -0
  29. package/src/skills/marketingskills/skills/copywriting/SKILL.md +455 -0
  30. package/src/skills/marketingskills/skills/email-sequence/SKILL.md +925 -0
  31. package/src/skills/marketingskills/skills/form-cro/SKILL.md +425 -0
  32. package/src/skills/marketingskills/skills/free-tool-strategy/SKILL.md +576 -0
  33. package/src/skills/marketingskills/skills/launch-strategy/SKILL.md +344 -0
  34. package/src/skills/marketingskills/skills/marketing-ideas/SKILL.md +565 -0
  35. package/src/skills/marketingskills/skills/marketing-psychology/SKILL.md +451 -0
  36. package/src/skills/marketingskills/skills/onboarding-cro/SKILL.md +433 -0
  37. package/src/skills/marketingskills/skills/page-cro/SKILL.md +334 -0
  38. package/src/skills/marketingskills/skills/paid-ads/SKILL.md +551 -0
  39. package/src/skills/marketingskills/skills/paywall-upgrade-cro/SKILL.md +570 -0
  40. package/src/skills/marketingskills/skills/popup-cro/SKILL.md +449 -0
  41. package/src/skills/marketingskills/skills/pricing-strategy/SKILL.md +710 -0
  42. package/src/skills/marketingskills/skills/programmatic-seo/SKILL.md +626 -0
  43. package/src/skills/marketingskills/skills/referral-program/SKILL.md +602 -0
  44. package/src/skills/marketingskills/skills/schema-markup/SKILL.md +596 -0
  45. package/src/skills/marketingskills/skills/seo-audit/SKILL.md +384 -0
  46. package/src/skills/marketingskills/skills/signup-flow-cro/SKILL.md +355 -0
  47. package/src/skills/marketingskills/skills/social-content/SKILL.md +807 -0
  48. package/src/skills/obsidian-bases/SKILL.md +651 -0
  49. package/src/skills/obsidian-cli/SKILL.md +103 -0
  50. package/src/skills/obsidian-markdown/SKILL.md +620 -0
  51. package/src/skills/react-best-practices/SKILL.md +136 -0
  52. package/src/skills/refactoring/SKILL.md +119 -0
  53. package/src/skills/seo-content-writer/SKILL.md +661 -0
  54. package/src/skills/seo-content-writer/references/content-structure-templates.md +875 -0
  55. package/src/skills/seo-content-writer/references/title-formulas.md +339 -0
  56. package/src/skills/skill-creator/LICENSE.txt +202 -0
  57. package/src/skills/skill-creator/SKILL.md +357 -0
  58. package/src/skills/skill-creator/references/output-patterns.md +82 -0
  59. package/src/skills/skill-creator/references/workflows.md +28 -0
  60. package/src/skills/skill-creator/scripts/init_skill.py +303 -0
  61. package/src/skills/skill-creator/scripts/package_skill.py +110 -0
  62. package/src/skills/skill-creator/scripts/quick_validate.py +103 -0
  63. package/src/skills/systematic-debugging/CREATION-LOG.md +119 -0
  64. package/src/skills/systematic-debugging/SKILL.md +296 -0
  65. package/src/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  66. package/src/skills/systematic-debugging/condition-based-waiting.md +115 -0
  67. package/src/skills/systematic-debugging/defense-in-depth.md +122 -0
  68. package/src/skills/systematic-debugging/find-polluter.sh +63 -0
  69. package/src/skills/systematic-debugging/root-cause-tracing.md +169 -0
  70. package/src/skills/systematic-debugging/test-academic.md +14 -0
  71. package/src/skills/systematic-debugging/test-pressure-1.md +58 -0
  72. package/src/skills/systematic-debugging/test-pressure-2.md +68 -0
  73. package/src/skills/systematic-debugging/test-pressure-3.md +69 -0
  74. package/src/skills/ui-ux-pro-max/SKILL.md +386 -0
  75. package/src/skills/webhook-handler-patterns/SKILL.md +81 -0
  76. package/src/skills/webhook-handler-patterns/references/error-handling.md +299 -0
  77. package/src/skills/webhook-handler-patterns/references/frameworks/express.md +295 -0
  78. package/src/skills/webhook-handler-patterns/references/frameworks/fastapi.md +415 -0
  79. package/src/skills/webhook-handler-patterns/references/frameworks/nextjs.md +331 -0
  80. package/src/skills/webhook-handler-patterns/references/handler-sequence.md +51 -0
  81. package/src/skills/webhook-handler-patterns/references/idempotency.md +227 -0
  82. package/src/skills/webhook-handler-patterns/references/retry-logic.md +261 -0
  83. package/src/structure/00_Inbox/.gitkeep +0 -0
  84. package/src/structure/00_Inbox/Quick_Capture.md +5 -0
  85. package/src/structure/01_Projects/.gitkeep +0 -0
  86. package/src/structure/01_Projects/_Template Project/plan.md +55 -0
  87. package/src/structure/01_Projects/_Template Project/project_brief.md +45 -0
  88. package/src/structure/01_Projects/_Template Project/project_state.md +19 -0
  89. package/src/structure/02_Areas/Engine/Active_Context.md +126 -0
  90. package/src/structure/02_Areas/Engine/Auto_Learnings.md +36 -0
  91. package/src/structure/02_Areas/Engine/Dailies/.gitkeep +0 -0
  92. package/src/structure/02_Areas/Engine/Daily_Template.md +130 -0
  93. package/src/structure/02_Areas/Engine/Goals.md +65 -0
  94. package/src/structure/02_Areas/Engine/Identity_Prime_template.md +86 -0
  95. package/src/structure/02_Areas/Engine/Metrics_Log.md +45 -0
  96. package/src/structure/02_Areas/Engine/Monthly Reviews/.gitkeep +0 -0
  97. package/src/structure/02_Areas/Engine/Mover_Dossier.md +120 -0
  98. package/src/structure/02_Areas/Engine/Quarterly Reviews/.gitkeep +0 -0
  99. package/src/structure/02_Areas/Engine/Someday_Maybe.md +51 -0
  100. package/src/structure/02_Areas/Engine/Strategy_template.md +65 -0
  101. package/src/structure/02_Areas/Engine/Weekly Reviews/.gitkeep +0 -0
  102. package/src/structure/03_Library/Cheatsheets/.gitkeep +0 -0
  103. package/src/structure/03_Library/Entities/Decisions/_TEMPLATE.md +38 -0
  104. package/src/structure/03_Library/Entities/Entities MOC.md +49 -0
  105. package/src/structure/03_Library/Entities/Organizations/_TEMPLATE.md +28 -0
  106. package/src/structure/03_Library/Entities/People/_TEMPLATE.md +31 -0
  107. package/src/structure/03_Library/Entities/Places/_TEMPLATE.md +26 -0
  108. package/src/structure/03_Library/Inputs/.gitkeep +0 -0
  109. package/src/structure/03_Library/Inputs/Archive/.gitkeep +0 -0
  110. package/src/structure/03_Library/Inputs/Articles/.gitkeep +0 -0
  111. package/src/structure/03_Library/Inputs/Articles/_TEMPLATE.md +29 -0
  112. package/src/structure/03_Library/Inputs/Books/.gitkeep +0 -0
  113. package/src/structure/03_Library/Inputs/Books/_TEMPLATE.md +41 -0
  114. package/src/structure/03_Library/Inputs/Inputs MOC.md +40 -0
  115. package/src/structure/03_Library/Inputs/Videos/.gitkeep +0 -0
  116. package/src/structure/03_Library/Inputs/Videos/_TEMPLATE.md +29 -0
  117. package/src/structure/03_Library/MOCs/.gitkeep +0 -0
  118. package/src/structure/03_Library/Misc/.gitkeep +0 -0
  119. package/src/structure/03_Library/Principles/.gitkeep +0 -0
  120. package/src/structure/03_Library/Principles/Naval_Leverage.md +65 -0
  121. package/src/structure/03_Library/SOPs/Mover_OS_Architecture.md +74 -0
  122. package/src/structure/03_Library/SOPs/Tech_Doctrine.md +123 -0
  123. package/src/structure/03_Library/SOPs/Tech_Stack.md +55 -0
  124. package/src/structure/03_Library/SOPs/Zone_Operating.md +58 -0
  125. package/src/structure/04_Archives/.gitkeep +0 -0
  126. package/src/system/AI_INSTALL_MANIFEST.md +219 -0
  127. package/src/system/Mover_Global_Rules.md +646 -0
  128. package/src/system/V4_CONTEXT.md +223 -0
  129. package/src/workflows/analyse-day.md +376 -0
  130. package/src/workflows/context.md +24 -0
  131. package/src/workflows/debrief.md +199 -0
  132. package/src/workflows/debug-resistance.md +181 -0
  133. package/src/workflows/harvest.md +240 -0
  134. package/src/workflows/history.md +247 -0
  135. package/src/workflows/ignite.md +696 -0
  136. package/src/workflows/init-plan.md +16 -0
  137. package/src/workflows/log.md +314 -0
  138. package/src/workflows/morning.md +209 -0
  139. package/src/workflows/overview.md +203 -0
  140. package/src/workflows/pivot-strategy.md +218 -0
  141. package/src/workflows/plan-tomorrow.md +286 -0
  142. package/src/workflows/primer.md +209 -0
  143. package/src/workflows/project-notes.md +17 -0
  144. package/src/workflows/reboot.md +201 -0
  145. package/src/workflows/refactor-plan.md +135 -0
  146. package/src/workflows/review-week.md +537 -0
  147. package/src/workflows/setup.md +387 -0
  148. package/src/workflows/update.md +411 -0
  149. package/src/workflows/walkthrough.md +259 -0
package/install.js ADDED
@@ -0,0 +1,1424 @@
1
+ #!/usr/bin/env node
2
+ // ══════════════════════════════════════════════════════════════════════════════
3
+ // Mover OS — Interactive TUI Installer
4
+ // Zero dependencies. Node.js 18+.
5
+ //
6
+ // Usage:
7
+ // npx install-mover-os
8
+ // npx install-mover-os --key YOUR_KEY --vault ~/vault
9
+ // ══════════════════════════════════════════════════════════════════════════════
10
+
11
+ const readline = require("readline");
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+ const os = require("os");
15
+ const { execSync } = require("child_process");
16
+
17
+ const VERSION = "4";
18
+
19
+ // ─── ANSI ────────────────────────────────────────────────────────────────────
20
+ const IS_TTY = process.stdout.isTTY && process.stdin.isTTY;
21
+ const S = IS_TTY
22
+ ? {
23
+ reset: "\x1b[0m",
24
+ bold: "\x1b[1m",
25
+ dim: "\x1b[2m",
26
+ italic: "\x1b[3m",
27
+ underline: "\x1b[4m",
28
+ green: "\x1b[32m",
29
+ yellow: "\x1b[33m",
30
+ cyan: "\x1b[36m",
31
+ red: "\x1b[31m",
32
+ magenta: "\x1b[35m",
33
+ blue: "\x1b[34m",
34
+ white: "\x1b[37m",
35
+ gray: "\x1b[90m",
36
+ inverse: "\x1b[7m",
37
+ hide: "\x1b[?25l",
38
+ show: "\x1b[?25h",
39
+ // 256-color for gradient
40
+ fg: (n) => `\x1b[38;5;${n}m`,
41
+ }
42
+ : {
43
+ reset: "", bold: "", dim: "", italic: "", underline: "",
44
+ green: "", yellow: "", cyan: "", red: "", magenta: "", blue: "",
45
+ white: "", gray: "", inverse: "", hide: "", show: "",
46
+ fg: () => "",
47
+ };
48
+
49
+ const w = (s) => process.stdout.write(s);
50
+ const ln = (s = "") => w(s + "\n");
51
+ const bold = (s) => `${S.bold}${s}${S.reset}`;
52
+ const dim = (s) => `${S.dim}${s}${S.reset}`;
53
+ const green = (s) => `${S.green}${s}${S.reset}`;
54
+ const yellow = (s) => `${S.yellow}${s}${S.reset}`;
55
+ const cyan = (s) => `${S.cyan}${s}${S.reset}`;
56
+ const red = (s) => `${S.red}${s}${S.reset}`;
57
+ const gray = (s) => `${S.gray}${s}${S.reset}`;
58
+ const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
59
+
60
+ // Gradient text using 256-color palette (white → gray, matching website monochrome theme)
61
+ const GRADIENT = [255, 255, 254, 253, 252, 251, 250, 249, 248, 247];
62
+ function gradient(text) {
63
+ if (!IS_TTY) return text;
64
+ const chars = [...text];
65
+ return chars.map((ch, i) => {
66
+ if (ch === " ") return ch;
67
+ const idx = Math.floor((i / Math.max(chars.length - 1, 1)) * (GRADIENT.length - 1));
68
+ return `${S.fg(GRADIENT[idx])}${ch}`;
69
+ }).join("") + S.reset;
70
+ }
71
+
72
+ // ─── Terminal cleanup ────────────────────────────────────────────────────────
73
+ function cleanup() {
74
+ w(S.show);
75
+ try { if (process.stdin.isTTY && process.stdin.isRaw) process.stdin.setRawMode(false); } catch {}
76
+ }
77
+ process.on("exit", cleanup);
78
+ process.on("SIGINT", () => { cleanup(); ln(); process.exit(0); });
79
+ process.on("SIGTERM", () => { cleanup(); process.exit(0); });
80
+
81
+ // ─── Branded header ─────────────────────────────────────────────────────────
82
+ const LOGO = [
83
+ " ╔╦╗╔═╗╦ ╦╔═╗╦═╗ ╔═╗╔═╗",
84
+ " ║║║║ ║╚╗╔╝║╣ ╠╦╝ ║ ║╚═╗",
85
+ " ╩ ╩╚═╝ ╚╝ ╚═╝╩╚═ ╚═╝╚═╝",
86
+ ];
87
+
88
+ function printHeader() {
89
+ ln();
90
+ for (const line of LOGO) {
91
+ ln(gradient(line));
92
+ }
93
+ ln();
94
+ ln(` ${dim(`v${VERSION}`)} ${gray("the self-improving OS for AI agents")}`);
95
+ ln();
96
+ ln(gray(" ─────────────────────────────────────"));
97
+ ln();
98
+ }
99
+
100
+ // ─── Clack-style frame ──────────────────────────────────────────────────────
101
+ const BAR_COLOR = S.cyan;
102
+ const bar = () => w(`${BAR_COLOR}│${S.reset}`);
103
+ const barLn = (text = "") => ln(`${BAR_COLOR}│${S.reset} ${text}`);
104
+ const result = (text) => ln(`${S.green}◇${S.reset} ${dim(text)}`);
105
+ const question = (text) => ln(`${BAR_COLOR}◆${S.reset} ${text}`);
106
+ const outro = (text) => { ln(`${BAR_COLOR}└${S.reset} ${text}`); ln(); };
107
+
108
+ // ─── Animated spinner ───────────────────────────────────────────────────────
109
+ const SPIN_FRAMES = ["◐", "◓", "◑", "◒"];
110
+ function spinner(label) {
111
+ if (!IS_TTY) {
112
+ return { stop: (finalLabel) => { ln(`${BAR_COLOR}│${S.reset} ${S.green}\u2713${S.reset} ${finalLabel || label}`); } };
113
+ }
114
+ let i = 0;
115
+ w(S.hide);
116
+ w(`${BAR_COLOR}│${S.reset} ${S.cyan}${SPIN_FRAMES[0]}${S.reset} ${label}`);
117
+ const timer = setInterval(() => {
118
+ i = (i + 1) % SPIN_FRAMES.length;
119
+ w(`\x1b[2K\r${BAR_COLOR}│${S.reset} ${S.cyan}${SPIN_FRAMES[i]}${S.reset} ${label}`);
120
+ }, 80);
121
+ return {
122
+ stop: (finalLabel) => {
123
+ clearInterval(timer);
124
+ w(`\x1b[2K\r${BAR_COLOR}│${S.reset} ${S.green}\u2713${S.reset} ${finalLabel || label}\n`);
125
+ w(S.show);
126
+ },
127
+ };
128
+ }
129
+
130
+ // ─── UI: Text input (raw mode) ──────────────────────────────────────────────
131
+ function textInput({ label = "", initial = "", mask = null, placeholder = "" }) {
132
+ return new Promise((resolve) => {
133
+ question(label);
134
+
135
+ if (!IS_TTY) {
136
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
137
+ rl.question(`${BAR_COLOR}│${S.reset} `, (ans) => { rl.close(); resolve(ans || initial); });
138
+ return;
139
+ }
140
+
141
+ const { stdin } = process;
142
+ stdin.setRawMode(true);
143
+ stdin.resume();
144
+ stdin.setEncoding("utf8");
145
+
146
+ let value = initial;
147
+ let pos = value.length;
148
+
149
+ const render = () => {
150
+ w("\x1b[2K\r");
151
+ const prefix = `${BAR_COLOR}│${S.reset} `;
152
+ if (value.length === 0 && placeholder) {
153
+ w(prefix + dim(placeholder));
154
+ } else if (mask) {
155
+ w(prefix + mask.repeat(value.length));
156
+ } else {
157
+ w(prefix + value);
158
+ }
159
+ // Position cursor
160
+ if (!mask) {
161
+ const back = value.length - pos;
162
+ if (back > 0) w(`\x1b[${back}D`);
163
+ }
164
+ };
165
+
166
+ render();
167
+
168
+ const handler = (data) => {
169
+ if (data === "\r" || data === "\n") {
170
+ stdin.removeListener("data", handler);
171
+ stdin.setRawMode(false);
172
+ stdin.pause();
173
+ ln();
174
+ barLn();
175
+ if (mask) {
176
+ result("Entered");
177
+ } else {
178
+ result(value || initial);
179
+ }
180
+ resolve(value || initial);
181
+ return;
182
+ }
183
+ if (data === "\x03") { cleanup(); ln(); process.exit(0); }
184
+
185
+ if (data === "\x1b[C") { pos = Math.min(pos + 1, value.length); }
186
+ else if (data === "\x1b[D") { pos = Math.max(0, pos - 1); }
187
+ else if (data === "\x1b[H" || data === "\x01") { pos = 0; }
188
+ else if (data === "\x1b[F" || data === "\x05") { pos = value.length; }
189
+ else if (data === "\x15") { value = ""; pos = 0; }
190
+ else if (data === "\x7f" || data === "\x08") {
191
+ if (pos > 0) {
192
+ value = value.slice(0, pos - 1) + value.slice(pos);
193
+ pos--;
194
+ }
195
+ }
196
+ else if (data === "\x1b[3~") {
197
+ if (pos < value.length) {
198
+ value = value.slice(0, pos) + value.slice(pos + 1);
199
+ }
200
+ }
201
+ else if (data.startsWith("\x1b")) { /* ignore escape sequences */ }
202
+ else {
203
+ for (const ch of data) {
204
+ if (ch.charCodeAt(0) >= 32) {
205
+ value = value.slice(0, pos) + ch + value.slice(pos);
206
+ pos++;
207
+ }
208
+ }
209
+ }
210
+ render();
211
+ };
212
+
213
+ stdin.on("data", handler);
214
+ });
215
+ }
216
+
217
+ // ─── UI: Interactive select (raw mode) ───────────────────────────────────────
218
+ function interactiveSelect(items, { multi = false, preSelected = [], defaultIndex = 0 } = {}) {
219
+ return new Promise((resolve) => {
220
+ if (!IS_TTY) {
221
+ resolve(multi ? preSelected : items[defaultIndex]?.id);
222
+ return;
223
+ }
224
+
225
+ const { stdin } = process;
226
+ stdin.setRawMode(true);
227
+ stdin.resume();
228
+ stdin.setEncoding("utf8");
229
+ w(S.hide);
230
+
231
+ let cursor = defaultIndex;
232
+ const selected = new Set(preSelected);
233
+ let prevLines = 0;
234
+
235
+ const render = () => {
236
+ if (prevLines > 0) w(`\x1b[${prevLines}A`);
237
+
238
+ let lines = 0;
239
+ for (let i = 0; i < items.length; i++) {
240
+ const item = items[i];
241
+ const active = i === cursor;
242
+ const checked = selected.has(item.id);
243
+
244
+ const icon = multi
245
+ ? (checked ? green("◼") : dim("◻"))
246
+ : (active ? cyan("●") : dim("○"));
247
+ const label = active ? bold(item.name) : item.name;
248
+ const padded = strip(label).padEnd(20);
249
+ const styledPadded = active ? bold(padded) : padded;
250
+ // Color-coded tier badges
251
+ let tierLabel = "";
252
+ if (item.tier === "Full") tierLabel = `${S.green}Full${S.reset}`;
253
+ else if (item.tier === "Enhanced") tierLabel = `${S.cyan}Enhanced${S.reset}`;
254
+ else if (item.tier === "Basic+" || item.tier === "Basic") tierLabel = `${S.gray}${item.tier}${S.reset}`;
255
+ else if (item.tier === "recommended") tierLabel = `${S.green}recommended${S.reset}`;
256
+ else if (item.tier) tierLabel = dim(item.tier);
257
+
258
+ const tierStr = tierLabel ? strip(tierLabel).length : 0;
259
+ const tierPad = tierLabel ? tierLabel + " ".repeat(Math.max(0, 12 - tierStr)) : " ".repeat(12);
260
+ const tag = item._detected ? dim("(detected)") : "";
261
+
262
+ w(`\x1b[2K${BAR_COLOR}│${S.reset} ${icon} ${styledPadded}${tierPad}${tag}\n`);
263
+ lines++;
264
+ }
265
+
266
+ w(`\x1b[2K${BAR_COLOR}│${S.reset}\n`);
267
+ lines++;
268
+
269
+ const hint = multi
270
+ ? dim(" ↑↓ navigate space select a all enter confirm")
271
+ : dim(" ↑↓ navigate enter select");
272
+ w(`\x1b[2K${BAR_COLOR}│${S.reset}${hint}\n`);
273
+ lines++;
274
+
275
+ prevLines = lines;
276
+ };
277
+
278
+ render();
279
+
280
+ const handler = (data) => {
281
+ if (data === "\x1b[A") { cursor = (cursor - 1 + items.length) % items.length; }
282
+ else if (data === "\x1b[B") { cursor = (cursor + 1) % items.length; }
283
+ else if (data === " " && multi) {
284
+ const id = items[cursor].id;
285
+ if (selected.has(id)) selected.delete(id);
286
+ else selected.add(id);
287
+ }
288
+ else if ((data === "a" || data === "A") && multi) {
289
+ if (selected.size === items.length) selected.clear();
290
+ else items.forEach((i) => selected.add(i.id));
291
+ }
292
+ else if (data === "\r" || data === "\n") {
293
+ stdin.removeListener("data", handler);
294
+ stdin.setRawMode(false);
295
+ stdin.pause();
296
+
297
+ // Clear rendered lines
298
+ if (prevLines > 0) {
299
+ w(`\x1b[${prevLines}A`);
300
+ for (let i = 0; i < prevLines; i++) w("\x1b[2K\n");
301
+ w(`\x1b[${prevLines}A`);
302
+ }
303
+ w(S.show);
304
+
305
+ if (multi) {
306
+ const names = items.filter((i) => selected.has(i.id)).map((i) => i.name);
307
+ barLn();
308
+ result(names.length > 0 ? names.join(", ") : "None");
309
+ resolve([...selected]);
310
+ } else {
311
+ barLn();
312
+ result(items[cursor].name);
313
+ resolve(items[cursor].id);
314
+ }
315
+ return;
316
+ }
317
+ else if (data === "\x03") { cleanup(); ln(); process.exit(0); }
318
+
319
+ render();
320
+ };
321
+
322
+ stdin.on("data", handler);
323
+ });
324
+ }
325
+
326
+ // ─── License validation ─────────────────────────────────────────────────────
327
+ const VALID_KEYS = new Set(["MOVER-BETA-2026", "MOVER-FOUNDER-001"]);
328
+ function validateKey(key) {
329
+ if (!key) return false;
330
+ return VALID_KEYS.has(key.toUpperCase().trim());
331
+ }
332
+
333
+ // ─── CLI ─────────────────────────────────────────────────────────────────────
334
+ function parseArgs() {
335
+ const args = process.argv.slice(2);
336
+ const opts = { vault: "", key: "" };
337
+ for (let i = 0; i < args.length; i++) {
338
+ if (args[i] === "--vault" && args[i + 1]) opts.vault = args[++i];
339
+ else if (args[i] === "--key" && args[i + 1]) opts.key = args[++i];
340
+ else if (args[i] === "--help" || args[i] === "-h") {
341
+ ln();
342
+ ln(` ${bold("mover os")} ${dim("installer")}`);
343
+ ln();
344
+ ln(` ${dim("Usage")} npx install-mover-os`);
345
+ ln();
346
+ ln(` ${dim("Options")}`);
347
+ ln(` --key KEY License key (skip interactive prompt)`);
348
+ ln(` --vault PATH Obsidian vault path (skip detection)`);
349
+ ln();
350
+ process.exit(0);
351
+ }
352
+ }
353
+ return opts;
354
+ }
355
+
356
+ // ─── Engine file detection ──────────────────────────────────────────────────
357
+ function detectEngineFiles(vaultPath) {
358
+ const engineDir = path.join(vaultPath, "02_Areas", "Engine");
359
+ if (!fs.existsSync(engineDir)) return { exists: false, files: [] };
360
+ const coreFiles = [
361
+ "Identity_Prime.md", "Strategy.md", "Active_Context.md",
362
+ "Mover_Dossier.md", "Auto_Learnings.md", "Goals.md",
363
+ ];
364
+ const found = coreFiles.filter((f) => {
365
+ const p = path.join(engineDir, f);
366
+ if (!fs.existsSync(p)) return false;
367
+ // Template files have placeholder content — check if user has actually written to them
368
+ try {
369
+ const content = fs.readFileSync(p, "utf8");
370
+ return content.length > 200; // Templates are ~100-200 chars of scaffolding
371
+ } catch { return false; }
372
+ });
373
+ return { exists: found.length > 0, files: found };
374
+ }
375
+
376
+ // ─── Uninstall ──────────────────────────────────────────────────────────────
377
+ async function runUninstall(vaultPath) {
378
+ ln();
379
+ ln(gradient(" ╔╦╗╔═╗╦ ╦╔═╗╦═╗ ╔═╗╔═╗"));
380
+ ln(gradient(" ║║║║ ║╚╗╔╝║╣ ╠╦╝ ║ ║╚═╗"));
381
+ ln(gradient(" ╩ ╩╚═╝ ╚╝ ╚═╝╩╚═ ╚═╝╚═╝"));
382
+ ln();
383
+ barLn(dim("uninstalling agent configs..."));
384
+ barLn();
385
+
386
+ const home = os.homedir();
387
+ let removed = 0;
388
+
389
+ // Agent configs
390
+ const agentPaths = [
391
+ { label: "Claude Code rules", path: path.join(home, ".claude", "CLAUDE.md") },
392
+ { label: "Claude Code commands", path: path.join(home, ".claude", "commands"), dir: true },
393
+ { label: "Claude Code skills", path: path.join(home, ".claude", "skills"), dir: true, keepBuiltins: true },
394
+ { label: "Cursor rules", path: path.join(home, ".cursor", "rules", "mover-os.mdc") },
395
+ { label: "Cursor commands", path: path.join(home, ".cursor", "commands"), dir: true },
396
+ { label: "Cursor skills", path: path.join(home, ".cursor", "skills"), dir: true },
397
+ { label: "Antigravity rules", path: path.join(home, ".gemini", "GEMINI.md") },
398
+ { label: "Antigravity workflows", path: path.join(home, ".gemini", "antigravity", "global_workflows"), dir: true },
399
+ { label: "Codex instructions", path: path.join(home, ".codex", "instructions.md") },
400
+ { label: "Claude Code hooks", path: path.join(home, ".claude", "hooks"), dir: true },
401
+ ];
402
+
403
+ for (const item of agentPaths) {
404
+ if (!fs.existsSync(item.path)) continue;
405
+ try {
406
+ if (item.dir) {
407
+ if (item.keepBuiltins) {
408
+ // Only remove skill directories that match our installed skills
409
+ for (const entry of fs.readdirSync(item.path, { withFileTypes: true })) {
410
+ if (entry.isDirectory() && fs.existsSync(path.join(item.path, entry.name, "SKILL.md"))) {
411
+ fs.rmSync(path.join(item.path, entry.name), { recursive: true, force: true });
412
+ }
413
+ }
414
+ } else {
415
+ fs.rmSync(item.path, { recursive: true, force: true });
416
+ }
417
+ } else {
418
+ fs.unlinkSync(item.path);
419
+ }
420
+ barLn(`${green("\u2713")} ${dim(item.label)}`);
421
+ removed++;
422
+ } catch {}
423
+ }
424
+
425
+ // Vault-specific files (only if vault path given)
426
+ if (vaultPath && fs.existsSync(vaultPath)) {
427
+ const vaultFiles = [
428
+ { label: "AGENTS.md", path: path.join(vaultPath, "AGENTS.md") },
429
+ { label: "SOUL.md", path: path.join(vaultPath, "SOUL.md") },
430
+ { label: ".mover-version", path: path.join(vaultPath, ".mover-version") },
431
+ { label: "Claude settings", path: path.join(vaultPath, ".claude", "settings.json") },
432
+ ];
433
+
434
+ const vaultAgentFiles = [
435
+ { label: ".cursorrules", path: path.join(vaultPath, ".cursorrules") },
436
+ { label: ".clinerules", path: path.join(vaultPath, ".clinerules"), dir: true },
437
+ { label: ".windsurfrules", path: path.join(vaultPath, ".windsurfrules") },
438
+ { label: "AMP.md", path: path.join(vaultPath, "AMP.md") },
439
+ { label: ".aider.conf.yml", path: path.join(vaultPath, ".aider.conf.yml") },
440
+ ];
441
+
442
+ for (const item of [...vaultFiles, ...vaultAgentFiles]) {
443
+ if (!fs.existsSync(item.path)) continue;
444
+ try {
445
+ if (item.dir) {
446
+ fs.rmSync(item.path, { recursive: true, force: true });
447
+ } else {
448
+ fs.unlinkSync(item.path);
449
+ }
450
+ barLn(`${green("\u2713")} ${dim(item.label)}`);
451
+ removed++;
452
+ } catch {}
453
+ }
454
+ }
455
+
456
+ barLn();
457
+ if (removed > 0) {
458
+ barLn(dim("Engine files and vault structure left intact."));
459
+ barLn(dim("Your Identity, Strategy, and Daily Notes are untouched."));
460
+ } else {
461
+ barLn(dim("Nothing to remove — Mover OS not detected."));
462
+ }
463
+ barLn();
464
+ outro(`${green("Done.")} ${removed} items removed.`);
465
+ }
466
+
467
+ // ─── Agent definitions ──────────────────────────────────────────────────────
468
+ const AGENTS = [
469
+ {
470
+ id: "claude-code",
471
+ name: "Claude Code",
472
+ tier: "Full",
473
+ detect: () => cmdExists("claude") || fs.existsSync(path.join(os.homedir(), ".claude")),
474
+ },
475
+ {
476
+ id: "cursor",
477
+ name: "Cursor",
478
+ tier: "Full",
479
+ detect: () => fs.existsSync(path.join(os.homedir(), ".cursor")) || cmdExists("cursor"),
480
+ },
481
+ {
482
+ id: "cline",
483
+ name: "Cline",
484
+ tier: "Full",
485
+ detect: () => globDirExists(path.join(os.homedir(), ".vscode", "extensions"), "saoudrizwan.claude-dev-*"),
486
+ },
487
+ {
488
+ id: "codex",
489
+ name: "Codex CLI",
490
+ tier: "Enhanced",
491
+ detect: () => cmdExists("codex") || fs.existsSync(path.join(os.homedir(), ".codex")),
492
+ },
493
+ {
494
+ id: "windsurf",
495
+ name: "Windsurf",
496
+ tier: "Enhanced",
497
+ detect: () => fs.existsSync(path.join(os.homedir(), ".windsurf")) || cmdExists("windsurf"),
498
+ },
499
+ {
500
+ id: "openclaw",
501
+ name: "OpenClaw",
502
+ tier: "Enhanced",
503
+ detect: () => fs.existsSync(path.join(os.homedir(), ".openclaw")) || cmdExists("openclaw"),
504
+ },
505
+ {
506
+ id: "antigravity",
507
+ name: "Antigravity",
508
+ tier: "Basic+",
509
+ detect: () => fs.existsSync(path.join(os.homedir(), ".gemini")) || cmdExists("gemini"),
510
+ },
511
+ {
512
+ id: "roo-code",
513
+ name: "Roo Code",
514
+ tier: "Basic",
515
+ detect: () => globDirExists(path.join(os.homedir(), ".vscode", "extensions"), "rooveterinaryinc.roo-cline-*"),
516
+ },
517
+ {
518
+ id: "copilot",
519
+ name: "GitHub Copilot",
520
+ tier: "Basic",
521
+ detect: () => cmdExists("gh"),
522
+ },
523
+ {
524
+ id: "amp",
525
+ name: "Amp",
526
+ tier: "Basic",
527
+ detect: () => cmdExists("amp") || fs.existsSync(path.join(os.homedir(), ".amp")),
528
+ },
529
+ {
530
+ id: "aider",
531
+ name: "Aider",
532
+ tier: "Basic",
533
+ detect: () => cmdExists("aider"),
534
+ },
535
+ ];
536
+
537
+ // ─── Utility functions ──────────────────────────────────────────────────────
538
+ function cmdExists(cmd) {
539
+ try {
540
+ execSync(process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`, { stdio: "ignore" });
541
+ return true;
542
+ } catch {
543
+ return false;
544
+ }
545
+ }
546
+
547
+ function globDirExists(dir, pattern) {
548
+ if (!fs.existsSync(dir)) return false;
549
+ const prefix = pattern.replace("*", "");
550
+ try {
551
+ return fs.readdirSync(dir).some((e) => e.startsWith(prefix));
552
+ } catch {
553
+ return false;
554
+ }
555
+ }
556
+
557
+ function linkOrCopy(src, dest) {
558
+ try {
559
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
560
+ if (fs.existsSync(dest)) fs.unlinkSync(dest);
561
+ fs.linkSync(src, dest);
562
+ return "linked";
563
+ } catch {
564
+ try {
565
+ fs.copyFileSync(src, dest);
566
+ return "copied";
567
+ } catch {
568
+ return null;
569
+ }
570
+ }
571
+ }
572
+
573
+ function copyDirRecursive(src, dest) {
574
+ fs.mkdirSync(dest, { recursive: true });
575
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
576
+ const s = path.join(src, entry.name);
577
+ const d = path.join(dest, entry.name);
578
+ if (entry.isDirectory()) {
579
+ copyDirRecursive(s, d);
580
+ } else {
581
+ fs.copyFileSync(s, d);
582
+ }
583
+ }
584
+ }
585
+
586
+ function findSkills(bundleDir) {
587
+ const skillsDir = path.join(bundleDir, "src", "skills");
588
+ if (!fs.existsSync(skillsDir)) return [];
589
+ const skills = [];
590
+ const walk = (dir) => {
591
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
592
+ const full = path.join(dir, entry.name);
593
+ if (entry.isDirectory()) {
594
+ if (fs.existsSync(path.join(full, "SKILL.md"))) {
595
+ skills.push({ name: entry.name, path: full });
596
+ } else {
597
+ walk(full);
598
+ }
599
+ }
600
+ }
601
+ };
602
+ walk(skillsDir);
603
+ return skills;
604
+ }
605
+
606
+ // ─── AGENTS.md generator ────────────────────────────────────────────────────
607
+ function generateAgentsMd() {
608
+ return `# AGENTS.md
609
+
610
+ > Mover OS — The Agentic Operating System for Obsidian
611
+
612
+ ## What This Is
613
+
614
+ You are operating within **Mover OS**, an AI-powered execution engine built on Obsidian. You audit behavior against stated strategy, extract reusable knowledge, and evolve via user corrections.
615
+
616
+ ## Core Rules
617
+
618
+ 1. **Read before acting.** Load Engine files (02_Areas/Engine/) before any strategic operation.
619
+ 2. **Cite sources.** Every claim needs \`[file:line]\` or \`[INFERRED: reason]\`. Confidence < 3 = ask.
620
+ 3. **Append-only.** Never delete from plan.md, Auto_Learnings.md, or Daily Notes.
621
+ 4. **Grounded output.** No fabricated answers. "I don't know" beats a guess.
622
+ 5. **Engine protection.** Never overwrite Identity_Prime.md, Strategy.md, or other Engine files without explicit approval.
623
+
624
+ ## Project Structure
625
+
626
+ \`\`\`
627
+ 00_Inbox/ # Quick capture
628
+ 01_Projects/ # Active projects with plan.md + project_state.md
629
+ 02_Areas/Engine/ # THE CORE — Identity, Strategy, Context, Dossier
630
+ 03_Library/ # Permanent knowledge (MOCs, SOPs, Principles)
631
+ 04_Archives/ # Completed projects
632
+ \`\`\`
633
+
634
+ ## Engine Files (Priority Order)
635
+
636
+ | Priority | File | Purpose |
637
+ |----------|------|---------|
638
+ | P0 | Auto_Learnings.md | AI corrections & behavioral patterns |
639
+ | P1 | Active_Context.md | Current state, blockers, energy |
640
+ | P2 | Strategy.md | Business/life hypothesis being tested |
641
+ | P3 | Identity_Prime.md | User persona, values, anti-identity |
642
+ | P4 | project_state.md | Project-specific knowledge |
643
+ | P5 | Yesterday's Daily Note | Recent session history |
644
+
645
+ ## Behavior
646
+
647
+ - Be direct. No hedging, no "Great question!", no emdashes.
648
+ - Think like a co-founder, not a consultant.
649
+ - When wrong, name it, fix it, move on.
650
+ - Comment WHY, not WHAT.
651
+ - Have opinions. "It depends" is a cop-out.
652
+
653
+ ## Workflows
654
+
655
+ Available slash commands: /setup, /walkthrough, /plan-tomorrow, /analyse-day, /log, /review-week, /morning, /overview, /ignite, /harvest, /debug-resistance, /pivot-strategy, /reboot, /refactor-plan
656
+
657
+ Each workflow hands off to the next. Follow the breadcrumbs.
658
+
659
+ ## Daily Flow
660
+
661
+ \`\`\`
662
+ /morning → [WORK] → /log → /analyse-day → /plan-tomorrow
663
+ Sunday: /review-week first
664
+ New project: /ignite → [WORK]
665
+ Stuck: /debug-resistance
666
+ \`\`\`
667
+ `;
668
+ }
669
+
670
+ // ─── SOUL.md generator (OpenClaw) ───────────────────────────────────────────
671
+ function generateSoulMd() {
672
+ return `# SOUL.md — Mover OS
673
+
674
+ > You are not a chatbot. You are a co-founder who happens to live inside a terminal.
675
+
676
+ ## Core Truths
677
+
678
+ - Be genuinely helpful, not performatively helpful. If the answer is "don't build that," say it.
679
+ - Have opinions. "It depends" is a cop-out. Pick a direction, defend it, change your mind if the user makes a better case.
680
+ - Be resourceful before asking. Exhaust what you can figure out, then ask sharp questions — not broad ones.
681
+ - Earn trust through competence, not compliance. Nobody respects a yes-man.
682
+
683
+ ## The Vibe
684
+
685
+ - Talk like a co-founder who's been in the trenches, not a consultant billing by the hour.
686
+ - One sharp sentence beats three careful ones. Say what you mean.
687
+ - When you're wrong, don't grovel. Name it, fix it, move. Apologies waste both your time.
688
+ - Match intensity to stakes. Casual for small tasks. Locked in for architecture. Blunt for bad ideas.
689
+
690
+ ## Boundaries
691
+
692
+ - Never hedge to avoid being wrong. Pick a position.
693
+ - Never pretend all approaches are equally valid when one is clearly better.
694
+ - Never pad output to look thorough. Substance only.
695
+ - Never be artificially enthusiastic about bad ideas.
696
+
697
+ ## Continuity
698
+
699
+ - You start fresh each session but the files are your memory. Read them, trust them, build on them.
700
+ - If something changed between sessions and you don't understand why, ask — don't assume.
701
+ - The user corrects you to make you better. Take corrections seriously and encode them.
702
+ `;
703
+ }
704
+
705
+ // ─── Claude Code hooks (settings.json) ──────────────────────────────────────
706
+ function generateClaudeSettings() {
707
+ return JSON.stringify(
708
+ {
709
+ hooks: {
710
+ Stop: [
711
+ {
712
+ hooks: [
713
+ { type: "command", command: 'bash "$HOME/.claude/hooks/session-log-reminder.sh"', timeout: 10 },
714
+ { type: "command", command: 'bash "$HOME/.claude/hooks/dirty-tree-guard.sh"', timeout: 10 },
715
+ { type: "command", command: 'bash "$HOME/.claude/hooks/context-staleness.sh"', timeout: 5 },
716
+ ],
717
+ },
718
+ ],
719
+ PreToolUse: [
720
+ {
721
+ matcher: "Write|Edit",
722
+ hooks: [{ type: "command", command: 'bash "$HOME/.claude/hooks/engine-protection.sh"', timeout: 5 }],
723
+ },
724
+ {
725
+ matcher: "Bash",
726
+ hooks: [{ type: "command", command: 'bash "$HOME/.claude/hooks/git-safety.sh"', timeout: 5 }],
727
+ },
728
+ ],
729
+ PreCompact: [
730
+ {
731
+ hooks: [{ type: "command", command: 'bash "$HOME/.claude/hooks/pre-compact-backup.sh"', timeout: 15 }],
732
+ },
733
+ ],
734
+ },
735
+ },
736
+ null,
737
+ 2
738
+ );
739
+ }
740
+
741
+ // ─── .gitignore ─────────────────────────────────────────────────────────────
742
+ function generateGitignore() {
743
+ return `# Mover OS — protected from git
744
+ 02_Areas/Engine/Dailies/
745
+ 02_Areas/Engine/Weekly Reviews/
746
+ .obsidian/
747
+ .trash/
748
+ dev/
749
+ `;
750
+ }
751
+
752
+ // ─── Install functions ──────────────────────────────────────────────────────
753
+ function createVaultStructure(vaultPath) {
754
+ const dirs = [
755
+ "00_Inbox",
756
+ "01_Projects",
757
+ "01_Projects/_Template Project",
758
+ "02_Areas/Engine",
759
+ "02_Areas/Engine/Dailies",
760
+ "02_Areas/Engine/Weekly Reviews",
761
+ "03_Library/Cheatsheets",
762
+ "03_Library/Inputs",
763
+ "03_Library/MOCs",
764
+ "03_Library/Principles",
765
+ "03_Library/Scripts",
766
+ "03_Library/SOPs",
767
+ "04_Archives",
768
+ "Templates",
769
+ ];
770
+
771
+ let created = 0;
772
+ for (const dir of dirs) {
773
+ const full = path.join(vaultPath, dir);
774
+ if (!fs.existsSync(full)) {
775
+ fs.mkdirSync(full, { recursive: true });
776
+ created++;
777
+ }
778
+ }
779
+ return created;
780
+ }
781
+
782
+ function installTemplateFiles(bundleDir, vaultPath) {
783
+ const structDir = path.join(bundleDir, "src", "structure");
784
+ if (!fs.existsSync(structDir)) return 0;
785
+
786
+ let count = 0;
787
+ const walk = (dir, rel) => {
788
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
789
+ const srcPath = path.join(dir, entry.name);
790
+ const destPath = path.join(vaultPath, rel, entry.name);
791
+
792
+ if (entry.isDirectory()) {
793
+ walk(srcPath, path.join(rel, entry.name));
794
+ } else {
795
+ const relNorm = rel.replace(/\\/g, "/");
796
+ if (relNorm.includes("02_Areas") && relNorm.includes("Engine") && fs.existsSync(destPath)) {
797
+ continue;
798
+ }
799
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
800
+ if (!fs.existsSync(destPath)) {
801
+ fs.copyFileSync(srcPath, destPath);
802
+ count++;
803
+ }
804
+ }
805
+ }
806
+ };
807
+ walk(structDir, "");
808
+ return count;
809
+ }
810
+
811
+ function installWorkflows(bundleDir, destDir) {
812
+ const srcDir = path.join(bundleDir, "src", "workflows");
813
+ if (!fs.existsSync(srcDir)) return 0;
814
+
815
+ fs.mkdirSync(destDir, { recursive: true });
816
+ let count = 0;
817
+ for (const file of fs.readdirSync(srcDir).filter((f) => f.endsWith(".md"))) {
818
+ if (linkOrCopy(path.join(srcDir, file), path.join(destDir, file))) count++;
819
+ }
820
+ return count;
821
+ }
822
+
823
+ function installRules(bundleDir, destPath) {
824
+ const src = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
825
+ if (!fs.existsSync(src)) return false;
826
+ return linkOrCopy(src, destPath) !== null;
827
+ }
828
+
829
+ function installSkillPacks(bundleDir, destDir) {
830
+ const skills = findSkills(bundleDir);
831
+ fs.mkdirSync(destDir, { recursive: true });
832
+ let count = 0;
833
+ for (const skill of skills) {
834
+ const dest = path.join(destDir, skill.name);
835
+ if (fs.existsSync(dest)) fs.rmSync(dest, { recursive: true, force: true });
836
+ copyDirRecursive(skill.path, dest);
837
+ count++;
838
+ }
839
+ return count;
840
+ }
841
+
842
+ function installHooksForClaude(bundleDir, vaultPath) {
843
+ const hooksSrc = path.join(bundleDir, "src", "hooks");
844
+ const hooksDst = path.join(os.homedir(), ".claude", "hooks");
845
+ const settingsDir = path.join(vaultPath, ".claude");
846
+
847
+ if (!fs.existsSync(hooksSrc)) return 0;
848
+
849
+ fs.mkdirSync(hooksDst, { recursive: true });
850
+ fs.mkdirSync(settingsDir, { recursive: true });
851
+
852
+ let count = 0;
853
+ for (const file of fs.readdirSync(hooksSrc).filter((f) => f.endsWith(".sh"))) {
854
+ fs.copyFileSync(path.join(hooksSrc, file), path.join(hooksDst, file));
855
+ count++;
856
+ }
857
+
858
+ const settingsPath = path.join(settingsDir, "settings.json");
859
+ if (!fs.existsSync(settingsPath)) {
860
+ fs.writeFileSync(settingsPath, generateClaudeSettings(), "utf8");
861
+ }
862
+
863
+ return count;
864
+ }
865
+
866
+ // ─── Per-agent install orchestrators ────────────────────────────────────────
867
+ function installClaudeCode(bundleDir, vaultPath) {
868
+ const home = os.homedir();
869
+ const steps = [];
870
+
871
+ const claudeDir = path.join(home, ".claude");
872
+ fs.mkdirSync(claudeDir, { recursive: true });
873
+ if (installRules(bundleDir, path.join(claudeDir, "CLAUDE.md"))) steps.push("rules");
874
+
875
+ const cmdsDir = path.join(claudeDir, "commands");
876
+ const wfCount = installWorkflows(bundleDir, cmdsDir);
877
+ if (wfCount > 0) steps.push(`${wfCount} commands`);
878
+
879
+ const skillsDir = path.join(claudeDir, "skills");
880
+ const skCount = installSkillPacks(bundleDir, skillsDir);
881
+ if (skCount > 0) steps.push(`${skCount} skills`);
882
+
883
+ if (vaultPath) {
884
+ const hkCount = installHooksForClaude(bundleDir, vaultPath);
885
+ if (hkCount > 0) steps.push(`${hkCount} hooks`);
886
+ }
887
+
888
+ return steps;
889
+ }
890
+
891
+ function installCursor(bundleDir, vaultPath) {
892
+ const home = os.homedir();
893
+ const steps = [];
894
+
895
+ if (vaultPath) {
896
+ if (installRules(bundleDir, path.join(vaultPath, ".cursorrules"))) steps.push("rules");
897
+ }
898
+
899
+ const cursorRulesDir = path.join(home, ".cursor", "rules");
900
+ fs.mkdirSync(cursorRulesDir, { recursive: true });
901
+ const src = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
902
+ if (fs.existsSync(src)) {
903
+ fs.copyFileSync(src, path.join(cursorRulesDir, "mover-os.mdc"));
904
+ }
905
+
906
+ const cmdsDir = path.join(home, ".cursor", "commands");
907
+ const wfCount = installWorkflows(bundleDir, cmdsDir);
908
+ if (wfCount > 0) steps.push(`${wfCount} commands`);
909
+
910
+ const skillsDir = path.join(home, ".cursor", "skills");
911
+ const skCount = installSkillPacks(bundleDir, skillsDir);
912
+ if (skCount > 0) steps.push(`${skCount} skills`);
913
+
914
+ return steps;
915
+ }
916
+
917
+ function installCline(bundleDir, vaultPath) {
918
+ const steps = [];
919
+
920
+ if (vaultPath) {
921
+ const rulesDir = path.join(vaultPath, ".clinerules");
922
+ fs.mkdirSync(rulesDir, { recursive: true });
923
+ const src = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
924
+ if (fs.existsSync(src)) {
925
+ fs.copyFileSync(src, path.join(rulesDir, "mover-os.md"));
926
+ steps.push("rules");
927
+ }
928
+
929
+ const skillsDir = path.join(vaultPath, ".cline", "skills");
930
+ const skCount = installSkillPacks(bundleDir, skillsDir);
931
+ if (skCount > 0) steps.push(`${skCount} skills`);
932
+ }
933
+
934
+ return steps;
935
+ }
936
+
937
+ function installCodex(bundleDir, vaultPath) {
938
+ const home = os.homedir();
939
+ const steps = [];
940
+
941
+ if (vaultPath) {
942
+ fs.writeFileSync(path.join(vaultPath, "AGENTS.md"), generateAgentsMd(), "utf8");
943
+ steps.push("AGENTS.md");
944
+ }
945
+
946
+ const codexDir = path.join(home, ".codex");
947
+ fs.mkdirSync(codexDir, { recursive: true });
948
+ if (installRules(bundleDir, path.join(codexDir, "instructions.md"))) steps.push("global instructions");
949
+
950
+ return steps;
951
+ }
952
+
953
+ function installWindsurf(bundleDir, vaultPath) {
954
+ const steps = [];
955
+ if (vaultPath) {
956
+ if (installRules(bundleDir, path.join(vaultPath, ".windsurfrules"))) steps.push("rules");
957
+ }
958
+ return steps;
959
+ }
960
+
961
+ function installOpenClaw(bundleDir, vaultPath) {
962
+ const home = os.homedir();
963
+ const workspace = path.join(home, ".openclaw", "workspace");
964
+ const steps = [];
965
+
966
+ fs.mkdirSync(path.join(workspace, "skills"), { recursive: true });
967
+ fs.mkdirSync(path.join(workspace, "memory"), { recursive: true });
968
+
969
+ fs.writeFileSync(path.join(workspace, "AGENTS.md"), generateAgentsMd(), "utf8");
970
+ steps.push("AGENTS.md");
971
+
972
+ fs.writeFileSync(path.join(workspace, "SOUL.md"), generateSoulMd(), "utf8");
973
+ steps.push("SOUL.md");
974
+
975
+ const userMd = path.join(workspace, "USER.md");
976
+ if (!fs.existsSync(userMd)) {
977
+ fs.writeFileSync(
978
+ userMd,
979
+ `# USER.md\n\n> Run \`/setup\` in Mover OS to populate this file with your Identity and Strategy.\n\n## Identity\n<!-- Populated by /setup -->\n\n## Strategy\n<!-- Populated by /setup -->\n\n## Assets\n<!-- Populated by /setup -->\n`,
980
+ "utf8"
981
+ );
982
+ steps.push("USER.md");
983
+ }
984
+
985
+ const skCount = installSkillPacks(bundleDir, path.join(workspace, "skills"));
986
+ if (skCount > 0) steps.push(`${skCount} skills`);
987
+
988
+ return steps;
989
+ }
990
+
991
+ function installAntigravity(bundleDir) {
992
+ const home = os.homedir();
993
+ const steps = [];
994
+
995
+ const geminiDir = path.join(home, ".gemini");
996
+ fs.mkdirSync(geminiDir, { recursive: true });
997
+ if (installRules(bundleDir, path.join(geminiDir, "GEMINI.md"))) steps.push("rules");
998
+
999
+ const wfDir = path.join(geminiDir, "antigravity", "global_workflows");
1000
+ const wfCount = installWorkflows(bundleDir, wfDir);
1001
+ if (wfCount > 0) steps.push(`${wfCount} workflows`);
1002
+
1003
+ return steps;
1004
+ }
1005
+
1006
+ function installRooCode(bundleDir, vaultPath) {
1007
+ const steps = [];
1008
+ if (vaultPath) {
1009
+ const rulesDir = path.join(vaultPath, ".roo", "rules");
1010
+ fs.mkdirSync(rulesDir, { recursive: true });
1011
+ const src = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
1012
+ if (fs.existsSync(src)) {
1013
+ fs.copyFileSync(src, path.join(rulesDir, "mover-os.md"));
1014
+ steps.push("rules");
1015
+ }
1016
+ }
1017
+ return steps;
1018
+ }
1019
+
1020
+ function installCopilot(bundleDir, vaultPath) {
1021
+ const steps = [];
1022
+ if (vaultPath) {
1023
+ const ghDir = path.join(vaultPath, ".github");
1024
+ fs.mkdirSync(ghDir, { recursive: true });
1025
+ const src = path.join(bundleDir, "src", "system", "Mover_Global_Rules.md");
1026
+ if (fs.existsSync(src)) {
1027
+ fs.copyFileSync(src, path.join(ghDir, "copilot-instructions.md"));
1028
+ steps.push("rules");
1029
+ }
1030
+ }
1031
+ return steps;
1032
+ }
1033
+
1034
+ function installAmp(bundleDir, vaultPath) {
1035
+ const steps = [];
1036
+ if (vaultPath) {
1037
+ fs.writeFileSync(path.join(vaultPath, "AGENTS.md"), generateAgentsMd(), "utf8");
1038
+ steps.push("AGENTS.md");
1039
+ if (installRules(bundleDir, path.join(vaultPath, "AMP.md"))) steps.push("AMP.md");
1040
+ }
1041
+ return steps;
1042
+ }
1043
+
1044
+ function installAider(bundleDir, vaultPath) {
1045
+ const steps = [];
1046
+ if (vaultPath) {
1047
+ const agentsMdPath = path.join(vaultPath, "AGENTS.md");
1048
+ if (!fs.existsSync(agentsMdPath)) {
1049
+ fs.writeFileSync(agentsMdPath, generateAgentsMd(), "utf8");
1050
+ }
1051
+ steps.push("AGENTS.md");
1052
+
1053
+ const aiderConf = path.join(vaultPath, ".aider.conf.yml");
1054
+ if (!fs.existsSync(aiderConf)) {
1055
+ fs.writeFileSync(aiderConf, "# Mover OS — Aider configuration\nread:\n - AGENTS.md\n", "utf8");
1056
+ steps.push(".aider.conf.yml");
1057
+ }
1058
+ }
1059
+ return steps;
1060
+ }
1061
+
1062
+ const AGENT_INSTALLERS = {
1063
+ "claude-code": installClaudeCode,
1064
+ cursor: installCursor,
1065
+ cline: installCline,
1066
+ codex: installCodex,
1067
+ windsurf: installWindsurf,
1068
+ openclaw: installOpenClaw,
1069
+ antigravity: installAntigravity,
1070
+ "roo-code": installRooCode,
1071
+ copilot: installCopilot,
1072
+ amp: installAmp,
1073
+ aider: installAider,
1074
+ };
1075
+
1076
+ // ─── Pre-flight checks ──────────────────────────────────────────────────────
1077
+ function preflight() {
1078
+ const issues = [];
1079
+
1080
+ // Node version
1081
+ const nodeVer = parseInt(process.versions.node.split(".")[0], 10);
1082
+ if (nodeVer < 18) {
1083
+ issues.push({ label: "Node.js", status: "fail", detail: `v${process.versions.node} (need 18+)` });
1084
+ } else {
1085
+ issues.push({ label: "Node.js", status: "ok", detail: `v${process.versions.node}` });
1086
+ }
1087
+
1088
+ // Git
1089
+ const hasGit = cmdExists("git");
1090
+ issues.push({ label: "git", status: hasGit ? "ok" : "warn", detail: hasGit ? "found" : "not found (optional)" });
1091
+
1092
+ // OS
1093
+ const plat = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
1094
+ issues.push({ label: "Platform", status: "ok", detail: plat });
1095
+
1096
+ return issues;
1097
+ }
1098
+
1099
+ // ─── Main ───────────────────────────────────────────────────────────────────
1100
+ async function main() {
1101
+ const opts = parseArgs();
1102
+ const bundleDir = path.resolve(__dirname);
1103
+ const startTime = Date.now();
1104
+
1105
+ // ── Intro ──
1106
+ printHeader();
1107
+
1108
+ // ── Pre-flight ──
1109
+ barLn(gray("Pre-flight"));
1110
+ barLn();
1111
+ const checks = preflight();
1112
+ for (const c of checks) {
1113
+ const icon = c.status === "ok" ? green("\u2713") : c.status === "warn" ? yellow("\u25CB") : red("\u2717");
1114
+ barLn(`${icon} ${dim(`${c.label} ${c.detail}`)}`);
1115
+ }
1116
+ barLn();
1117
+
1118
+ if (checks.some((c) => c.status === "fail")) {
1119
+ outro(red("Pre-flight failed. Fix the issues above."));
1120
+ process.exit(1);
1121
+ }
1122
+
1123
+ // ── License key ──
1124
+ let key = opts.key;
1125
+
1126
+ if (!key) {
1127
+ let attempts = 0;
1128
+ while (attempts < 3) {
1129
+ key = await textInput({
1130
+ label: "Enter your license key",
1131
+ mask: "\u25AA",
1132
+ placeholder: "MOVER-XXXX-XXXX",
1133
+ });
1134
+
1135
+ if (validateKey(key)) break;
1136
+
1137
+ attempts++;
1138
+ if (attempts < 3) {
1139
+ barLn(red("Invalid key. Try again."));
1140
+ barLn();
1141
+ key = "";
1142
+ }
1143
+ }
1144
+
1145
+ if (!validateKey(key)) {
1146
+ barLn(red("Invalid license key."));
1147
+ barLn();
1148
+ barLn(dim("Get a key at https://moveros.com"));
1149
+ outro("Cancelled.");
1150
+ process.exit(1);
1151
+ }
1152
+ } else {
1153
+ if (!validateKey(key)) {
1154
+ barLn(red("Invalid license key."));
1155
+ barLn();
1156
+ barLn(dim("Get a key at https://moveros.com"));
1157
+ outro("Cancelled.");
1158
+ process.exit(1);
1159
+ }
1160
+ barLn(green("License verified."));
1161
+ barLn();
1162
+ }
1163
+
1164
+ // ── Vault path ──
1165
+ let vaultPath = opts.vault;
1166
+
1167
+ if (!vaultPath) {
1168
+ const candidates = [
1169
+ path.join(os.homedir(), "Documents", "Obsidian"),
1170
+ path.join(os.homedir(), "Obsidian"),
1171
+ path.join(os.homedir(), "Documents", "Mover-OS"),
1172
+ ];
1173
+
1174
+ const detected = candidates.find((p) => {
1175
+ try {
1176
+ return fs.existsSync(p) && (fs.existsSync(path.join(p, ".obsidian")) || fs.existsSync(path.join(p, "02_Areas")));
1177
+ } catch {
1178
+ return false;
1179
+ }
1180
+ });
1181
+
1182
+ const defaultPath = detected || path.join(os.homedir(), "Mover-OS");
1183
+
1184
+ vaultPath = await textInput({
1185
+ label: `Where is your Obsidian vault?${detected ? dim(" (detected)") : ""}`,
1186
+ initial: defaultPath,
1187
+ });
1188
+ } else {
1189
+ barLn(dim(`Vault: ${vaultPath}`));
1190
+ barLn();
1191
+ }
1192
+
1193
+ if (vaultPath.startsWith("~")) vaultPath = path.join(os.homedir(), vaultPath.slice(1));
1194
+ vaultPath = path.resolve(vaultPath);
1195
+
1196
+ // ── Detect existing install → show mode menu ──
1197
+ const engine = detectEngineFiles(vaultPath);
1198
+ const versionFile = path.join(vaultPath, ".mover-version");
1199
+ const hasExistingInstall = fs.existsSync(versionFile) || engine.exists;
1200
+
1201
+ let installMode = "fresh"; // fresh | update | uninstall
1202
+
1203
+ if (hasExistingInstall) {
1204
+ if (engine.exists) {
1205
+ barLn(yellow("Existing Mover OS vault detected."));
1206
+ barLn(dim(` Engine files: ${engine.files.join(", ")}`));
1207
+ } else {
1208
+ barLn(yellow("Mover OS installed, but no Engine data yet."));
1209
+ }
1210
+ barLn();
1211
+
1212
+ question("What would you like to do?");
1213
+ barLn();
1214
+
1215
+ installMode = await interactiveSelect(
1216
+ [
1217
+ { id: "update", name: "Update", tier: "recommended" },
1218
+ { id: "fresh", name: "Fresh Install", tier: "overwrites templates" },
1219
+ { id: "uninstall", name: "Uninstall", tier: "removes agent configs" },
1220
+ ],
1221
+ { multi: false, defaultIndex: 0 }
1222
+ );
1223
+ }
1224
+
1225
+ // ── Uninstall flow ──
1226
+ if (installMode === "uninstall") {
1227
+ await runUninstall(vaultPath);
1228
+ return;
1229
+ }
1230
+
1231
+ const updateMode = installMode === "update";
1232
+
1233
+ if (!fs.existsSync(vaultPath)) fs.mkdirSync(vaultPath, { recursive: true });
1234
+
1235
+ // ── Agent selection ──
1236
+ const detectedIds = AGENTS.filter((a) => a.detect()).map((a) => a.id);
1237
+ const agentItems = AGENTS.map((a) => ({
1238
+ ...a,
1239
+ _detected: detectedIds.includes(a.id),
1240
+ }));
1241
+
1242
+ question(`Select your AI agents${detectedIds.length > 0 ? dim(" (detected agents pre-selected)") : ""}`);
1243
+ barLn();
1244
+
1245
+ const selectedIds = await interactiveSelect(agentItems, {
1246
+ multi: true,
1247
+ preSelected: detectedIds,
1248
+ });
1249
+ const selectedAgents = AGENTS.filter((a) => selectedIds.includes(a.id));
1250
+
1251
+ if (selectedAgents.length === 0) {
1252
+ barLn(yellow("No agents selected."));
1253
+ outro("Cancelled.");
1254
+ process.exit(0);
1255
+ }
1256
+
1257
+ // ── Skills ──
1258
+ const allSkills = findSkills(bundleDir);
1259
+ let installSkills = false;
1260
+
1261
+ if (allSkills.length > 0 && selectedAgents.some((a) => ["Full", "Enhanced"].includes(a.tier))) {
1262
+ question(`Install ${bold(String(allSkills.length))} skill packs? ${dim("code review, marketing, security...")}`);
1263
+ barLn();
1264
+
1265
+ const skillChoice = await interactiveSelect(
1266
+ [
1267
+ { id: "yes", name: "Yes" },
1268
+ { id: "no", name: "No" },
1269
+ ],
1270
+ { multi: false, defaultIndex: 0 }
1271
+ );
1272
+ installSkills = skillChoice === "yes";
1273
+ }
1274
+
1275
+ // ── Install with animated spinners ──
1276
+ barLn();
1277
+ question(updateMode ? bold("Updating...") : bold("Installing..."));
1278
+ barLn();
1279
+
1280
+ let totalSteps = 0;
1281
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1282
+
1283
+ // 1. Vault structure
1284
+ let sp = spinner("Vault structure");
1285
+ const dirsCreated = createVaultStructure(vaultPath);
1286
+ await sleep(200);
1287
+ sp.stop(`Vault structure${dirsCreated > 0 ? dim(` ${dirsCreated} folders`) : dim(" verified")}`);
1288
+ totalSteps++;
1289
+
1290
+ // 2. Template files (skip on update)
1291
+ if (!updateMode) {
1292
+ sp = spinner("Engine templates");
1293
+ const templatesInstalled = installTemplateFiles(bundleDir, vaultPath);
1294
+ await sleep(200);
1295
+ sp.stop(`Engine templates${templatesInstalled > 0 ? dim(` ${templatesInstalled} files`) : dim(" up to date")}`);
1296
+ totalSteps++;
1297
+ }
1298
+
1299
+ // 3. CLAUDE.md (skip on update)
1300
+ if (!updateMode) {
1301
+ const vaultClaudeMd = path.join(vaultPath, "CLAUDE.md");
1302
+ const bundleClaudeMd = path.join(bundleDir, "CLAUDE.md");
1303
+ if (!fs.existsSync(vaultClaudeMd) && fs.existsSync(bundleClaudeMd)) {
1304
+ fs.copyFileSync(bundleClaudeMd, vaultClaudeMd);
1305
+ sp = spinner("CLAUDE.md");
1306
+ await sleep(150);
1307
+ sp.stop("CLAUDE.md");
1308
+ totalSteps++;
1309
+ }
1310
+ }
1311
+
1312
+ // 4. Per-agent installation
1313
+ for (const agent of selectedAgents) {
1314
+ const fn = AGENT_INSTALLERS[agent.id];
1315
+ if (!fn) continue;
1316
+ sp = spinner(agent.name);
1317
+ const steps = agent.id === "antigravity" ? fn(bundleDir) : fn(bundleDir, vaultPath);
1318
+ await sleep(250);
1319
+ if (steps.length > 0) {
1320
+ sp.stop(`${agent.name} ${dim(steps.join(", "))}`);
1321
+ totalSteps++;
1322
+ } else {
1323
+ sp.stop(`${agent.name} ${dim("configured")}`);
1324
+ totalSteps++;
1325
+ }
1326
+ }
1327
+
1328
+ // 5. AGENTS.md — only for agents that need it
1329
+ const needsAgentsMd = selectedAgents.some((a) => ["codex", "amp", "aider", "openclaw"].includes(a.id));
1330
+ if (needsAgentsMd) {
1331
+ fs.writeFileSync(path.join(vaultPath, "AGENTS.md"), generateAgentsMd(), "utf8");
1332
+ sp = spinner("AGENTS.md");
1333
+ await sleep(150);
1334
+ sp.stop("AGENTS.md");
1335
+ totalSteps++;
1336
+ }
1337
+
1338
+ // 6. .gitignore + git init (fresh install only)
1339
+ if (!updateMode) {
1340
+ const hasGit = cmdExists("git");
1341
+ if (hasGit) {
1342
+ const gitignorePath = path.join(vaultPath, ".gitignore");
1343
+ if (!fs.existsSync(gitignorePath)) {
1344
+ fs.writeFileSync(gitignorePath, generateGitignore(), "utf8");
1345
+ sp = spinner(".gitignore");
1346
+ await sleep(100);
1347
+ sp.stop(".gitignore");
1348
+ totalSteps++;
1349
+ }
1350
+ if (!fs.existsSync(path.join(vaultPath, ".git"))) {
1351
+ sp = spinner("Git repository");
1352
+ try {
1353
+ execSync("git init", { cwd: vaultPath, stdio: "ignore" });
1354
+ execSync("git add -A", { cwd: vaultPath, stdio: "ignore" });
1355
+ execSync('git commit -m "Initial commit — Mover OS v' + VERSION + '"', { cwd: vaultPath, stdio: "ignore" });
1356
+ await sleep(300);
1357
+ sp.stop("Git initialized");
1358
+ totalSteps++;
1359
+ } catch {
1360
+ sp.stop(dim("Git skipped"));
1361
+ }
1362
+ }
1363
+ }
1364
+ }
1365
+
1366
+ // 7. Version stamp
1367
+ fs.writeFileSync(path.join(vaultPath, ".mover-version"), `V${VERSION}\n`, "utf8");
1368
+
1369
+ barLn();
1370
+
1371
+ // ── Done ──
1372
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
1373
+ const verb = updateMode ? "updated" : "installed";
1374
+ outro(`${green("Done.")} Mover OS v${VERSION} ${verb}. ${dim(`${totalSteps} steps in ${elapsed}s`)}`);
1375
+
1376
+ // How to open the vault in each agent
1377
+ const agentOpen = {
1378
+ "claude-code": { cmd: `cd "${vaultPath}" && claude`, name: "Claude Code" },
1379
+ cursor: { cmd: `Open Cursor → File → Open Folder → select vault`, name: "Cursor" },
1380
+ cline: { cmd: `Open VS Code → File → Open Folder → select vault`, name: "Cline (VS Code)" },
1381
+ codex: { cmd: `cd "${vaultPath}" && codex`, name: "Codex" },
1382
+ windsurf: { cmd: `Open Windsurf → File → Open Folder → select vault`, name: "Windsurf" },
1383
+ openclaw: { cmd: `cd "${vaultPath}" && openclaw`, name: "OpenClaw" },
1384
+ antigravity: { cmd: `cd "${vaultPath}" && gemini`, name: "Antigravity" },
1385
+ "roo-code": { cmd: `Open VS Code → File → Open Folder → select vault`, name: "Roo Code" },
1386
+ copilot: { cmd: `Open VS Code → File → Open Folder → select vault`, name: "Copilot" },
1387
+ amp: { cmd: `cd "${vaultPath}" && amp`, name: "Amp" },
1388
+ aider: { cmd: `cd "${vaultPath}" && aider`, name: "Aider" },
1389
+ };
1390
+
1391
+ const primaryAgent = selectedAgents[0];
1392
+ const command = updateMode ? "/update" : "/setup";
1393
+ const agent = agentOpen[primaryAgent.id] || { cmd: "Open your AI agent in the vault", name: "your agent" };
1394
+
1395
+ // Box-drawn summary panel
1396
+ const boxW = 58;
1397
+ const pad = (s, w) => {
1398
+ const visible = strip(s).length;
1399
+ return visible < w ? s + " ".repeat(w - visible) : s;
1400
+ };
1401
+
1402
+ ln(gray(` ┌${"─".repeat(boxW)}┐`));
1403
+ ln(gray(` │`) + pad(` ${bold("Next steps")}`, boxW) + gray(`│`));
1404
+ ln(gray(` │${"─".repeat(boxW)}│`));
1405
+ ln(gray(` │`) + pad(` ${cyan("1")} Open your vault in ${bold("Obsidian")} ${dim("(for viewing files)")}`, boxW) + gray(`│`));
1406
+ ln(gray(` │`) + pad(` ${dim(vaultPath.length > 48 ? "..." + vaultPath.slice(-45) : vaultPath)}`, boxW) + gray(`│`));
1407
+ ln(gray(` │`) + pad(` ${cyan("2")} Open the vault in ${bold(agent.name)} ${dim("(your AI agent)")}`, boxW) + gray(`│`));
1408
+ ln(gray(` │`) + pad(` ${dim(agent.cmd.length > 48 ? agent.cmd.slice(0, 48) + "..." : agent.cmd)}`, boxW) + gray(`│`));
1409
+ ln(gray(` │`) + pad(` ${cyan("3")} Run ${bold(command)} in ${agent.name}`, boxW) + gray(`│`));
1410
+ ln(gray(` │`) + pad(` ${dim(updateMode ? "Syncs your Engine with the latest version" : "Builds your Identity, Strategy, and Goals")}`, boxW) + gray(`│`));
1411
+ ln(gray(` │${" ".repeat(boxW)}│`));
1412
+ ln(gray(` │`) + pad(` ${dim("Obsidian = view your files")}`, boxW) + gray(`│`));
1413
+ ln(gray(` │`) + pad(` ${dim(agent.name + " = where you work with the AI")}`, boxW) + gray(`│`));
1414
+ ln(gray(` │${" ".repeat(boxW)}│`));
1415
+ ln(gray(` │`) + pad(` ${dim("/morning → [work] → /log → /analyse-day → /plan-tomorrow")}`, boxW) + gray(`│`));
1416
+ ln(gray(` └${"─".repeat(boxW)}┘`));
1417
+ ln();
1418
+ }
1419
+
1420
+ main().catch((err) => {
1421
+ cleanup();
1422
+ console.error(red(`\n Error: ${err.message}`));
1423
+ process.exit(1);
1424
+ });