youmd 0.8.2 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agent.d.ts +13 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +233 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +17 -0
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/env.d.ts +20 -4
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +613 -21
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/stack.d.ts.map +1 -1
- package/dist/commands/stack.js +8 -2
- package/dist/commands/stack.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +8 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +200 -0
- package/dist/commands/sync.js.map +1 -1
- package/dist/index.js +71 -4
- package/dist/index.js.map +1 -1
- package/dist/lib/api.d.ts +173 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +87 -0
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/daemon.d.ts +3 -0
- package/dist/lib/daemon.d.ts.map +1 -1
- package/dist/lib/daemon.js +21 -0
- package/dist/lib/daemon.js.map +1 -1
- package/dist/lib/machine-bootstrap-prompt.d.ts.map +1 -1
- package/dist/lib/machine-bootstrap-prompt.js +372 -184
- package/dist/lib/machine-bootstrap-prompt.js.map +1 -1
- package/dist/lib/realtime-sync.d.ts +174 -0
- package/dist/lib/realtime-sync.d.ts.map +1 -0
- package/dist/lib/realtime-sync.js +300 -0
- package/dist/lib/realtime-sync.js.map +1 -0
- package/package.json +2 -1
- package/scripts/env-vault/README.md +9 -0
- package/scripts/env-vault/restore.sh +85 -12
- package/scripts/skillstack-sync/README.md +24 -6
- package/scripts/skillstack-sync/bootstrap-new-mac.sh +31 -0
- package/scripts/skillstack-sync/com.youmd.realtime-sync.plist +35 -0
- package/scripts/skillstack-sync/install-daemons.sh +4 -1
- package/skills/machine-bootstrap.md +114 -19
|
@@ -45,10 +45,28 @@ exports.DEFAULT_FRESH_MACHINE_LIMIT = 80;
|
|
|
45
45
|
function shellQuote(value) {
|
|
46
46
|
return `'${value.replace(/'/g, `'\"'\"'`)}'`;
|
|
47
47
|
}
|
|
48
|
-
function envAssignment(name, value) {
|
|
48
|
+
function envAssignment(name, value, options = {}) {
|
|
49
49
|
if (value === undefined || value === null || `${value}`.trim() === "")
|
|
50
50
|
return null;
|
|
51
|
-
|
|
51
|
+
const stringValue = String(value);
|
|
52
|
+
if (options.expandHome) {
|
|
53
|
+
if (stringValue === "~")
|
|
54
|
+
return `${name}="$HOME"`;
|
|
55
|
+
if (stringValue.startsWith("~/")) {
|
|
56
|
+
return `${name}="$HOME${stringValue.slice(1).replace(/["\\$`]/g, (char) => `\\${char}`)}"`;
|
|
57
|
+
}
|
|
58
|
+
if (stringValue === "$HOME")
|
|
59
|
+
return `${name}="$HOME"`;
|
|
60
|
+
if (stringValue.startsWith("$HOME/")) {
|
|
61
|
+
return `${name}="$HOME/${stringValue.slice("$HOME/".length).replace(/["\\$`]/g, (char) => `\\${char}`)}"`;
|
|
62
|
+
}
|
|
63
|
+
if (stringValue === "${HOME}")
|
|
64
|
+
return `${name}="$HOME"`;
|
|
65
|
+
if (stringValue.startsWith("${HOME}/")) {
|
|
66
|
+
return `${name}="$HOME/${stringValue.slice("${HOME}/".length).replace(/["\\$`]/g, (char) => `\\${char}`)}"`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return `${name}=${shellQuote(stringValue)}`;
|
|
52
70
|
}
|
|
53
71
|
function portableHomePath(value) {
|
|
54
72
|
if (!value)
|
|
@@ -64,193 +82,356 @@ function portableHomePath(value) {
|
|
|
64
82
|
return value;
|
|
65
83
|
}
|
|
66
84
|
function buildFreshMachineBootstrapScript() {
|
|
67
|
-
return
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
85
|
+
return `set -euo pipefail
|
|
86
|
+
export PATH="$HOME/.youmd/bin:$HOME/.youmd/npm-global/bin:/opt/homebrew/opt/node@22/bin:/usr/local/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
87
|
+
ROOT="\${YOUMD_CODE_ROOT:-$HOME/Desktop/CODE_YOU}"
|
|
88
|
+
DAYS="\${YOUMD_ACTIVE_DAYS:-30}"
|
|
89
|
+
EXPAND_DAYS="\${YOUMD_EXPAND_ACTIVE_DAYS:-90}"
|
|
90
|
+
LIMIT="\${YOUMD_PROJECT_LIMIT:-80}"
|
|
91
|
+
HYDRATE_TIMEOUT="\${YOUMD_PORTFOLIO_HYDRATE_TIMEOUT_SECONDS:-180}"
|
|
92
|
+
MIN_YOUMD_VERSION="\${YOUMD_MIN_VERSION:-0.8.6}"
|
|
93
|
+
mkdir -p "$ROOT"
|
|
94
|
+
|
|
95
|
+
command_exists() { command -v "$1" >/dev/null 2>&1; }
|
|
96
|
+
node_major() { node -e 'console.log(process.versions.node.split(".")[0])' 2>/dev/null || echo 0; }
|
|
97
|
+
version_at_least() {
|
|
98
|
+
local current="\${1#v}"
|
|
99
|
+
local required="\${2#v}"
|
|
100
|
+
local IFS=.
|
|
101
|
+
local current_parts=($current)
|
|
102
|
+
local required_parts=($required)
|
|
103
|
+
local i current_value required_value
|
|
104
|
+
for i in 0 1 2; do
|
|
105
|
+
current_value="\${current_parts[$i]:-0}"
|
|
106
|
+
required_value="\${required_parts[$i]:-0}"
|
|
107
|
+
current_value="\${current_value%%[^0-9]*}"
|
|
108
|
+
required_value="\${required_value%%[^0-9]*}"
|
|
109
|
+
current_value="\${current_value:-0}"
|
|
110
|
+
required_value="\${required_value:-0}"
|
|
111
|
+
if ((10#$current_value > 10#$required_value)); then return 0; fi
|
|
112
|
+
if ((10#$current_value < 10#$required_value)); then return 1; fi
|
|
113
|
+
done
|
|
114
|
+
return 0
|
|
115
|
+
}
|
|
116
|
+
ensure_youmd_min_version() {
|
|
117
|
+
local installed
|
|
118
|
+
installed="$(youmd --version 2>/dev/null | tr -d '[:space:]' || true)"
|
|
119
|
+
echo "[you.md] installed version: \${installed:-unknown}"
|
|
120
|
+
if [ -n "$installed" ] && version_at_least "$installed" "$MIN_YOUMD_VERSION"; then
|
|
121
|
+
return 0
|
|
122
|
+
fi
|
|
123
|
+
echo "[you.md] installed youmd is older than required \${MIN_YOUMD_VERSION}; forcing GitHub source install"
|
|
124
|
+
curl -fsSL https://you.md/install.sh | YOUMD_INSTALL_CHANNEL=source YOUMD_SOURCE_REF=main bash
|
|
125
|
+
export PATH="$HOME/.youmd/bin:$HOME/.youmd/npm-global/bin:/opt/homebrew/opt/node@22/bin:/usr/local/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
126
|
+
installed="$(youmd --version 2>/dev/null | tr -d '[:space:]' || true)"
|
|
127
|
+
echo "[you.md] installed version after forced source install: \${installed:-unknown}"
|
|
128
|
+
if [ -z "$installed" ] || ! version_at_least "$installed" "$MIN_YOUMD_VERSION"; then
|
|
129
|
+
echo "[you.md] youmd \${MIN_YOUMD_VERSION}+ is required for Secret Vault, agent bus, and fresh-machine restore. npm latest may still be behind; rerun after this commit is deployed or install from GitHub main." >&2
|
|
130
|
+
exit 1
|
|
131
|
+
fi
|
|
132
|
+
}
|
|
133
|
+
run_with_timeout() {
|
|
134
|
+
seconds="$1"
|
|
135
|
+
shift
|
|
136
|
+
if command_exists gtimeout; then
|
|
137
|
+
gtimeout "$seconds" "$@"
|
|
138
|
+
elif command_exists timeout; then
|
|
139
|
+
timeout "$seconds" "$@"
|
|
140
|
+
else
|
|
141
|
+
"$@" &
|
|
142
|
+
pid=$!
|
|
143
|
+
( sleep "$seconds"; if kill -0 "$pid" >/dev/null 2>&1; then echo "[you.md] timeout after \${seconds}s: $*" >&2; kill "$pid" >/dev/null 2>&1 || true; fi ) &
|
|
144
|
+
watcher=$!
|
|
145
|
+
wait "$pid"
|
|
146
|
+
status=$?
|
|
147
|
+
kill "$watcher" >/dev/null 2>&1 || true
|
|
148
|
+
wait "$watcher" 2>/dev/null || true
|
|
149
|
+
return "$status"
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
bootstrap_prereqs() {
|
|
154
|
+
echo "[you.md] checking local prerequisites: Homebrew, Node 22/npm, git, gh, bun, pnpm"
|
|
155
|
+
if [ "$(uname -s 2>/dev/null || echo unknown)" = "Darwin" ] && ! command_exists brew; then
|
|
156
|
+
echo "[you.md] Homebrew missing; installing it first so the rest of setup can stay boring"
|
|
157
|
+
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || true
|
|
158
|
+
fi
|
|
159
|
+
if [ -x /opt/homebrew/bin/brew ]; then eval "$(/opt/homebrew/bin/brew shellenv)" || true; fi
|
|
160
|
+
if [ -x /usr/local/bin/brew ]; then eval "$(/usr/local/bin/brew shellenv)" || true; fi
|
|
161
|
+
export PATH="$HOME/.youmd/bin:$HOME/.youmd/npm-global/bin:/opt/homebrew/opt/node@22/bin:/usr/local/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
162
|
+
|
|
163
|
+
if command_exists brew; then
|
|
164
|
+
MAJOR="$(node_major)"
|
|
165
|
+
if ! command_exists node || [ "$MAJOR" -lt 20 ] || [ "$MAJOR" -ge 23 ]; then
|
|
166
|
+
echo "[you.md] installing Node 22 for modern Next/Convex projects"
|
|
167
|
+
brew install node@22 || brew install node || true
|
|
168
|
+
brew link --overwrite --force node@22 || true
|
|
169
|
+
fi
|
|
170
|
+
command_exists git || brew install git || true
|
|
171
|
+
command_exists gh || brew install gh || true
|
|
172
|
+
command_exists bun || brew install bun || brew install oven-sh/bun/bun || true
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
export PATH="$HOME/.youmd/bin:$HOME/.youmd/npm-global/bin:/opt/homebrew/opt/node@22/bin:/usr/local/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
176
|
+
command_exists corepack && corepack enable || true
|
|
177
|
+
if command_exists corepack && ! command_exists pnpm; then
|
|
178
|
+
corepack prepare pnpm@latest --activate || true
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
for required in node npm git; do
|
|
182
|
+
if ! command_exists "$required"; then
|
|
183
|
+
echo "[you.md] required tool missing after bootstrap: $required" >&2
|
|
184
|
+
exit 1
|
|
185
|
+
fi
|
|
186
|
+
done
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
ensure_github_auth() {
|
|
190
|
+
if ! command_exists gh; then
|
|
191
|
+
echo "[you.md] GitHub CLI is missing, so private repo clone/setup cannot continue yet." >&2
|
|
192
|
+
return 1
|
|
193
|
+
fi
|
|
194
|
+
if gh auth status >/dev/null 2>&1; then
|
|
195
|
+
echo "[you.md] GitHub auth is ready"
|
|
196
|
+
return 0
|
|
197
|
+
fi
|
|
198
|
+
echo "[you.md] GitHub auth is required before private skill/project repos can clone."
|
|
199
|
+
if [ -t 0 ]; then
|
|
200
|
+
gh auth login -h github.com -p https -s repo || true
|
|
201
|
+
fi
|
|
202
|
+
if gh auth status >/dev/null 2>&1; then
|
|
203
|
+
echo "[you.md] GitHub auth is ready"
|
|
204
|
+
return 0
|
|
205
|
+
fi
|
|
206
|
+
echo "[you.md] GitHub auth still is not complete."
|
|
207
|
+
echo "[you.md] Open a normal Terminal on this Mac and run:"
|
|
208
|
+
echo " $(command -v gh || echo gh) auth login -h github.com -p https -s repo"
|
|
209
|
+
echo "[you.md] Then rerun this same setup prompt/command."
|
|
210
|
+
return 1
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
bootstrap_prereqs
|
|
214
|
+
|
|
215
|
+
echo "[you.md] installing runtime from GitHub main"
|
|
216
|
+
curl -fsSL https://you.md/install.sh | YOUMD_INSTALL_CHANNEL=source YOUMD_SOURCE_REF=main bash
|
|
217
|
+
export PATH="$HOME/.youmd/bin:$HOME/.youmd/npm-global/bin:/opt/homebrew/opt/node@22/bin:/usr/local/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
|
|
218
|
+
if ! command_exists youmd; then
|
|
219
|
+
echo "[you.md] youmd was installed but is not on PATH. Try a new Terminal, then rerun this command." >&2
|
|
220
|
+
exit 1
|
|
221
|
+
fi
|
|
222
|
+
ensure_youmd_min_version
|
|
223
|
+
|
|
224
|
+
if [ -n "\${YOUMD_API_KEY:-}" ]; then
|
|
225
|
+
echo "[you.md] logging in with bootstrap key"
|
|
226
|
+
youmd login --key "$YOUMD_API_KEY"
|
|
227
|
+
else
|
|
228
|
+
echo "[you.md] no YOUMD_API_KEY set; starting interactive login"
|
|
229
|
+
youmd login
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
echo "[you.md] pulling identity bundle and syncing local brain"
|
|
233
|
+
youmd pull
|
|
234
|
+
youmd sync
|
|
235
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) authenticated, pulled identity, and started setup" || true
|
|
236
|
+
|
|
237
|
+
echo "[you.md] installing resident realtime/identity/skillstack/project-context daemons early"
|
|
238
|
+
youmd stack daemon install || true
|
|
239
|
+
youmd stack daemon status || true
|
|
240
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) installed resident realtime/skillstack daemons" || true
|
|
241
|
+
|
|
242
|
+
GITHUB_READY=0
|
|
243
|
+
if ensure_github_auth; then
|
|
244
|
+
GITHUB_READY=1
|
|
245
|
+
elif [ "\${YOUMD_REQUIRE_GITHUB_AUTH:-1}" = "1" ]; then
|
|
246
|
+
echo "[you.md] stopping after You.md identity/daemon setup because GitHub auth is required for private repos." >&2
|
|
247
|
+
exit 2
|
|
248
|
+
fi
|
|
249
|
+
|
|
250
|
+
echo "[you.md] restoring shared skills, stacks, and agent host config"
|
|
251
|
+
youmd machine setup || echo "[you.md] machine setup reported a warning; continuing to skills/MCP so the next run can self-heal"
|
|
252
|
+
youmd skill install all || true
|
|
253
|
+
youmd skill sync || true
|
|
254
|
+
youmd mcp --install claude --auto || true
|
|
255
|
+
youmd mcp --install codex --auto || true
|
|
256
|
+
youmd skill link claude || true
|
|
257
|
+
youmd skill link codex || true
|
|
258
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) restored shared skills/stacks and MCP config" || true
|
|
259
|
+
|
|
260
|
+
if [ "$GITHUB_READY" != "1" ]; then
|
|
261
|
+
echo "[you.md] GitHub auth is still missing; project clone pass skipped. Rerun after gh auth login."
|
|
262
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) stopped: GitHub auth required before private project clone" || true
|
|
263
|
+
exit 2
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
RECENT_ONLY_ARGS=()
|
|
267
|
+
if youmd machine --help 2>/dev/null | grep -q -- "--recent-only"; then
|
|
268
|
+
RECENT_ONLY_ARGS=(--recent-only)
|
|
269
|
+
else
|
|
270
|
+
echo "[you.md] installed CLI does not expose --recent-only yet; using noninteractive no-to-older-projects fallback"
|
|
271
|
+
fi
|
|
272
|
+
run_machine_projects_recent_only() {
|
|
273
|
+
if [ "\${#RECENT_ONLY_ARGS[@]}" -gt 0 ]; then
|
|
274
|
+
youmd machine projects "$@"
|
|
275
|
+
else
|
|
276
|
+
youmd machine projects "$@" </dev/null
|
|
277
|
+
fi
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
echo "[you.md] hydrating portfolio graph from You.md/GitHub before the 30-day local clone pass"
|
|
281
|
+
run_with_timeout "$HYDRATE_TIMEOUT" youmd project portfolio-hydrate --root "$ROOT" --days "$DAYS" --limit "$LIMIT" || true
|
|
282
|
+
PROJECT_ARGS=(--root "$ROOT" --days "$DAYS" "\${RECENT_ONLY_ARGS[@]}")
|
|
283
|
+
if [ -n "\${YOUMD_MAX_CLONE_PROJECTS:-}" ]; then
|
|
284
|
+
PROJECT_ARGS+=(--max-clone-projects "$YOUMD_MAX_CLONE_PROJECTS")
|
|
285
|
+
fi
|
|
286
|
+
echo "[you.md] previewing graph-backed 30-day project setup plan (ACTIVE + Top Priority/Focusing only)"
|
|
287
|
+
run_machine_projects_recent_only "\${PROJECT_ARGS[@]}" --dry-run || true
|
|
288
|
+
echo "[you.md] creating code workspace and cloning ACTIVE + Top Priority/Focusing 30-day project repos"
|
|
289
|
+
run_machine_projects_recent_only "\${PROJECT_ARGS[@]}"
|
|
290
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) finished 30-day active/focused project clone pass into $ROOT" || true
|
|
291
|
+
|
|
292
|
+
echo "[you.md] checking encrypted env-vault tooling without printing secrets"
|
|
293
|
+
youmd env backup --root "$ROOT" --preflight || true
|
|
294
|
+
SECRET_VAULT_RESTORED=0
|
|
295
|
+
if [ -z "\${YOUMD_ENV_VAULT:-}" ] && [ "\${YOUMD_SECRET_VAULT_PULL:-1}" = "1" ]; then
|
|
296
|
+
SECRET_VAULT_DIR="\${YOUMD_SECRET_VAULT_DIR:-$HOME/.youmd/secret-vault}"
|
|
297
|
+
echo "[you.md] registering this Mac as a trusted Secret Vault device"
|
|
298
|
+
youmd env vault device-register || true
|
|
299
|
+
echo "[you.md] checking You.md Secret Vault for the latest encrypted env vault and trusted-device envelope"
|
|
300
|
+
if youmd env vault pull --out "$SECRET_VAULT_DIR" --restore --root "$ROOT" --map-existing --existing-only --skip-agent-auth; then
|
|
301
|
+
SECRET_VAULT_RESTORED=1
|
|
302
|
+
echo "[you.md] restored env vault through trusted-device Secret Vault"
|
|
303
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) restored encrypted env vault through trusted-device Secret Vault" || true
|
|
304
|
+
else
|
|
305
|
+
echo "[you.md] account-backed Secret Vault restore not available yet; falling back to local/iCloud vault discovery"
|
|
306
|
+
echo "[you.md] if this Mac was just registered, run this on the source Mac: youmd env vault share"
|
|
307
|
+
fi
|
|
308
|
+
fi
|
|
309
|
+
if [ "$SECRET_VAULT_RESTORED" != "1" ] && [ -z "\${YOUMD_ENV_VAULT:-}" ]; then
|
|
310
|
+
for DEFAULT_ENV_VAULT_DIR in "$HOME/Desktop/youmd-env-vault" "$HOME/Library/Mobile Documents/com~apple~CloudDocs/Desktop/youmd-env-vault" "$HOME/Library/Mobile Documents/com~apple~CloudDocs/youmd-env-vault"; do
|
|
311
|
+
if [ -d "$DEFAULT_ENV_VAULT_DIR" ]; then
|
|
312
|
+
DETECTED_ENV_VAULT="$(find "$DEFAULT_ENV_VAULT_DIR" -maxdepth 1 -type f \\( -name "env-vault-*.tar.enc" -o -name "env-vault-*.tar.age" -o -name "env-vault-*.tar.gpg" \\) -print 2>/dev/null | sort | tail -n 1 || true)"
|
|
313
|
+
if [ -n "$DETECTED_ENV_VAULT" ]; then
|
|
314
|
+
export YOUMD_ENV_VAULT="$DETECTED_ENV_VAULT"
|
|
315
|
+
echo "[you.md] using detected env vault: $YOUMD_ENV_VAULT"
|
|
316
|
+
break
|
|
317
|
+
fi
|
|
318
|
+
fi
|
|
319
|
+
done
|
|
320
|
+
fi
|
|
321
|
+
if [ "$SECRET_VAULT_RESTORED" = "1" ]; then
|
|
322
|
+
:
|
|
323
|
+
elif [ -n "\${YOUMD_ENV_VAULT:-}" ]; then
|
|
324
|
+
if [ ! -f "$YOUMD_ENV_VAULT" ]; then
|
|
325
|
+
echo "[you.md] env vault path does not exist: $YOUMD_ENV_VAULT" >&2
|
|
326
|
+
exit 1
|
|
327
|
+
fi
|
|
328
|
+
if [ -z "\${ENV_VAULT_PASS:-}" ] && command_exists security; then
|
|
329
|
+
KEYCHAIN_SERVICE="\${YOUMD_ENV_VAULT_KEYCHAIN_SERVICE:-youmd-env-vault}"
|
|
330
|
+
if ENV_VAULT_PASS_FROM_KEYCHAIN="$(security find-generic-password -a "\${USER:-\${LOGNAME:-houston}}" -s "$KEYCHAIN_SERVICE" -w 2>/dev/null)"; then
|
|
331
|
+
export ENV_VAULT_PASS="$ENV_VAULT_PASS_FROM_KEYCHAIN"
|
|
332
|
+
unset ENV_VAULT_PASS_FROM_KEYCHAIN
|
|
333
|
+
echo "[you.md] loaded env-vault passphrase from macOS Keychain service: $KEYCHAIN_SERVICE"
|
|
334
|
+
else
|
|
335
|
+
echo "[you.md] no env-vault Keychain item found; restore will prompt for the passphrase"
|
|
336
|
+
fi
|
|
337
|
+
fi
|
|
338
|
+
echo "[you.md] listing encrypted .env.local vault before restore"
|
|
339
|
+
youmd env restore "$YOUMD_ENV_VAULT" --root "$ROOT" --list --map-existing --existing-only --skip-agent-auth
|
|
340
|
+
echo "[you.md] restoring encrypted .env.local vault into existing cloned project dirs"
|
|
341
|
+
youmd env restore "$YOUMD_ENV_VAULT" --root "$ROOT" --map-existing --existing-only --skip-agent-auth
|
|
342
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) restored encrypted env vault into existing project dirs" || true
|
|
343
|
+
else
|
|
344
|
+
echo "[you.md] env vault not restored yet"
|
|
345
|
+
echo "[you.md] On the old/source Mac, create the encrypted vault first:"
|
|
346
|
+
echo "youmd env vault push --root ~/Desktop/CODE_2025 --out ~/Desktop/youmd-env-vault"
|
|
347
|
+
echo "[you.md] Then share local decrypt access to registered trusted Macs:"
|
|
348
|
+
echo "youmd env vault share"
|
|
349
|
+
echo "[you.md] Or create a transferable local copy without account sync:"
|
|
350
|
+
echo "youmd env backup --root ~/Desktop/CODE_2025 --out ~/Desktop/youmd-env-vault"
|
|
351
|
+
echo "[you.md] The setup command also auto-detects vaults in local Desktop and iCloud Desktop: ~/Library/Mobile Documents/com~apple~CloudDocs/Desktop/youmd-env-vault"
|
|
352
|
+
echo "[you.md] Move the generated env-vault-*.tar.enc file to this Mac, then rerun this command with:"
|
|
353
|
+
echo "YOUMD_ENV_VAULT=/path/to/env-vault-YYYYMMDDTHHMMZ.tar.enc YOUMD_REQUIRE_ENV_VAULT=1 <same command>"
|
|
354
|
+
echo "[you.md] Or restore manually after clone:"
|
|
355
|
+
echo "youmd env restore <vault> --root \\"$ROOT\\" --map-existing --existing-only --skip-agent-auth"
|
|
356
|
+
if [ "\${YOUMD_REQUIRE_ENV_VAULT:-}" = "1" ]; then
|
|
357
|
+
echo "[you.md] strict proof requires Secret Vault restore or YOUMD_ENV_VAULT; stopping before readiness is marked complete" >&2
|
|
358
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) stopped: strict env vault restore still required; source Mac may need youmd env vault share" || true
|
|
359
|
+
exit 1
|
|
360
|
+
fi
|
|
361
|
+
fi
|
|
362
|
+
echo "[you.md] rehydrating portfolio graph with local README/project-context/env-key evidence"
|
|
363
|
+
run_with_timeout "$HYDRATE_TIMEOUT" youmd project portfolio-hydrate --root "$ROOT" --days "$DAYS" --limit "$LIMIT" || true
|
|
364
|
+
echo "[you.md] auditing cloned project readiness"
|
|
365
|
+
youmd machine verify --root "$ROOT" --max-projects "$LIMIT" --write-report --sync-report || true
|
|
366
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) synced machine readiness proof for $ROOT" || true
|
|
367
|
+
FULL_PROJECT_SET_COMPLETE=0
|
|
368
|
+
EXPAND_TO_90="\${YOUMD_EXPAND_TO_90_DAYS:-}"
|
|
369
|
+
if [ -z "$EXPAND_TO_90" ] && [ "\${YOUMD_SKIP_90_DAY_EXPANSION_PROMPT:-}" != "1" ] && [ -t 0 ]; then
|
|
370
|
+
printf "\\n[you.md] expand to all active projects from the last \${EXPAND_DAYS} days before calling setup complete? [y/N] "
|
|
371
|
+
read -r EXPAND_TO_90
|
|
372
|
+
fi
|
|
373
|
+
case "$EXPAND_TO_90" in
|
|
374
|
+
y|Y|yes|YES|1|true|TRUE)
|
|
375
|
+
echo "[you.md] expanding workspace to active \${EXPAND_DAYS}-day project set"
|
|
376
|
+
run_with_timeout "$HYDRATE_TIMEOUT" youmd project portfolio-hydrate --root "$ROOT" --days "$EXPAND_DAYS" --limit "$LIMIT" || true
|
|
377
|
+
EXPAND_PROJECT_ARGS=(--root "$ROOT" --days "$EXPAND_DAYS" "\${RECENT_ONLY_ARGS[@]}")
|
|
378
|
+
if [ -n "\${YOUMD_MAX_CLONE_PROJECTS:-}" ]; then
|
|
379
|
+
EXPAND_PROJECT_ARGS+=(--max-clone-projects "$YOUMD_MAX_CLONE_PROJECTS")
|
|
380
|
+
fi
|
|
381
|
+
run_machine_projects_recent_only "\${EXPAND_PROJECT_ARGS[@]}" --dry-run || true
|
|
382
|
+
run_machine_projects_recent_only "\${EXPAND_PROJECT_ARGS[@]}"
|
|
383
|
+
run_with_timeout "$HYDRATE_TIMEOUT" youmd project portfolio-hydrate --root "$ROOT" --days "$EXPAND_DAYS" --limit "$LIMIT" || true
|
|
384
|
+
youmd machine verify --root "$ROOT" --max-projects "$LIMIT" --write-report --sync-report || true
|
|
385
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) expanded to active/focused \${EXPAND_DAYS}-day project set and synced proof" || true
|
|
386
|
+
FULL_PROJECT_SET_COMPLETE=1
|
|
387
|
+
;;
|
|
388
|
+
*)
|
|
389
|
+
echo "[you.md] stopped after the \${DAYS}-day active project setup; \${EXPAND_DAYS}-day expansion remains intentionally open."
|
|
390
|
+
;;
|
|
391
|
+
esac
|
|
392
|
+
if [ "\${YOUMD_RUN_CHECKS:-}" = "1" ]; then
|
|
393
|
+
echo "[you.md] running bounded package checks"
|
|
394
|
+
youmd machine verify --root "$ROOT" --max-projects "$LIMIT" --run-checks --max-check-projects "\${YOUMD_MAX_CHECK_PROJECTS:-8}" --check-timeout-ms "\${YOUMD_CHECK_TIMEOUT_MS:-120000}" --write-report --sync-report || true
|
|
395
|
+
else
|
|
396
|
+
echo "[you.md] bounded package checks skipped; set YOUMD_RUN_CHECKS=1 to run lint/typecheck/test/build caps"
|
|
397
|
+
fi
|
|
398
|
+
if [ "\${YOUMD_INSTALL_DEPS:-}" = "1" ] || [ "\${YOUMD_PROBE_SERVERS:-}" = "1" ]; then
|
|
399
|
+
echo "[you.md] running bounded clean-host install/server proof"
|
|
400
|
+
VERIFY_ARGS=(--root "$ROOT" --max-projects "$LIMIT")
|
|
401
|
+
if [ "\${YOUMD_INSTALL_DEPS:-}" = "1" ]; then
|
|
402
|
+
VERIFY_ARGS+=(--install-deps --max-install-projects "\${YOUMD_MAX_INSTALL_PROJECTS:-4}" --install-timeout-ms "\${YOUMD_INSTALL_TIMEOUT_MS:-180000}")
|
|
403
|
+
fi
|
|
404
|
+
if [ "\${YOUMD_PROBE_SERVERS:-}" = "1" ]; then
|
|
405
|
+
VERIFY_ARGS+=(--probe-servers --max-server-projects "\${YOUMD_MAX_SERVER_PROJECTS:-3}" --server-timeout-ms "\${YOUMD_SERVER_TIMEOUT_MS:-45000}" --server-start-port "\${YOUMD_SERVER_START_PORT:-4310}")
|
|
406
|
+
fi
|
|
407
|
+
youmd machine verify "\${VERIFY_ARGS[@]}" --write-report --sync-report || true
|
|
408
|
+
else
|
|
409
|
+
echo "[you.md] clean-host install/server proof skipped; set YOUMD_INSTALL_DEPS=1 and YOUMD_PROBE_SERVERS=1 to install deps and smoke-probe local dev servers"
|
|
410
|
+
fi
|
|
411
|
+
echo "[you.md] confirming resident realtime/identity/skillstack/project-context daemons"
|
|
412
|
+
youmd stack daemon install || true
|
|
413
|
+
youmd stack daemon status || true
|
|
414
|
+
youmd status
|
|
415
|
+
if [ "$FULL_PROJECT_SET_COMPLETE" = "1" ]; then
|
|
416
|
+
echo "[you.md] fresh-machine full \${EXPAND_DAYS}-day project setup complete: $ROOT"
|
|
417
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) complete: full \${EXPAND_DAYS}-day active/focused setup at $ROOT" || true
|
|
418
|
+
else
|
|
419
|
+
echo "[you.md] fresh-machine \${DAYS}-day setup pass complete; \${EXPAND_DAYS}-day expansion is still open: $ROOT"
|
|
420
|
+
youmd agent send --channel machine-sync --kind status "fresh machine \$(hostname) complete for \${DAYS}-day active/focused pass at $ROOT; \${EXPAND_DAYS}-day expansion still open" || true
|
|
421
|
+
fi`;
|
|
242
422
|
}
|
|
243
423
|
function buildFreshMachineBootstrapCommand(options = {}) {
|
|
244
424
|
const assignments = [
|
|
245
425
|
envAssignment("YOUMD_API_KEY", options.apiKey),
|
|
246
|
-
envAssignment("YOUMD_CODE_ROOT", portableHomePath(options.root)),
|
|
426
|
+
envAssignment("YOUMD_CODE_ROOT", portableHomePath(options.root), { expandHome: true }),
|
|
247
427
|
envAssignment("YOUMD_ACTIVE_DAYS", options.days ?? exports.DEFAULT_FRESH_MACHINE_DAYS),
|
|
248
428
|
envAssignment("YOUMD_PROJECT_LIMIT", options.limit ?? exports.DEFAULT_FRESH_MACHINE_LIMIT),
|
|
249
429
|
envAssignment("YOUMD_MAX_CLONE_PROJECTS", options.maxCloneProjects),
|
|
250
|
-
envAssignment("YOUMD_ENV_VAULT", portableHomePath(options.envVaultPath)),
|
|
430
|
+
envAssignment("YOUMD_ENV_VAULT", portableHomePath(options.envVaultPath), { expandHome: true }),
|
|
251
431
|
envAssignment("YOUMD_REQUIRE_ENV_VAULT", options.requireEnvVault ? "1" : undefined),
|
|
252
432
|
].filter((value) => Boolean(value));
|
|
253
|
-
|
|
433
|
+
const pathPrefix = `PATH="$HOME/.youmd/bin:$HOME/.youmd/npm-global/bin:/opt/homebrew/opt/node@22/bin:/usr/local/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"`;
|
|
434
|
+
return `${[pathPrefix, ...assignments].join(" ")} bash -lc ${shellQuote(buildFreshMachineBootstrapScript())}`;
|
|
254
435
|
}
|
|
255
436
|
function buildFreshMachineBootstrapPrompt(options = {}) {
|
|
256
437
|
const root = portableHomePath(options.root) ?? exports.DEFAULT_FRESH_MACHINE_ROOT;
|
|
@@ -270,6 +451,8 @@ function buildFreshMachineBootstrapPrompt(options = {}) {
|
|
|
270
451
|
"",
|
|
271
452
|
authLine,
|
|
272
453
|
"",
|
|
454
|
+
"Behind-the-scenes operating rule: use the You.md CLI and You Agent yourself before asking Houston to do anything. Run deterministic commands like `youmd status`, `youmd whoami`, `youmd pull`, `youmd sync`, `youmd skill install all`, `youmd skill sync`, `youmd env vault list`, `youmd env vault device-register`, `youmd env vault pull --restore`, `youmd machine prompt`, `youmd machine verify`, `youmd project portfolio-hydrate`, and `youmd agent status` as needed. Use `youmd agent send --channel machine-sync --kind status \"...\"` to report setup milestones back to Houston's other trusted Macs over the realtime agent bus. Use `you` / `youmd chat` only when you need the You Agent to synthesize or route context, then turn its output into concrete shell actions. Interrupt Houston only for true human-gated steps: GitHub browser auth, source-Mac vault sharing/passphrase entry if the trusted-device envelope has not been created yet, npm OTP, OS permissions, or an explicit choice such as the 90-day expansion.",
|
|
455
|
+
"",
|
|
273
456
|
"Exact setup command:",
|
|
274
457
|
"",
|
|
275
458
|
"```bash",
|
|
@@ -277,19 +460,24 @@ function buildFreshMachineBootstrapPrompt(options = {}) {
|
|
|
277
460
|
"```",
|
|
278
461
|
"",
|
|
279
462
|
"What it will do:",
|
|
280
|
-
"- install
|
|
463
|
+
"- check/install the local prerequisites first: Homebrew, Node 22/npm, git, GitHub CLI, bun, and pnpm/corepack",
|
|
464
|
+
"- install You.md from the curl runtime and force the You.md/Homebrew/Node 22 paths into this setup shell",
|
|
281
465
|
"- authenticate and pull/sync your identity bundle",
|
|
466
|
+
"- install resident realtime sync daemons early so identity/skill/project-context sync is not deferred until the end",
|
|
467
|
+
"- require GitHub CLI auth before private shared-skill/project repos clone; if browser auth fails inside Claude/Codex, it prints the exact Terminal command to run and stops cleanly",
|
|
282
468
|
"- install/configure MCP for Claude Code and Codex",
|
|
283
|
-
"- restore shared agent skills, stack config, Claude/Codex links, and
|
|
469
|
+
"- restore shared agent skills, stack config, Claude/Codex links, and agent host config",
|
|
470
|
+
"- use the installed You.md CLI behind the scenes for status, skill sync, Secret Vault pull, portfolio graph hydration, machine verification, and You Agent context routing instead of making Houston manually drive those steps",
|
|
471
|
+
"- publish setup milestones through `youmd agent send` so Houston's source Mac and realtime daemon can see the Mac mini come online without clipboard babysitting",
|
|
284
472
|
"- hydrate the portfolio graph from You.md + authenticated GitHub before cloning",
|
|
285
473
|
`- preview the graph-backed plan, create ${root}, and clone only projects marked ACTIVE plus Top Priority/Focusing from the last ${days} days first`,
|
|
286
474
|
`- ask whether to expand to all ACTIVE plus Top Priority/Focusing projects from the last ${expandDays} days before calling the full project clone set complete`,
|
|
287
|
-
"- check env-vault tooling,
|
|
475
|
+
"- check env-vault tooling, register this Mac as a trusted Secret Vault device, pull the latest account-backed You.md Secret Vault encrypted snapshot when available, unwrap the vault passphrase locally through a trusted-device key envelope, restore env files into existing cloned project dirs with `--map-existing --existing-only --skip-agent-auth`, otherwise auto-detect the newest local Desktop or iCloud Desktop `youmd-env-vault/env-vault-*` file if `YOUMD_ENV_VAULT` is not set, then rehydrate local project/env evidence",
|
|
288
476
|
"- write and sync a secret-safe machine proof report, with optional bounded install/check/server proof flags",
|
|
289
477
|
"- bound portfolio hydration with `YOUMD_PORTFOLIO_HYDRATE_TIMEOUT_SECONDS` so large restored roots do not wedge setup",
|
|
290
478
|
"",
|
|
291
479
|
`Project source: You.md portfolio graph + authenticated GitHub recent repos, capped at ${limit} tracked projects before local audit evidence is merged. When the graph exists, new-computer setup clones only projects with status ACTIVE and focus Top Priority/Focusing; inactive, unsorted, on-ice, abandoned, killed, and unreviewed GitHub-only repos are skipped unless --include-inactive is explicitly used. First pass is ${days} days with out-of-window projects skipped; the ${expandDays}-day pass is explicit.`,
|
|
292
|
-
"Secret rule: .env.local values are never embedded here.
|
|
480
|
+
"Secret rule: .env.local values are never embedded here. Best path: on the old/source Mac, run `youmd env vault push --root ~/Desktop/CODE_2025 --out ~/Desktop/youmd-env-vault` once, then after the new Mac registers itself run `youmd env vault share` on the source Mac. That uploads only encrypted archive bytes plus safe manifest metadata, and stores only per-device encrypted passphrase envelopes. A trusted new Mac pulls the encrypted snapshot after login and decrypts locally with its private device key; raw env values never hit the browser, chat, or You.md servers. Local fallback: run `youmd env backup --root ~/Desktop/CODE_2025 --out ~/Desktop/youmd-env-vault`, move the generated `env-vault-*.tar.enc` file to `~/Desktop/youmd-env-vault/` or the iCloud Desktop `youmd-env-vault/` on the new machine, then run this command. The command checks Secret Vault first, then auto-detects the newest local vault; you can also override with `YOUMD_ENV_VAULT=/path/to/env-vault-*.tar.enc`. The restore path lists variable names/counts and target paths only, never values, maps old folder names onto cloned dirs, and skips agent auth config so it does not clobber the new machine's active Claude/Codex login.",
|
|
293
481
|
"",
|
|
294
482
|
"After the command finishes, report:",
|
|
295
483
|
"- the `youmd status` sync state",
|