youmd 0.7.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/commands/chat.d.ts.map +1 -1
  2. package/dist/commands/chat.js +3 -2
  3. package/dist/commands/chat.js.map +1 -1
  4. package/dist/commands/env.d.ts +2 -0
  5. package/dist/commands/env.d.ts.map +1 -1
  6. package/dist/commands/env.js +7 -3
  7. package/dist/commands/env.js.map +1 -1
  8. package/dist/commands/login.d.ts +1 -0
  9. package/dist/commands/login.d.ts.map +1 -1
  10. package/dist/commands/login.js +20 -4
  11. package/dist/commands/login.js.map +1 -1
  12. package/dist/commands/machine.d.ts +31 -0
  13. package/dist/commands/machine.d.ts.map +1 -1
  14. package/dist/commands/machine.js +610 -14
  15. package/dist/commands/machine.js.map +1 -1
  16. package/dist/commands/project-log.d.ts +13 -0
  17. package/dist/commands/project-log.d.ts.map +1 -0
  18. package/dist/commands/project-log.js +241 -0
  19. package/dist/commands/project-log.js.map +1 -0
  20. package/dist/commands/project.d.ts.map +1 -1
  21. package/dist/commands/project.js +872 -15
  22. package/dist/commands/project.js.map +1 -1
  23. package/dist/commands/prompts.d.ts +19 -0
  24. package/dist/commands/prompts.d.ts.map +1 -1
  25. package/dist/commands/prompts.js +304 -1
  26. package/dist/commands/prompts.js.map +1 -1
  27. package/dist/commands/pull.d.ts.map +1 -1
  28. package/dist/commands/pull.js +28 -8
  29. package/dist/commands/pull.js.map +1 -1
  30. package/dist/commands/push.d.ts +3 -1
  31. package/dist/commands/push.d.ts.map +1 -1
  32. package/dist/commands/push.js +26 -12
  33. package/dist/commands/push.js.map +1 -1
  34. package/dist/commands/skill.d.ts.map +1 -1
  35. package/dist/commands/skill.js +19 -15
  36. package/dist/commands/skill.js.map +1 -1
  37. package/dist/commands/stack.d.ts.map +1 -1
  38. package/dist/commands/stack.js +37 -17
  39. package/dist/commands/stack.js.map +1 -1
  40. package/dist/commands/status.d.ts.map +1 -1
  41. package/dist/commands/status.js +31 -0
  42. package/dist/commands/status.js.map +1 -1
  43. package/dist/commands/sync.d.ts +1 -0
  44. package/dist/commands/sync.d.ts.map +1 -1
  45. package/dist/commands/sync.js +51 -2
  46. package/dist/commands/sync.js.map +1 -1
  47. package/dist/index.js +125 -36
  48. package/dist/index.js.map +1 -1
  49. package/dist/lib/api.d.ts +324 -0
  50. package/dist/lib/api.d.ts.map +1 -1
  51. package/dist/lib/api.js +68 -0
  52. package/dist/lib/api.js.map +1 -1
  53. package/dist/lib/compiler.d.ts.map +1 -1
  54. package/dist/lib/compiler.js +10 -0
  55. package/dist/lib/compiler.js.map +1 -1
  56. package/dist/lib/daemon.d.ts +23 -0
  57. package/dist/lib/daemon.d.ts.map +1 -0
  58. package/dist/lib/daemon.js +166 -0
  59. package/dist/lib/daemon.js.map +1 -0
  60. package/dist/lib/first-run.d.ts +1 -1
  61. package/dist/lib/first-run.d.ts.map +1 -1
  62. package/dist/lib/first-run.js +8 -0
  63. package/dist/lib/first-run.js.map +1 -1
  64. package/dist/lib/machine-bootstrap-prompt.d.ts +17 -0
  65. package/dist/lib/machine-bootstrap-prompt.d.ts.map +1 -0
  66. package/dist/lib/machine-bootstrap-prompt.js +305 -0
  67. package/dist/lib/machine-bootstrap-prompt.js.map +1 -0
  68. package/dist/lib/machine-projects.d.ts +71 -0
  69. package/dist/lib/machine-projects.d.ts.map +1 -0
  70. package/dist/lib/machine-projects.js +510 -0
  71. package/dist/lib/machine-projects.js.map +1 -0
  72. package/dist/lib/machine-verify.d.ts +175 -0
  73. package/dist/lib/machine-verify.d.ts.map +1 -0
  74. package/dist/lib/machine-verify.js +684 -0
  75. package/dist/lib/machine-verify.js.map +1 -0
  76. package/dist/lib/portfolio-audit.d.ts +34 -0
  77. package/dist/lib/portfolio-audit.d.ts.map +1 -0
  78. package/dist/lib/portfolio-audit.js +254 -0
  79. package/dist/lib/portfolio-audit.js.map +1 -0
  80. package/dist/lib/portfolio-graph.d.ts +104 -0
  81. package/dist/lib/portfolio-graph.d.ts.map +1 -0
  82. package/dist/lib/portfolio-graph.js +281 -0
  83. package/dist/lib/portfolio-graph.js.map +1 -0
  84. package/dist/lib/project-strategy.d.ts +45 -0
  85. package/dist/lib/project-strategy.d.ts.map +1 -0
  86. package/dist/lib/project-strategy.js +234 -0
  87. package/dist/lib/project-strategy.js.map +1 -0
  88. package/dist/lib/reusable-patterns.d.ts +30 -0
  89. package/dist/lib/reusable-patterns.d.ts.map +1 -0
  90. package/dist/lib/reusable-patterns.js +375 -0
  91. package/dist/lib/reusable-patterns.js.map +1 -0
  92. package/dist/lib/skill-catalog.d.ts.map +1 -1
  93. package/dist/lib/skill-catalog.js +20 -0
  94. package/dist/lib/skill-catalog.js.map +1 -1
  95. package/dist/lib/skills.d.ts +1 -0
  96. package/dist/lib/skills.d.ts.map +1 -1
  97. package/dist/lib/skills.js +43 -13
  98. package/dist/lib/skills.js.map +1 -1
  99. package/dist/lib/version.d.ts +2 -0
  100. package/dist/lib/version.d.ts.map +1 -0
  101. package/dist/lib/version.js +63 -0
  102. package/dist/lib/version.js.map +1 -0
  103. package/dist/mcp/registry.d.ts +5 -1
  104. package/dist/mcp/registry.d.ts.map +1 -1
  105. package/dist/mcp/registry.js +426 -2
  106. package/dist/mcp/registry.js.map +1 -1
  107. package/dist/mcp/server.d.ts +2 -0
  108. package/dist/mcp/server.d.ts.map +1 -1
  109. package/dist/mcp/server.js +50 -5
  110. package/dist/mcp/server.js.map +1 -1
  111. package/package.json +1 -1
  112. package/scripts/env-vault/README.md +37 -10
  113. package/scripts/env-vault/backup.sh +70 -4
  114. package/scripts/env-vault/restore.sh +117 -9
  115. package/scripts/skillstack-sync/README.md +13 -6
  116. package/scripts/skillstack-sync/bootstrap-new-mac.sh +19 -5
  117. package/scripts/skillstack-sync/capture-agent-config.sh +110 -0
  118. package/scripts/skillstack-sync/com.youmd.context-sync.plist +38 -0
  119. package/scripts/skillstack-sync/com.youmd.identity-sync.plist +3 -1
  120. package/scripts/skillstack-sync/com.youmd.skillstack-sync.plist +1 -1
  121. package/scripts/skillstack-sync/context-sync.sh +412 -0
  122. package/scripts/skillstack-sync/install-daemons.sh +7 -3
  123. package/scripts/skillstack-sync/restore-agent-config.sh +152 -0
  124. package/scripts/skillstack-sync/sync.sh +20 -6
  125. package/skills/machine-bootstrap.md +243 -0
@@ -1,8 +1,22 @@
1
- # env-vault — Secure .env.local Backup & Restore
1
+ # env-vault — Secure .env.local + Agent-Auth Backup & Restore
2
2
 
3
- Backs up all `.env.local` files across your CODE_2025 projects into a single
4
- encrypted archive, and restores them on a new Mac. No secret values ever appear
5
- in plaintext outside the encrypted blob.
3
+ Backs up all `.env.local` files across your CODE_2025 projects **and** a fixed
4
+ set of hard agent-auth secret files into a single encrypted archive, and
5
+ restores them on a new Mac. No secret values ever appear in plaintext outside
6
+ the encrypted blob.
7
+
8
+ ### What's in the vault
9
+
10
+ | Source path | Archive path | Restore destination |
11
+ |---|---|---|
12
+ | `<CODE_2025>/<project>/.env.local` | `<project>/.env.local` | `<CODE_2025>/<project>/.env.local` |
13
+ | `~/.codex/auth.json` | `agent-auth/codex-auth.json` | `~/.codex/auth.json` |
14
+ | `~/.cursor/mcp.json` | `agent-auth/cursor-mcp.json` | `~/.cursor/mcp.json` |
15
+ | `~/.claude.json` | `agent-auth/claude.json` | `~/.claude.json` |
16
+
17
+ To add more agent-auth files, extend the `AGENT_SECRET_FILES` array near the
18
+ top of `backup.sh` and add the matching entry to `AGENT_AUTH_MAP` in
19
+ `restore.sh`.
6
20
 
7
21
  ---
8
22
 
@@ -14,6 +28,9 @@ in plaintext outside the encrypted blob.
14
28
  # via CLI (preferred — ships in the npm package)
15
29
  youmd env backup
16
30
 
31
+ # Check local encryption tooling and env discovery without writing a vault:
32
+ youmd env backup --preflight
33
+
17
34
  # or directly via bash (from the youmd repo root)
18
35
  cd /Users/houstongolden/Desktop/CODE_2025/youmd
19
36
  bash cli/scripts/env-vault/backup.sh
@@ -21,7 +38,7 @@ bash cli/scripts/env-vault/backup.sh
21
38
 
22
39
  This writes two files to `.env-vault/`:
23
40
  - `env-vault-<date>.tar.<ext>` — encrypted archive (safe to transport)
24
- - `manifest-<date>.txt` — variable names + counts only (safe to read, no values)
41
+ - `manifest-<date>.txt` — variable names + counts + agent-auth file paths/sizes only (safe to read, no values)
25
42
 
26
43
  You will be prompted for a passphrase. Store it in **1Password** immediately.
27
44
 
@@ -38,9 +55,10 @@ Do NOT transfer via unencrypted email or public Slack.
38
55
 
39
56
  ```bash
40
57
  # Install dependencies first (e.g. Node, pnpm, etc.), then:
58
+ youmd env restore /path/to/env-vault-<date>.tar.<ext> --list
41
59
  youmd env restore /path/to/env-vault-<date>.tar.<ext>
42
60
 
43
- # If project dirs already exist with .env.local files:
61
+ # If files already exist on the new Mac:
44
62
  youmd env restore --force /path/to/env-vault-<date>.tar.<ext>
45
63
 
46
64
  # or directly via bash:
@@ -48,8 +66,13 @@ bash cli/scripts/env-vault/restore.sh /path/to/env-vault-<date>.tar.<ext>
48
66
  bash cli/scripts/env-vault/restore.sh --force /path/to/env-vault-<date>.tar.<ext>
49
67
  ```
50
68
 
51
- The script creates `<CODE_2025>/<project>/.env.local` for each project.
52
- Existing files are backed up to `.env.local.bak.<timestamp>` (or overwritten with `--force`).
69
+ The script:
70
+ 1. Lists vault contents without writing files when `--list` is provided.
71
+ 2. Creates `<CODE_2025>/<project>/.env.local` for each project during restore.
72
+ 3. Restores agent-auth files to their absolute home paths (`~/.codex/auth.json`, `~/.cursor/mcp.json`, `~/.claude.json`), creating parent directories as needed.
73
+
74
+ Existing files are backed up to `<path>.bak.<timestamp>` before overwrite (or silently overwritten with `--force`).
75
+ List mode prints project env target paths, variable names/counts, and agent-auth file presence only. It never prints secret values.
53
76
 
54
77
  ---
55
78
 
@@ -70,8 +93,8 @@ The encrypted file itself is safe to store anywhere. The passphrase is the secre
70
93
  ## Security Model
71
94
 
72
95
  - **Encryption:** `age` (preferred) → `gpg AES-256` → `openssl AES-256-CBC PBKDF2`
73
- - **What's in the encrypted archive:** full `.env.local` file contents
74
- - **What's in plaintext:** only the project name + variable NAMES + count (the manifest)
96
+ - **What's in the encrypted archive:** full `.env.local` file contents + agent-auth files (`~/.codex/auth.json`, `~/.cursor/mcp.json`, `~/.claude.json`)
97
+ - **What's in plaintext:** only the project name + variable NAMES + count + agent-auth file paths/byte-sizes (the manifest)
75
98
  - **Values:** never printed, echoed, logged, or written anywhere outside the encrypted blob
76
99
  - **Passphrase:** stored nowhere by these scripts — you must store it in 1Password
77
100
 
@@ -84,10 +107,12 @@ and are exposed as first-class CLI commands:
84
107
 
85
108
  ```bash
86
109
  youmd env backup # back up all .env.local files
110
+ youmd env backup --preflight # verify tooling/discovery without writing
87
111
  youmd env backup --root <dir> # custom search root
88
112
  youmd env backup --out <dir> # custom output dir
89
113
 
90
114
  youmd env restore <vault> # restore from an encrypted vault
115
+ youmd env restore <vault> --list # list vault contents without writing
91
116
  youmd env restore --force # overwrite existing .env.local files
92
117
  youmd env restore --root <dir> # restore into a custom root
93
118
  ```
@@ -104,6 +129,7 @@ your terminal unchanged.
104
129
 
105
130
  | Flag | Default | Description |
106
131
  |------|---------|-------------|
132
+ | `--preflight` | off | Verify encryption tooling/discovery without writing a vault |
107
133
  | `--root <dir>` | `~/Desktop/CODE_2025` | Where to discover `.env.local` files |
108
134
  | `--out <dir>` | `youmd/.env-vault/` | Where to write encrypted vault + manifest |
109
135
 
@@ -111,6 +137,7 @@ your terminal unchanged.
111
137
 
112
138
  | Flag | Default | Description |
113
139
  |------|---------|-------------|
140
+ | `--list` | off | List vault contents without writing files |
114
141
  | `--root <dir>` | `~/Desktop/CODE_2025` | Where to restore project `.env.local` files |
115
142
  | `--force` | off | Overwrite existing `.env.local` instead of skipping |
116
143
 
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env bash
2
- # env-vault/backup.sh — backs up all .env.local files under CODE_2025 into an
3
- # encrypted vault. NEVER prints, echoes, logs, or writes any secret VALUE.
4
- # Only variable NAMES and counts appear in plaintext output.
2
+ # env-vault/backup.sh — backs up all .env.local files under CODE_2025 AND a
3
+ # fixed set of agent-auth secret files into an encrypted vault. NEVER prints,
4
+ # echoes, logs, or writes any secret VALUE. Only variable NAMES, counts, and
5
+ # file paths appear in plaintext output.
5
6
  #
6
- # Usage: bash backup.sh [--root <search-root>] [--out <output-dir>]
7
+ # Usage: bash backup.sh [--preflight] [--root <search-root>] [--out <output-dir>]
7
8
  # Defaults:
8
9
  # search-root: /Users/houstongolden/Desktop/CODE_2025
9
10
  # output-dir: /Users/houstongolden/Desktop/CODE_2025/youmd/.env-vault
@@ -13,10 +14,22 @@ set -euo pipefail
13
14
  # ── configurable defaults ──────────────────────────────────────────────────────
14
15
  SEARCH_ROOT="${ENV_VAULT_ROOT:-/Users/houstongolden/Desktop/CODE_2025}"
15
16
  OUTPUT_DIR="${ENV_VAULT_OUT:-/Users/houstongolden/Desktop/CODE_2025/youmd/.env-vault}"
17
+ PREFLIGHT=false
18
+
19
+ # ── agent-auth secret files ────────────────────────────────────────────────────
20
+ # Absolute paths to hard agent-auth secrets that must travel with the vault.
21
+ # Each entry is archived as agent-auth/<stable-name> inside the tar.
22
+ # To add more: append to this array. Format: "absolute-path:stable-archive-name"
23
+ AGENT_SECRET_FILES=(
24
+ "${HOME}/.codex/auth.json:codex-auth.json"
25
+ "${HOME}/.cursor/mcp.json:cursor-mcp.json"
26
+ "${HOME}/.claude.json:claude.json"
27
+ )
16
28
 
17
29
  # ── parse flags ────────────────────────────────────────────────────────────────
18
30
  while [[ $# -gt 0 ]]; do
19
31
  case $1 in
32
+ --preflight) PREFLIGHT=true; shift ;;
20
33
  --root) SEARCH_ROOT="$2"; shift 2 ;;
21
34
  --out) OUTPUT_DIR="$2"; shift 2 ;;
22
35
  *) echo "Unknown flag: $1" >&2; exit 1 ;;
@@ -55,11 +68,32 @@ done < <(find "${SEARCH_ROOT}" -maxdepth 2 -name ".env.local" -print0 2>/dev/nul
55
68
 
56
69
  if [[ ${#ENV_FILES[@]} -eq 0 ]]; then
57
70
  echo "No .env.local files found under ${SEARCH_ROOT}" >&2
71
+ if [[ "${PREFLIGHT}" == "true" ]]; then
72
+ echo "Preflight only: encryption tooling is available, but no .env.local files were found."
73
+ exit 0
74
+ fi
58
75
  exit 1
59
76
  fi
60
77
 
61
78
  echo "Found ${#ENV_FILES[@]} .env.local file(s)."
62
79
 
80
+ if [[ "${PREFLIGHT}" == "true" ]]; then
81
+ echo "Output dir: ${OUTPUT_DIR}"
82
+ echo "Agent-auth candidates:"
83
+ for ENTRY in "${AGENT_SECRET_FILES[@]}"; do
84
+ SRC_PATH="${ENTRY%%:*}"
85
+ ARCHIVE_NAME="${ENTRY##*:}"
86
+ if [[ -f "${SRC_PATH}" ]]; then
87
+ FILE_BYTES=$(wc -c < "${SRC_PATH}" | tr -d ' ')
88
+ echo " present: agent-auth/${ARCHIVE_NAME} (${FILE_BYTES} bytes)"
89
+ else
90
+ echo " missing: agent-auth/${ARCHIVE_NAME}"
91
+ fi
92
+ done
93
+ echo "Preflight only: no vault written and no secret values printed."
94
+ exit 0
95
+ fi
96
+
63
97
  # ── build plaintext manifest (names + counts, NO values) ─────────────────────
64
98
  mkdir -p "${OUTPUT_DIR}"
65
99
  UTC_DATE="$(date -u +%Y-%m-%dT%H%MZ)"
@@ -94,6 +128,38 @@ for ENV_FILE in "${ENV_FILES[@]}"; do
94
128
  >> "${MANIFEST_PATH}"
95
129
  done
96
130
 
131
+ # ── stage agent-auth secret files ─────────────────────────────────────────────
132
+ # Each entry in AGENT_SECRET_FILES has format "absolute-path:stable-archive-name".
133
+ # Files are stored under agent-auth/<stable-name> inside the archive.
134
+ # Only the path and byte-size appear in the manifest — never the contents.
135
+ AGENT_AUTH_STAGING="${STAGING_DIR}/agent-auth"
136
+ mkdir -p "${AGENT_AUTH_STAGING}"
137
+ AGENT_AUTH_COUNT=0
138
+
139
+ echo "" >> "${MANIFEST_PATH}"
140
+ echo "agent-auth files:" >> "${MANIFEST_PATH}"
141
+
142
+ for ENTRY in "${AGENT_SECRET_FILES[@]}"; do
143
+ SRC_PATH="${ENTRY%%:*}"
144
+ ARCHIVE_NAME="${ENTRY##*:}"
145
+
146
+ if [[ -f "${SRC_PATH}" ]]; then
147
+ cp "${SRC_PATH}" "${AGENT_AUTH_STAGING}/${ARCHIVE_NAME}"
148
+ FILE_BYTES=$(wc -c < "${SRC_PATH}" | tr -d ' ')
149
+ echo " agent-auth/${ARCHIVE_NAME} (${SRC_PATH}): ${FILE_BYTES} bytes" \
150
+ >> "${MANIFEST_PATH}"
151
+ echo " Staged agent-auth: ${SRC_PATH} → agent-auth/${ARCHIVE_NAME}"
152
+ (( AGENT_AUTH_COUNT++ )) || true
153
+ else
154
+ echo " agent-auth/${ARCHIVE_NAME} (${SRC_PATH}): NOT FOUND — skipped" \
155
+ >> "${MANIFEST_PATH}"
156
+ echo " Skipped (not found): ${SRC_PATH}"
157
+ fi
158
+ done
159
+
160
+ echo ""
161
+ echo "Staged ${AGENT_AUTH_COUNT} agent-auth file(s)."
162
+
97
163
  echo "" >> "${MANIFEST_PATH}"
98
164
  echo "SECURITY: The encrypted archive is safe to transport. Keep the passphrase private." \
99
165
  >> "${MANIFEST_PATH}"
@@ -1,23 +1,42 @@
1
1
  #!/usr/bin/env bash
2
- # env-vault/restore.sh — restores .env.local files from an encrypted vault
3
- # created by backup.sh. NEVER prints, echoes, or logs any secret VALUES.
4
- # Only prints which file paths were restored or skipped.
2
+ # env-vault/restore.sh — restores .env.local files AND agent-auth secrets from
3
+ # an encrypted vault created by backup.sh. NEVER prints, echoes, or logs any
4
+ # secret VALUES. Restore prints which file paths were restored or skipped; list
5
+ # mode prints target paths plus variable names/counts only.
5
6
  #
6
- # Usage: bash restore.sh [--force] [--root <target-root>] <vault-file>
7
+ # Usage: bash restore.sh [--list] [--force] [--root <target-root>] <vault-file>
7
8
  # vault-file: path to env-vault-*.tar.age / .tar.gpg / .tar.enc
8
- # --force: overwrite existing .env.local files without backing them up
9
- # --root: destination root (default: /Users/houstongolden/Desktop/CODE_2025)
9
+ # --force: overwrite existing files without backing them up
10
+ # --root: destination root for .env.local files
11
+ # (default: /Users/houstongolden/Desktop/CODE_2025)
12
+ #
13
+ # agent-auth restore map (archive name → absolute home path):
14
+ # agent-auth/codex-auth.json → ~/.codex/auth.json
15
+ # agent-auth/cursor-mcp.json → ~/.cursor/mcp.json
16
+ # agent-auth/claude.json → ~/.claude.json
10
17
 
11
18
  set -euo pipefail
12
19
 
20
+ # ── agent-auth restore map ─────────────────────────────────────────────────────
21
+ # Maps archive name (inside agent-auth/) to its absolute home destination.
22
+ # Format: "archive-name:absolute-destination-path"
23
+ # Must stay in sync with AGENT_SECRET_FILES in backup.sh.
24
+ AGENT_AUTH_MAP=(
25
+ "codex-auth.json:${HOME}/.codex/auth.json"
26
+ "cursor-mcp.json:${HOME}/.cursor/mcp.json"
27
+ "claude.json:${HOME}/.claude.json"
28
+ )
29
+
13
30
  # ── defaults ──────────────────────────────────────────────────────────────────
14
31
  TARGET_ROOT="${ENV_VAULT_RESTORE_ROOT:-/Users/houstongolden/Desktop/CODE_2025}"
15
32
  FORCE=false
33
+ LIST_ONLY=false
16
34
  VAULT_FILE=""
17
35
 
18
36
  # ── parse args ─────────────────────────────────────────────────────────────────
19
37
  while [[ $# -gt 0 ]]; do
20
38
  case $1 in
39
+ --list) LIST_ONLY=true; shift ;;
21
40
  --force) FORCE=true; shift ;;
22
41
  --root) TARGET_ROOT="$2"; shift 2 ;;
23
42
  -*) echo "Unknown flag: $1" >&2; exit 1 ;;
@@ -33,7 +52,7 @@ while [[ $# -gt 0 ]]; do
33
52
  done
34
53
 
35
54
  if [[ -z "${VAULT_FILE}" ]]; then
36
- echo "Usage: bash restore.sh [--force] [--root <path>] <vault-file>" >&2
55
+ echo "Usage: bash restore.sh [--list] [--force] [--root <path>] <vault-file>" >&2
37
56
  echo " vault-file: env-vault-*.tar.age | .tar.gpg | .tar.enc" >&2
38
57
  exit 1
39
58
  fi
@@ -103,6 +122,45 @@ EXTRACT_DIR="${WORK_DIR}/extracted"
103
122
  mkdir -p "${EXTRACT_DIR}"
104
123
  tar -xf "${TAR_PATH}" -C "${EXTRACT_DIR}"
105
124
 
125
+ if [[ "${LIST_ONLY}" == "true" ]]; then
126
+ ENV_COUNT=0
127
+ AGENT_PRESENT=0
128
+ AGENT_MISSING=0
129
+
130
+ echo "Vault contents (secret-safe inventory):"
131
+ while IFS= read -r -d '' ENV_FILE; do
132
+ REL_PATH="${ENV_FILE#${EXTRACT_DIR}/}"
133
+ PROJECT_NAME="$(dirname "${REL_PATH}")"
134
+ DEST="${TARGET_ROOT}/${PROJECT_NAME}/.env.local"
135
+ VAR_COUNT=$(grep -cE '^[A-Za-z_][A-Za-z0-9_]*=' "${ENV_FILE}" 2>/dev/null || true)
136
+ VAR_NAMES=$(grep -oE '^[A-Za-z_][A-Za-z0-9_]*' "${ENV_FILE}" 2>/dev/null \
137
+ | grep -v '^$' | sort | paste -sd ',' - || true)
138
+ echo " env: ${PROJECT_NAME}/.env.local -> ${DEST} (${VAR_COUNT} variable(s) [${VAR_NAMES}])"
139
+ (( ENV_COUNT++ )) || true
140
+ done < <(find "${EXTRACT_DIR}" -name ".env.local" -print0)
141
+
142
+ AGENT_AUTH_SRC="${EXTRACT_DIR}/agent-auth"
143
+ echo " agent-auth:"
144
+ for ENTRY in "${AGENT_AUTH_MAP[@]}"; do
145
+ ARCHIVE_NAME="${ENTRY%%:*}"
146
+ DEST_PATH="${ENTRY##*:}"
147
+ SRC_FILE="${AGENT_AUTH_SRC}/${ARCHIVE_NAME}"
148
+ if [[ -f "${SRC_FILE}" ]]; then
149
+ FILE_BYTES=$(wc -c < "${SRC_FILE}" | tr -d ' ')
150
+ echo " present: agent-auth/${ARCHIVE_NAME} -> ${DEST_PATH} (${FILE_BYTES} bytes)"
151
+ (( AGENT_PRESENT++ )) || true
152
+ else
153
+ echo " missing: agent-auth/${ARCHIVE_NAME}"
154
+ (( AGENT_MISSING++ )) || true
155
+ fi
156
+ done
157
+
158
+ echo ""
159
+ echo "List summary: ${ENV_COUNT} .env.local file(s), ${AGENT_PRESENT} agent-auth file(s) present, ${AGENT_MISSING} agent-auth file(s) missing."
160
+ echo "No files were written and no secret values were printed."
161
+ exit 0
162
+ fi
163
+
106
164
  # ── restore each .env.local ───────────────────────────────────────────────────
107
165
  RESTORED=0
108
166
  SKIPPED=0
@@ -139,11 +197,61 @@ while IFS= read -r -d '' ENV_FILE; do
139
197
  fi
140
198
  done < <(find "${EXTRACT_DIR}" -name ".env.local" -print0)
141
199
 
200
+ # ── restore agent-auth files ───────────────────────────────────────────────────
201
+ AGENT_RESTORED=0
202
+ AGENT_SKIPPED=0
203
+ AGENT_BACKED_UP=0
204
+
205
+ AGENT_AUTH_SRC="${EXTRACT_DIR}/agent-auth"
206
+
207
+ if [[ -d "${AGENT_AUTH_SRC}" ]]; then
208
+ echo "Restoring agent-auth files…"
209
+ for ENTRY in "${AGENT_AUTH_MAP[@]}"; do
210
+ ARCHIVE_NAME="${ENTRY%%:*}"
211
+ DEST_PATH="${ENTRY##*:}"
212
+ SRC_FILE="${AGENT_AUTH_SRC}/${ARCHIVE_NAME}"
213
+
214
+ if [[ ! -f "${SRC_FILE}" ]]; then
215
+ echo " SKIPPED (not in vault): agent-auth/${ARCHIVE_NAME}"
216
+ (( AGENT_SKIPPED++ )) || true
217
+ continue
218
+ fi
219
+
220
+ DEST_DIR="$(dirname "${DEST_PATH}")"
221
+ mkdir -p "${DEST_DIR}"
222
+
223
+ if [[ -f "${DEST_PATH}" ]]; then
224
+ if [[ "${FORCE}" == "true" ]]; then
225
+ cp "${SRC_FILE}" "${DEST_PATH}"
226
+ echo " RESTORED (overwrite): ${DEST_PATH}"
227
+ (( AGENT_RESTORED++ )) || true
228
+ else
229
+ TS="$(date -u +%Y%m%dT%H%M%SZ)"
230
+ BAK="${DEST_PATH}.bak.${TS}"
231
+ cp "${DEST_PATH}" "${BAK}"
232
+ cp "${SRC_FILE}" "${DEST_PATH}"
233
+ echo " RESTORED (backed up old → $(basename "${BAK}")): ${DEST_PATH}"
234
+ (( AGENT_RESTORED++ )) || true
235
+ (( AGENT_BACKED_UP++ )) || true
236
+ fi
237
+ else
238
+ cp "${SRC_FILE}" "${DEST_PATH}"
239
+ echo " RESTORED (new): ${DEST_PATH}"
240
+ (( AGENT_RESTORED++ )) || true
241
+ fi
242
+ done
243
+ echo ""
244
+ else
245
+ echo "No agent-auth/ directory in vault — skipping agent-auth restore."
246
+ echo ""
247
+ fi
248
+
142
249
  echo ""
143
250
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
144
251
  echo " Restore summary"
145
252
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
146
- echo " Restored: ${RESTORED} | Skipped: ${SKIPPED} | Old files backed up: ${BACKED_UP}"
253
+ echo " .env.local — Restored: ${RESTORED} | Skipped: ${SKIPPED} | Old files backed up: ${BACKED_UP}"
254
+ echo " agent-auth — Restored: ${AGENT_RESTORED} | Skipped: ${AGENT_SKIPPED} | Old files backed up: ${AGENT_BACKED_UP}"
147
255
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
148
256
  echo ""
149
- echo "Secrets are now in place. Do not print or share .env.local files."
257
+ echo "Secrets are now in place. Do not print or share secret files."
@@ -28,7 +28,8 @@ Cross-machine sync toolkit for Houston Golden's agent skill/stack setup.
28
28
 
29
29
  ### Complementary sync planes
30
30
 
31
- - **Identity plane:** `youmd sync` (Convex ↔ local profile, memories, preferences). Automated via `com.youmd.identity-sync` daemon.
31
+ - **Identity plane:** `youmd sync --daemon` (Convex ↔ local profile, memories, preferences). Automated via `com.youmd.identity-sync` daemon. Daemon mode refreshes local files/skills and skips unsafe lossy pushes instead of forcing over richer server data.
32
+ - **Project-context plane:** `youmd stack context-sync` safely commits and syncs only `AGENTS.md`, `CLAUDE.md`, `project-context/`, and `.claude/` in curated Houston-owned repos. It refuses to merge remote app-code changes.
32
33
  - **Env-vault plane:** `cli/scripts/env-vault/restore.sh` for secrets (manual, invoked on new machine); also available as `youmd env restore <vault>`.
33
34
 
34
35
  ---
@@ -38,10 +39,11 @@ Cross-machine sync toolkit for Houston Golden's agent skill/stack setup.
38
39
  ```
39
40
  cli/scripts/skillstack-sync/
40
41
  ├── sync.sh Core syncer (bash 3.2 compatible)
41
- ├── install-daemons.sh Installs both LaunchAgents
42
+ ├── install-daemons.sh Installs all resident LaunchAgents
42
43
  ├── bootstrap-new-mac.sh New machine setup script
43
44
  ├── com.youmd.skillstack-sync.plist LaunchAgent: runs `youmd stack sync` every 5 min
44
- ├── com.youmd.identity-sync.plist LaunchAgent: runs `youmd sync` every 5 min
45
+ ├── com.youmd.identity-sync.plist LaunchAgent: runs `youmd sync --daemon` every 5 min
46
+ ├── com.youmd.context-sync.plist LaunchAgent: runs `youmd stack context-sync` every 15 min
45
47
  └── README.md This file
46
48
  ```
47
49
 
@@ -90,7 +92,7 @@ GIT_USER_EMAIL="houston@bamf.ai" # commit email (default)
90
92
  The preferred way is via the CLI:
91
93
 
92
94
  ```bash
93
- youmd stack daemon install # installs both LaunchAgents
95
+ youmd stack daemon install # installs all resident LaunchAgents
94
96
  youmd stack daemon status # check loaded/not-loaded
95
97
  youmd stack daemon uninstall # remove plists from ~/Library/LaunchAgents/
96
98
  ```
@@ -101,9 +103,10 @@ Or run the raw script directly:
101
103
  bash cli/scripts/skillstack-sync/install-daemons.sh
102
104
  ```
103
105
 
104
- Installs two LaunchAgents that fire every 300 seconds (5 minutes):
106
+ Installs three LaunchAgents:
105
107
  - `com.youmd.skillstack-sync` → runs `youmd stack sync`
106
- - `com.youmd.identity-sync` → runs `youmd sync`
108
+ - `com.youmd.identity-sync` → runs `youmd sync --daemon`
109
+ - `com.youmd.context-sync` → runs `youmd stack context-sync`
107
110
 
108
111
  Check status:
109
112
  ```bash
@@ -111,6 +114,7 @@ youmd stack daemon status
111
114
  # or directly:
112
115
  launchctl list com.youmd.skillstack-sync
113
116
  launchctl list com.youmd.identity-sync
117
+ launchctl list com.youmd.context-sync
114
118
  ```
115
119
 
116
120
  Check logs:
@@ -118,6 +122,7 @@ Check logs:
118
122
  tail -f ~/.youmd/logs/skillstack-sync.log
119
123
  tail -f ~/.youmd/logs/skillstack-sync.out.log
120
124
  tail -f ~/.youmd/logs/identity-sync.out.log
125
+ tail -f ~/.youmd/logs/context-sync.log
121
126
  ```
122
127
 
123
128
  ### Uninstall daemons
@@ -130,8 +135,10 @@ Or manually:
130
135
  ```bash
131
136
  launchctl unload ~/Library/LaunchAgents/com.youmd.skillstack-sync.plist
132
137
  launchctl unload ~/Library/LaunchAgents/com.youmd.identity-sync.plist
138
+ launchctl unload ~/Library/LaunchAgents/com.youmd.context-sync.plist
133
139
  rm ~/Library/LaunchAgents/com.youmd.skillstack-sync.plist
134
140
  rm ~/Library/LaunchAgents/com.youmd.identity-sync.plist
141
+ rm ~/Library/LaunchAgents/com.youmd.context-sync.plist
135
142
  ```
136
143
 
137
144
  ---
@@ -101,14 +101,29 @@ for skill in ${LOOSE_SKILLS}; do
101
101
  done
102
102
 
103
103
  # ---------------------------------------------------------------------------
104
- step "5. Reminder: restore secrets"
104
+ step "5. Restore agent config from agent-shared"
105
+ # ---------------------------------------------------------------------------
106
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
107
+ RESTORE_SCRIPT="${SCRIPT_DIR}/restore-agent-config.sh"
108
+
109
+ if [ -f "${RESTORE_SCRIPT}" ]; then
110
+ info "Running restore-agent-config.sh (backs up existing files, safe to re-run)..."
111
+ bash "${RESTORE_SCRIPT}"
112
+ info "Agent config restored."
113
+ else
114
+ warn "restore-agent-config.sh not found at ${RESTORE_SCRIPT} — skipping agent config restore."
115
+ warn "You can run it manually later once youmd is fully installed:"
116
+ warn " bash ${RESTORE_SCRIPT}"
117
+ fi
118
+
119
+ # ---------------------------------------------------------------------------
120
+ step "6. Reminder: restore secrets"
105
121
  # ---------------------------------------------------------------------------
106
122
  echo ""
107
123
  echo " !! IMPORTANT: Secrets are NOT in git. Restore them separately:"
108
124
  echo ""
109
- echo " If you use the env-vault (~/Desktop/CODE_2025/youmd/cli/scripts/env-vault/):"
125
+ echo " If you use the env-vault:"
110
126
  echo " youmd env restore <vault-file> # CLI wrapper (preferred)"
111
- echo " bash ~/Desktop/CODE_2025/youmd/cli/scripts/env-vault/restore.sh <vault-file>"
112
127
  echo ""
113
128
  echo " Secrets to restore (set in your shell profile or .env files):"
114
129
  echo " OPENROUTER_API_KEY"
@@ -119,7 +134,7 @@ echo " ANTHROPIC_API_KEY"
119
134
  echo ""
120
135
 
121
136
  # ---------------------------------------------------------------------------
122
- step "6. Next steps"
137
+ step "7. Next steps"
123
138
  # ---------------------------------------------------------------------------
124
139
  echo ""
125
140
  echo " Bootstrap complete. To activate the auto-sync daemons, run:"
@@ -127,7 +142,6 @@ echo ""
127
142
  echo " youmd stack daemon install # preferred (CLI-native)"
128
143
  echo ""
129
144
  echo " Or run the raw script directly:"
130
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
131
145
  echo " bash ${SCRIPT_DIR}/install-daemons.sh"
132
146
  echo ""
133
147
  echo " To do a manual sync now:"
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bash
2
+ # capture-agent-config.sh — snapshot NON-SECRET agent config into ~/.agent-shared/
3
+ #
4
+ # This captures the agent-behavior config that isn't already in a synced repo —
5
+ # Claude/Codex/Warp settings, slash commands, plugin list, the orphaned
6
+ # ~/.agents skills, and the launchd/cron automations — into the agent-shared
7
+ # repo so they travel across machines. The skillstack-sync daemon then commits +
8
+ # pushes agent-shared, and `youmd machine setup`/restore applies them on a new Mac.
9
+ #
10
+ # SECRETS NEVER GO HERE. agent-shared is a git repo. Live secrets (codex
11
+ # auth.json, cursor mcp.json, ~/.claude.json BAMF token, .env.local) travel via
12
+ # the ENCRYPTED env-vault instead. This script captures only files confirmed to
13
+ # be secret-free, and a final guard greps the staged output for secret patterns
14
+ # and ABORTS if any are found (nothing is left in agent-shared on abort).
15
+ #
16
+ # bash 3.2 / BSD safe. Idempotent. --dry-run to preview.
17
+
18
+ set -euo pipefail
19
+
20
+ AGENT_SHARED="${HOME}/.agent-shared"
21
+ DEST="${AGENT_SHARED}/agent-config"
22
+ LOG_DIR="${HOME}/.youmd/logs"
23
+ LOG_FILE="${LOG_DIR}/capture-agent-config.log"
24
+
25
+ DRY_RUN=0
26
+ for arg in "$@"; do
27
+ case "$arg" in
28
+ --dry-run) DRY_RUN=1 ;;
29
+ *) echo "Unknown flag: $arg" >&2; exit 1 ;;
30
+ esac
31
+ done
32
+
33
+ mkdir -p "${LOG_DIR}"
34
+ log() { local ts; ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"; echo "[${ts}] $*"; echo "[${ts}] $*" >> "${LOG_FILE}"; }
35
+ is_dry() { [ "${DRY_RUN}" -eq 1 ]; }
36
+
37
+ if [ ! -d "${AGENT_SHARED}/.git" ]; then
38
+ log "ERROR: ${AGENT_SHARED} is not a git repo — run machine setup first."
39
+ exit 1
40
+ fi
41
+
42
+ # copy_in <src> <dest-subpath> — copy a file/dir into the staging dest if it exists
43
+ copy_in() {
44
+ local src="$1" rel="$2" target
45
+ target="${DEST}/${rel}"
46
+ if [ ! -e "${src}" ]; then return 0; fi
47
+ if is_dry; then log "DRY would capture ${src} → agent-config/${rel}"; return 0; fi
48
+ mkdir -p "$(dirname "${target}")"
49
+ if [ -d "${src}" ]; then
50
+ rsync -a --delete "${src}/" "${target}/"
51
+ else
52
+ cp "${src}" "${target}"
53
+ fi
54
+ log "captured ${src} → agent-config/${rel}"
55
+ }
56
+
57
+ log "=== capture-agent-config START ($([ "${DRY_RUN}" -eq 1 ] && echo DRY || echo LIVE)) ==="
58
+
59
+ # ── Claude config (settings have no raw secrets; ~/.claude.json is EXCLUDED — vaulted) ──
60
+ copy_in "${HOME}/.claude/settings.json" "claude/settings.json"
61
+ copy_in "${HOME}/.claude/settings.local.json" "claude/settings.local.json"
62
+ copy_in "${HOME}/.claude/commands" "claude/commands"
63
+ copy_in "${HOME}/.claude/mcp.json" "claude/mcp.json"
64
+ copy_in "${HOME}/.claude/plugins/installed_plugins.json" "claude/plugins/installed_plugins.json"
65
+
66
+ # ── Codex config (config.toml only; auth.json is EXCLUDED — vaulted) ──
67
+ copy_in "${HOME}/.codex/config.toml" "codex/config.toml"
68
+
69
+ # ── Warp (no secrets) ──
70
+ copy_in "${HOME}/.warp/settings.toml" "warp/settings.toml"
71
+ copy_in "${HOME}/.warp/keybindings.yaml" "warp/keybindings.yaml"
72
+
73
+ # ── Orphaned ~/.agents skills (no repo of their own today) ──
74
+ copy_in "${HOME}/.agents/skills/find-skills" "agents-skills/find-skills"
75
+ copy_in "${HOME}/.agents/skills/ui-ux-pro-max" "agents-skills/ui-ux-pro-max"
76
+
77
+ # ── Automations: launchd plists (HOME-templated) + crontab snapshot ──
78
+ capture_plist() {
79
+ local label="$1" src="${HOME}/Library/LaunchAgents/$1.plist" target="${DEST}/automations/launchd/$1.plist"
80
+ if [ ! -f "${src}" ]; then return 0; fi
81
+ if is_dry; then log "DRY would capture plist ${label} (HOME-templated)"; return 0; fi
82
+ mkdir -p "$(dirname "${target}")"
83
+ sed "s|${HOME}|__HOME__|g" "${src}" > "${target}"
84
+ log "captured plist ${label} → agent-config/automations/launchd/${label}.plist"
85
+ }
86
+ capture_plist "com.youmd.skillstack-sync"
87
+ capture_plist "com.youmd.identity-sync"
88
+ capture_plist "com.youmd.context-sync"
89
+ capture_plist "com.houstongolden.agent-runtime-guard"
90
+
91
+ if ! is_dry; then
92
+ mkdir -p "${DEST}/automations"
93
+ crontab -l 2>/dev/null > "${DEST}/automations/crontab.txt" || true
94
+ log "captured crontab → agent-config/automations/crontab.txt"
95
+ fi
96
+
97
+ # ── SECRET-LEAK GUARD — abort if anything captured looks like a live secret ──
98
+ if ! is_dry && [ -d "${DEST}" ]; then
99
+ # Match real secret token shapes; allow bare key NAMES (KEY=) which are fine.
100
+ HITS="$(grep -rIlE 'sk-ant-[A-Za-z0-9_-]{20}|sk-[A-Za-z0-9]{32}|ghp_[A-Za-z0-9]{30}|gho_[A-Za-z0-9]{30}|Bearer [A-Za-z0-9._-]{20}|AKIA[0-9A-Z]{16}|xox[baprs]-[A-Za-z0-9-]{10}' "${DEST}" 2>/dev/null || true)"
101
+ if [ -n "${HITS}" ]; then
102
+ log "ABORT: possible secret(s) found in captured config — removing capture, nothing committed:"
103
+ log "${HITS}"
104
+ rm -rf "${DEST}"
105
+ exit 1
106
+ fi
107
+ log "secret-leak guard: clean (no live secret patterns in captured config)"
108
+ fi
109
+
110
+ log "=== capture-agent-config DONE ==="
@@ -0,0 +1,38 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.youmd.context-sync</string>
7
+
8
+ <key>ProgramArguments</key>
9
+ <array>
10
+ <string>/bin/zsh</string>
11
+ <string>-lc</string>
12
+ <string>youmd stack context-sync</string>
13
+ </array>
14
+
15
+ <key>StartInterval</key>
16
+ <integer>900</integer>
17
+
18
+ <key>RunAtLoad</key>
19
+ <true/>
20
+
21
+ <key>KeepAlive</key>
22
+ <false/>
23
+
24
+ <key>StandardOutPath</key>
25
+ <string>__HOME__/.youmd/logs/context-sync.out.log</string>
26
+
27
+ <key>StandardErrorPath</key>
28
+ <string>__HOME__/.youmd/logs/context-sync.err.log</string>
29
+
30
+ <key>EnvironmentVariables</key>
31
+ <dict>
32
+ <key>HOME</key>
33
+ <string>__HOME__</string>
34
+ <key>PATH</key>
35
+ <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
36
+ </dict>
37
+ </dict>
38
+ </plist>