youmd 0.8.2 → 0.8.5

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 (42) hide show
  1. package/dist/commands/chat.d.ts.map +1 -1
  2. package/dist/commands/chat.js +17 -0
  3. package/dist/commands/chat.js.map +1 -1
  4. package/dist/commands/env.d.ts +19 -4
  5. package/dist/commands/env.d.ts.map +1 -1
  6. package/dist/commands/env.js +243 -21
  7. package/dist/commands/env.js.map +1 -1
  8. package/dist/commands/stack.d.ts.map +1 -1
  9. package/dist/commands/stack.js +8 -2
  10. package/dist/commands/stack.js.map +1 -1
  11. package/dist/commands/status.d.ts.map +1 -1
  12. package/dist/commands/status.js +8 -1
  13. package/dist/commands/status.js.map +1 -1
  14. package/dist/commands/sync.d.ts +1 -0
  15. package/dist/commands/sync.d.ts.map +1 -1
  16. package/dist/commands/sync.js +184 -0
  17. package/dist/commands/sync.js.map +1 -1
  18. package/dist/index.js +40 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/api.d.ts +74 -0
  21. package/dist/lib/api.d.ts.map +1 -1
  22. package/dist/lib/api.js +36 -0
  23. package/dist/lib/api.js.map +1 -1
  24. package/dist/lib/daemon.d.ts +3 -0
  25. package/dist/lib/daemon.d.ts.map +1 -1
  26. package/dist/lib/daemon.js +21 -0
  27. package/dist/lib/daemon.js.map +1 -1
  28. package/dist/lib/machine-bootstrap-prompt.d.ts.map +1 -1
  29. package/dist/lib/machine-bootstrap-prompt.js +305 -184
  30. package/dist/lib/machine-bootstrap-prompt.js.map +1 -1
  31. package/dist/lib/realtime-sync.d.ts +128 -0
  32. package/dist/lib/realtime-sync.d.ts.map +1 -0
  33. package/dist/lib/realtime-sync.js +215 -0
  34. package/dist/lib/realtime-sync.js.map +1 -0
  35. package/package.json +2 -1
  36. package/scripts/env-vault/README.md +9 -0
  37. package/scripts/env-vault/restore.sh +85 -12
  38. package/scripts/skillstack-sync/README.md +24 -6
  39. package/scripts/skillstack-sync/bootstrap-new-mac.sh +31 -0
  40. package/scripts/skillstack-sync/com.youmd.realtime-sync.plist +35 -0
  41. package/scripts/skillstack-sync/install-daemons.sh +4 -1
  42. package/skills/machine-bootstrap.md +88 -19
@@ -0,0 +1,128 @@
1
+ export type RealtimeSyncHead = {
2
+ schemaVersion?: string;
3
+ serverNow?: number;
4
+ sessionExpiresAt?: number;
5
+ user?: {
6
+ username?: string;
7
+ plan?: string;
8
+ };
9
+ identity?: {
10
+ latestVersion?: number | null;
11
+ latestHash?: string | null;
12
+ publishedVersion?: number | null;
13
+ publishedHash?: string | null;
14
+ latestCreatedAt?: number | null;
15
+ latestPublishedAt?: number | null;
16
+ };
17
+ skills?: {
18
+ installedCount?: number;
19
+ latestInstalledAt?: number | null;
20
+ names?: string[];
21
+ };
22
+ portfolio?: {
23
+ updatedAt?: number | null;
24
+ projects?: number;
25
+ activeFocusedProjects?: number;
26
+ apiSurfaces?: number;
27
+ dependencyEdges?: number;
28
+ reusablePatterns?: number;
29
+ activities?: number;
30
+ providerAccounts?: number;
31
+ tasks?: number;
32
+ brainDumps?: number;
33
+ machineProofs?: number;
34
+ };
35
+ repoMirror?: {
36
+ repoFullName?: string | null;
37
+ commitSha?: string | null;
38
+ fileCount?: number;
39
+ totalBytes?: number;
40
+ truncated?: boolean;
41
+ syncedAt?: number;
42
+ } | null;
43
+ github?: {
44
+ repoFullName?: string | null;
45
+ lastSyncedAt?: number | null;
46
+ lastPushedCommitSha?: string | null;
47
+ mirrorStale?: boolean;
48
+ lastPushErrorAt?: number | null;
49
+ } | null;
50
+ encryptedEnvVault?: {
51
+ status?: "ready" | "missing" | "scope-missing" | string;
52
+ flow?: string;
53
+ available?: boolean;
54
+ snapshotCount?: number;
55
+ id?: string | null;
56
+ label?: string | null;
57
+ createdAt?: number | null;
58
+ fileName?: string | null;
59
+ contentType?: string | null;
60
+ encryptionTool?: string | null;
61
+ extension?: string | null;
62
+ formatVersion?: number | null;
63
+ sizeBytes?: number | null;
64
+ projectCount?: number | null;
65
+ variableCount?: number | null;
66
+ sha256?: string | null;
67
+ manifestSha256?: string | null;
68
+ agentAuthIncluded?: boolean;
69
+ sourceHost?: string | null;
70
+ sourceRoot?: string | null;
71
+ restoreMode?: string;
72
+ scope?: string;
73
+ secretValuesExposed?: false;
74
+ };
75
+ };
76
+ export declare const REALTIME_SYNC_STATUS_PATH: string;
77
+ export type RealtimeSecretVaultStatus = {
78
+ state: "ready" | "missing" | "scope-missing" | "unknown";
79
+ flow: "account-backed-encrypted-snapshot";
80
+ available: boolean;
81
+ snapshotCount: number;
82
+ summary: string;
83
+ latestSnapshot?: {
84
+ id?: string | null;
85
+ label?: string | null;
86
+ fileName?: string | null;
87
+ createdAt?: number | null;
88
+ sizeBytes?: number | null;
89
+ projectCount?: number | null;
90
+ variableCount?: number | null;
91
+ sha256?: string | null;
92
+ sha256Short?: string | null;
93
+ manifestSha256?: string | null;
94
+ encryptionTool?: string | null;
95
+ extension?: string | null;
96
+ formatVersion?: number | null;
97
+ agentAuthIncluded?: boolean;
98
+ sourceHost?: string | null;
99
+ sourceRoot?: string | null;
100
+ };
101
+ pullCommand: string;
102
+ restoreCommand: string;
103
+ secretValuesExposed: false;
104
+ };
105
+ export type RealtimeSyncStatusFile = {
106
+ schemaVersion: "you-md/realtime-sync-daemon-status/v1";
107
+ generatedAt: number;
108
+ summary: string;
109
+ user?: RealtimeSyncHead["user"];
110
+ identity?: RealtimeSyncHead["identity"];
111
+ skills?: {
112
+ installedCount?: number;
113
+ latestInstalledAt?: number | null;
114
+ };
115
+ portfolio?: RealtimeSyncHead["portfolio"];
116
+ repoMirror?: RealtimeSyncHead["repoMirror"];
117
+ github?: RealtimeSyncHead["github"];
118
+ secretVault: RealtimeSecretVaultStatus;
119
+ secretValuesExposed: false;
120
+ };
121
+ export declare function describeRealtimeSecretVault(head: RealtimeSyncHead | null | undefined): RealtimeSecretVaultStatus;
122
+ export declare function realtimeSyncHeadSignature(head: RealtimeSyncHead | null | undefined): string;
123
+ export declare function summarizeRealtimeSyncHead(head: RealtimeSyncHead): string;
124
+ export declare function buildRealtimeSyncStatusFile(head: RealtimeSyncHead): RealtimeSyncStatusFile;
125
+ export declare function writeRealtimeSyncStatusFile(head: RealtimeSyncHead): void;
126
+ export declare function readRealtimeSyncStatusFile(): RealtimeSyncStatusFile | null;
127
+ export declare function shouldRunBoundedSync(lastRunAt: number, now: number, minIntervalMs: number): boolean;
128
+ //# sourceMappingURL=realtime-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-sync.d.ts","sourceRoot":"","sources":["../../src/lib/realtime-sync.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,QAAQ,CAAC,EAAE;QACT,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACnC,CAAC;IACF,MAAM,CAAC,EAAE;QACP,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IACF,SAAS,CAAC,EAAE;QACV,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,IAAI,CAAC;IACT,MAAM,CAAC,EAAE;QACP,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpC,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACjC,GAAG,IAAI,CAAC;IACT,iBAAiB,CAAC,EAAE;QAClB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,eAAe,GAAG,MAAM,CAAC;QACxD,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,mBAAmB,CAAC,EAAE,KAAK,CAAC;KAC7B,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,yBAAyB,QAAiE,CAAC;AAExG,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAAC;IACzD,IAAI,EAAE,mCAAmC,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE;QACf,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,KAAK,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,EAAE,uCAAuC,CAAC;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACxE,SAAS,CAAC,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,CAAC,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACpC,WAAW,EAAE,yBAAyB,CAAC;IACvC,mBAAmB,EAAE,KAAK,CAAC;CAC5B,CAAC;AAOF,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAAG,yBAAyB,CAmFhH;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAgC3F;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CASxE;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,gBAAgB,GAAG,sBAAsB,CAiB1F;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAOxE;AAED,wBAAgB,0BAA0B,IAAI,sBAAsB,GAAG,IAAI,CAS1E;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GACpB,OAAO,CAET"}
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.REALTIME_SYNC_STATUS_PATH = void 0;
37
+ exports.describeRealtimeSecretVault = describeRealtimeSecretVault;
38
+ exports.realtimeSyncHeadSignature = realtimeSyncHeadSignature;
39
+ exports.summarizeRealtimeSyncHead = summarizeRealtimeSyncHead;
40
+ exports.buildRealtimeSyncStatusFile = buildRealtimeSyncStatusFile;
41
+ exports.writeRealtimeSyncStatusFile = writeRealtimeSyncStatusFile;
42
+ exports.readRealtimeSyncStatusFile = readRealtimeSyncStatusFile;
43
+ exports.shouldRunBoundedSync = shouldRunBoundedSync;
44
+ const fs = __importStar(require("fs"));
45
+ const os = __importStar(require("os"));
46
+ const path = __importStar(require("path"));
47
+ exports.REALTIME_SYNC_STATUS_PATH = path.join(os.homedir(), ".youmd", "realtime-sync-status.json");
48
+ function shortSha(value) {
49
+ if (!value)
50
+ return null;
51
+ return value.length <= 16 ? value : `${value.slice(0, 12)}...${value.slice(-8)}`;
52
+ }
53
+ function describeRealtimeSecretVault(head) {
54
+ const vault = head?.encryptedEnvVault;
55
+ const state = vault?.status === "ready"
56
+ ? "ready"
57
+ : vault?.status === "scope-missing" || vault?.scope === "vault-not-granted"
58
+ ? "scope-missing"
59
+ : vault
60
+ ? "missing"
61
+ : "unknown";
62
+ const pullCommand = "youmd env vault pull --out ~/.youmd/secret-vault";
63
+ const restoreCommand = "youmd env vault pull --restore --root ~/Desktop/CODE_YOU --map-existing --existing-only --skip-agent-auth";
64
+ if (state === "ready") {
65
+ const projects = vault?.projectCount ?? 0;
66
+ const vars = vault?.variableCount ?? 0;
67
+ const source = vault?.sourceHost ? ` from ${vault.sourceHost}` : "";
68
+ const hash = shortSha(vault?.sha256);
69
+ return {
70
+ state,
71
+ flow: "account-backed-encrypted-snapshot",
72
+ available: true,
73
+ snapshotCount: vault?.snapshotCount ?? 1,
74
+ summary: `Secret Vault ready: ${projects} projects / ${vars} vars${source}${hash ? ` / ${hash}` : ""}`,
75
+ latestSnapshot: {
76
+ id: vault?.id ?? null,
77
+ label: vault?.label ?? null,
78
+ fileName: vault?.fileName ?? null,
79
+ createdAt: vault?.createdAt ?? null,
80
+ sizeBytes: vault?.sizeBytes ?? null,
81
+ projectCount: vault?.projectCount ?? null,
82
+ variableCount: vault?.variableCount ?? null,
83
+ sha256: vault?.sha256 ?? null,
84
+ sha256Short: hash,
85
+ manifestSha256: vault?.manifestSha256 ?? null,
86
+ encryptionTool: vault?.encryptionTool ?? null,
87
+ extension: vault?.extension ?? null,
88
+ formatVersion: vault?.formatVersion ?? null,
89
+ agentAuthIncluded: vault?.agentAuthIncluded === true,
90
+ sourceHost: vault?.sourceHost ?? null,
91
+ sourceRoot: vault?.sourceRoot ?? null,
92
+ },
93
+ pullCommand,
94
+ restoreCommand,
95
+ secretValuesExposed: false,
96
+ };
97
+ }
98
+ if (state === "scope-missing") {
99
+ return {
100
+ state,
101
+ flow: "account-backed-encrypted-snapshot",
102
+ available: false,
103
+ snapshotCount: 0,
104
+ summary: "Secret Vault unknown: current realtime key lacks vault scope",
105
+ pullCommand,
106
+ restoreCommand,
107
+ secretValuesExposed: false,
108
+ };
109
+ }
110
+ if (state === "missing") {
111
+ return {
112
+ state,
113
+ flow: "account-backed-encrypted-snapshot",
114
+ available: false,
115
+ snapshotCount: vault?.snapshotCount ?? 0,
116
+ summary: "Secret Vault missing: upload encrypted snapshot from the source Mac with `youmd env vault push`",
117
+ pullCommand,
118
+ restoreCommand,
119
+ secretValuesExposed: false,
120
+ };
121
+ }
122
+ return {
123
+ state,
124
+ flow: "account-backed-encrypted-snapshot",
125
+ available: false,
126
+ snapshotCount: 0,
127
+ summary: "Secret Vault not observed yet: realtime daemon has not received a vault-scoped sync head",
128
+ pullCommand,
129
+ restoreCommand,
130
+ secretValuesExposed: false,
131
+ };
132
+ }
133
+ function realtimeSyncHeadSignature(head) {
134
+ if (!head)
135
+ return "none";
136
+ return JSON.stringify({
137
+ identity: {
138
+ latestVersion: head.identity?.latestVersion ?? null,
139
+ latestHash: head.identity?.latestHash ?? null,
140
+ publishedVersion: head.identity?.publishedVersion ?? null,
141
+ publishedHash: head.identity?.publishedHash ?? null,
142
+ },
143
+ skills: {
144
+ installedCount: head.skills?.installedCount ?? 0,
145
+ latestInstalledAt: head.skills?.latestInstalledAt ?? null,
146
+ names: head.skills?.names ?? [],
147
+ },
148
+ portfolio: head.portfolio ?? null,
149
+ repoMirror: head.repoMirror ?? null,
150
+ github: head.github ?? null,
151
+ encryptedEnvVault: {
152
+ status: head.encryptedEnvVault?.status ?? null,
153
+ available: head.encryptedEnvVault?.available ?? false,
154
+ snapshotCount: head.encryptedEnvVault?.snapshotCount ?? 0,
155
+ id: head.encryptedEnvVault?.id ?? null,
156
+ createdAt: head.encryptedEnvVault?.createdAt ?? null,
157
+ fileName: head.encryptedEnvVault?.fileName ?? null,
158
+ sha256: head.encryptedEnvVault?.sha256 ?? null,
159
+ manifestSha256: head.encryptedEnvVault?.manifestSha256 ?? null,
160
+ projectCount: head.encryptedEnvVault?.projectCount ?? null,
161
+ variableCount: head.encryptedEnvVault?.variableCount ?? null,
162
+ sourceHost: head.encryptedEnvVault?.sourceHost ?? null,
163
+ sourceRoot: head.encryptedEnvVault?.sourceRoot ?? null,
164
+ },
165
+ });
166
+ }
167
+ function summarizeRealtimeSyncHead(head) {
168
+ const bundle = head.identity?.latestVersion
169
+ ? `bundle v${head.identity.latestVersion}`
170
+ : "bundle unknown";
171
+ const skills = `${head.skills?.installedCount ?? 0} skills`;
172
+ const projects = `${head.portfolio?.projects ?? 0} projects`;
173
+ const tasks = `${head.portfolio?.tasks ?? 0} tasks`;
174
+ const vault = describeRealtimeSecretVault(head).summary;
175
+ return `${bundle}, ${skills}, ${projects}, ${tasks}, ${vault}`;
176
+ }
177
+ function buildRealtimeSyncStatusFile(head) {
178
+ return {
179
+ schemaVersion: "you-md/realtime-sync-daemon-status/v1",
180
+ generatedAt: Date.now(),
181
+ summary: summarizeRealtimeSyncHead(head),
182
+ user: head.user,
183
+ identity: head.identity,
184
+ skills: {
185
+ installedCount: head.skills?.installedCount,
186
+ latestInstalledAt: head.skills?.latestInstalledAt ?? null,
187
+ },
188
+ portfolio: head.portfolio,
189
+ repoMirror: head.repoMirror,
190
+ github: head.github,
191
+ secretVault: describeRealtimeSecretVault(head),
192
+ secretValuesExposed: false,
193
+ };
194
+ }
195
+ function writeRealtimeSyncStatusFile(head) {
196
+ fs.mkdirSync(path.dirname(exports.REALTIME_SYNC_STATUS_PATH), { recursive: true, mode: 0o700 });
197
+ fs.writeFileSync(exports.REALTIME_SYNC_STATUS_PATH, JSON.stringify(buildRealtimeSyncStatusFile(head), null, 2), { mode: 0o600 });
198
+ }
199
+ function readRealtimeSyncStatusFile() {
200
+ try {
201
+ const parsed = JSON.parse(fs.readFileSync(exports.REALTIME_SYNC_STATUS_PATH, "utf-8"));
202
+ if (!parsed || typeof parsed !== "object")
203
+ return null;
204
+ if (parsed.secretValuesExposed !== false)
205
+ return null;
206
+ return parsed;
207
+ }
208
+ catch {
209
+ return null;
210
+ }
211
+ }
212
+ function shouldRunBoundedSync(lastRunAt, now, minIntervalMs) {
213
+ return lastRunAt === 0 || now - lastRunAt >= minIntervalMs;
214
+ }
215
+ //# sourceMappingURL=realtime-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-sync.js","sourceRoot":"","sources":["../../src/lib/realtime-sync.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "youmd",
3
- "version": "0.8.2",
3
+ "version": "0.8.5",
4
4
  "description": "Identity context protocol for the agent internet — an MCP where the context is you. CLI for the You.md platform.",
5
5
  "bin": {
6
6
  "you": "dist/you.js",
@@ -45,6 +45,7 @@
45
45
  "@modelcontextprotocol/sdk": "^1.29.0",
46
46
  "chalk": "^4.1.2",
47
47
  "commander": "^12.1.0",
48
+ "convex": "1.33.1",
48
49
  "gray-matter": "^4.0.3",
49
50
  "jimp": "^0.22.12",
50
51
  "js-yaml": "^4.1.1",
@@ -58,6 +58,10 @@ Do NOT transfer via unencrypted email or public Slack.
58
58
  youmd env restore /path/to/env-vault-<date>.tar.<ext> --list
59
59
  youmd env restore /path/to/env-vault-<date>.tar.<ext>
60
60
 
61
+ # Fresh-machine restore after repos have been cloned into CODE_YOU:
62
+ youmd env restore /path/to/env-vault-<date>.tar.<ext> --root ~/Desktop/CODE_YOU --list --map-existing --existing-only --skip-agent-auth
63
+ youmd env restore /path/to/env-vault-<date>.tar.<ext> --root ~/Desktop/CODE_YOU --map-existing --existing-only --skip-agent-auth
64
+
61
65
  # If files already exist on the new Mac:
62
66
  youmd env restore --force /path/to/env-vault-<date>.tar.<ext>
63
67
 
@@ -70,6 +74,9 @@ The script:
70
74
  1. Lists vault contents without writing files when `--list` is provided.
71
75
  2. Creates `<CODE_2025>/<project>/.env.local` for each project during restore.
72
76
  3. Restores agent-auth files to their absolute home paths (`~/.codex/auth.json`, `~/.cursor/mcp.json`, `~/.claude.json`), creating parent directories as needed.
77
+ 4. With `--map-existing`, maps archived project folder names onto existing target-root dirs by normalized name, e.g. `foldermd` -> `folder-md`.
78
+ 5. With `--existing-only`, skips archived projects that do not already have a mapped target dir. This prevents fresh-machine restores from creating stale duplicate folders.
79
+ 6. With `--skip-agent-auth`, restores only project `.env.local` files and leaves the new machine's active Claude/Codex/Cursor auth files untouched.
73
80
 
74
81
  Existing files are backed up to `<path>.bak.<timestamp>` before overwrite (or silently overwritten with `--force`).
75
82
  List mode prints project env target paths, variable names/counts, and agent-auth file presence only. It never prints secret values.
@@ -115,6 +122,8 @@ youmd env restore <vault> # restore from an encrypted vault
115
122
  youmd env restore <vault> --list # list vault contents without writing
116
123
  youmd env restore --force # overwrite existing .env.local files
117
124
  youmd env restore --root <dir> # restore into a custom root
125
+ youmd env restore <vault> --root ~/Desktop/CODE_YOU --map-existing --existing-only --skip-agent-auth
126
+ # safe fresh-machine restore into cloned project dirs
118
127
  ```
119
128
 
120
129
  The CLI wrapper delegates directly to these bash scripts via `spawnSync` with
@@ -4,7 +4,8 @@
4
4
  # secret VALUES. Restore prints which file paths were restored or skipped; list
5
5
  # mode prints target paths plus variable names/counts only.
6
6
  #
7
- # Usage: bash restore.sh [--list] [--force] [--root <target-root>] <vault-file>
7
+ # Usage: bash restore.sh [--list] [--force] [--map-existing] [--existing-only]
8
+ # [--skip-agent-auth] [--root <target-root>] <vault-file>
8
9
  # vault-file: path to env-vault-*.tar.age / .tar.gpg / .tar.enc
9
10
  # --force: overwrite existing files without backing them up
10
11
  # --root: destination root for .env.local files
@@ -31,15 +32,21 @@ AGENT_AUTH_MAP=(
31
32
  TARGET_ROOT="${ENV_VAULT_RESTORE_ROOT:-/Users/houstongolden/Desktop/CODE_2025}"
32
33
  FORCE=false
33
34
  LIST_ONLY=false
35
+ MAP_EXISTING=false
36
+ EXISTING_ONLY=false
37
+ SKIP_AGENT_AUTH=false
34
38
  VAULT_FILE=""
35
39
 
36
40
  # ── parse args ─────────────────────────────────────────────────────────────────
37
41
  while [[ $# -gt 0 ]]; do
38
42
  case $1 in
39
- --list) LIST_ONLY=true; shift ;;
40
- --force) FORCE=true; shift ;;
41
- --root) TARGET_ROOT="$2"; shift 2 ;;
42
- -*) echo "Unknown flag: $1" >&2; exit 1 ;;
43
+ --list) LIST_ONLY=true; shift ;;
44
+ --force) FORCE=true; shift ;;
45
+ --map-existing) MAP_EXISTING=true; shift ;;
46
+ --existing-only) EXISTING_ONLY=true; shift ;;
47
+ --skip-agent-auth) SKIP_AGENT_AUTH=true; shift ;;
48
+ --root) TARGET_ROOT="$2"; shift 2 ;;
49
+ -*) echo "Unknown flag: $1" >&2; exit 1 ;;
43
50
  *)
44
51
  if [[ -z "${VAULT_FILE}" ]]; then
45
52
  VAULT_FILE="$1"
@@ -52,7 +59,7 @@ while [[ $# -gt 0 ]]; do
52
59
  done
53
60
 
54
61
  if [[ -z "${VAULT_FILE}" ]]; then
55
- echo "Usage: bash restore.sh [--list] [--force] [--root <path>] <vault-file>" >&2
62
+ echo "Usage: bash restore.sh [--list] [--force] [--map-existing] [--existing-only] [--skip-agent-auth] [--root <path>] <vault-file>" >&2
56
63
  echo " vault-file: env-vault-*.tar.age | .tar.gpg | .tar.enc" >&2
57
64
  exit 1
58
65
  fi
@@ -80,8 +87,53 @@ fi
80
87
  echo "Encryption tool: ${ENC_TOOL}"
81
88
  echo "Vault file: ${VAULT_FILE}"
82
89
  echo "Target root: ${TARGET_ROOT}"
90
+ echo "Project mapping: map-existing=${MAP_EXISTING} existing-only=${EXISTING_ONLY}"
91
+ echo "Agent auth: $([[ "${SKIP_AGENT_AUTH}" == "true" ]] && echo "skip" || echo "restore")"
83
92
  echo ""
84
93
 
94
+ normalize_project_name() {
95
+ printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]//g'
96
+ }
97
+
98
+ map_project_name() {
99
+ local archive_name="$1"
100
+ local exact_dir="${TARGET_ROOT}/${archive_name}"
101
+ if [[ "${MAP_EXISTING}" != "true" ]]; then
102
+ printf '%s\n' "${archive_name}"
103
+ return 0
104
+ fi
105
+ if [[ -d "${exact_dir}" ]]; then
106
+ printf '%s\n' "${archive_name}"
107
+ return 0
108
+ fi
109
+
110
+ local wanted
111
+ wanted="$(normalize_project_name "${archive_name}")"
112
+ local matches=()
113
+ local candidate
114
+ while IFS= read -r -d '' candidate; do
115
+ local base
116
+ base="$(basename "${candidate}")"
117
+ if [[ "$(normalize_project_name "${base}")" == "${wanted}" ]]; then
118
+ matches+=("${base}")
119
+ fi
120
+ done < <(find "${TARGET_ROOT}" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null || true)
121
+
122
+ if [[ "${#matches[@]}" -eq 1 ]]; then
123
+ printf '%s\n' "${matches[0]}"
124
+ return 0
125
+ fi
126
+ if [[ "${#matches[@]}" -gt 1 ]]; then
127
+ echo "WARN: multiple existing dirs match vault project '${archive_name}': ${matches[*]} — skipping" >&2
128
+ return 1
129
+ fi
130
+ if [[ "${EXISTING_ONLY}" == "true" ]]; then
131
+ echo "WARN: no existing dir for vault project '${archive_name}' under ${TARGET_ROOT} — skipping" >&2
132
+ return 1
133
+ fi
134
+ printf '%s\n' "${archive_name}"
135
+ }
136
+
85
137
  # ── decrypt into a temp dir ────────────────────────────────────────────────────
86
138
  WORK_DIR="$(mktemp -d)"
87
139
  TAR_PATH="${WORK_DIR}/vault.tar"
@@ -130,18 +182,30 @@ if [[ "${LIST_ONLY}" == "true" ]]; then
130
182
  echo "Vault contents (secret-safe inventory):"
131
183
  while IFS= read -r -d '' ENV_FILE; do
132
184
  REL_PATH="${ENV_FILE#${EXTRACT_DIR}/}"
133
- PROJECT_NAME="$(dirname "${REL_PATH}")"
185
+ ARCHIVE_PROJECT_NAME="$(dirname "${REL_PATH}")"
186
+ if ! PROJECT_NAME="$(map_project_name "${ARCHIVE_PROJECT_NAME}")"; then
187
+ echo " env: ${ARCHIVE_PROJECT_NAME}/.env.local -> SKIPPED (no safe target)"
188
+ (( ENV_COUNT++ )) || true
189
+ continue
190
+ fi
134
191
  DEST="${TARGET_ROOT}/${PROJECT_NAME}/.env.local"
135
192
  VAR_COUNT=$(grep -cE '^[A-Za-z_][A-Za-z0-9_]*=' "${ENV_FILE}" 2>/dev/null || true)
136
193
  VAR_NAMES=$(grep -oE '^[A-Za-z_][A-Za-z0-9_]*' "${ENV_FILE}" 2>/dev/null \
137
194
  | grep -v '^$' | sort | paste -sd ',' - || true)
138
- echo " env: ${PROJECT_NAME}/.env.local -> ${DEST} (${VAR_COUNT} variable(s) [${VAR_NAMES}])"
195
+ if [[ "${ARCHIVE_PROJECT_NAME}" == "${PROJECT_NAME}" ]]; then
196
+ echo " env: ${PROJECT_NAME}/.env.local -> ${DEST} (${VAR_COUNT} variable(s) [${VAR_NAMES}])"
197
+ else
198
+ echo " env: ${ARCHIVE_PROJECT_NAME}/.env.local -> ${DEST} (${VAR_COUNT} variable(s) [${VAR_NAMES}])"
199
+ fi
139
200
  (( ENV_COUNT++ )) || true
140
201
  done < <(find "${EXTRACT_DIR}" -name ".env.local" -print0)
141
202
 
142
203
  AGENT_AUTH_SRC="${EXTRACT_DIR}/agent-auth"
143
204
  echo " agent-auth:"
144
- for ENTRY in "${AGENT_AUTH_MAP[@]}"; do
205
+ if [[ "${SKIP_AGENT_AUTH}" == "true" ]]; then
206
+ echo " skipped by --skip-agent-auth"
207
+ else
208
+ for ENTRY in "${AGENT_AUTH_MAP[@]}"; do
145
209
  ARCHIVE_NAME="${ENTRY%%:*}"
146
210
  DEST_PATH="${ENTRY##*:}"
147
211
  SRC_FILE="${AGENT_AUTH_SRC}/${ARCHIVE_NAME}"
@@ -153,7 +217,8 @@ if [[ "${LIST_ONLY}" == "true" ]]; then
153
217
  echo " missing: agent-auth/${ARCHIVE_NAME}"
154
218
  (( AGENT_MISSING++ )) || true
155
219
  fi
156
- done
220
+ done
221
+ fi
157
222
 
158
223
  echo ""
159
224
  echo "List summary: ${ENV_COUNT} .env.local file(s), ${AGENT_PRESENT} agent-auth file(s) present, ${AGENT_MISSING} agent-auth file(s) missing."
@@ -169,7 +234,12 @@ BACKED_UP=0
169
234
  while IFS= read -r -d '' ENV_FILE; do
170
235
  # ENV_FILE is relative to EXTRACT_DIR, e.g. ./project-name/.env.local
171
236
  REL_PATH="${ENV_FILE#${EXTRACT_DIR}/}" # project-name/.env.local
172
- PROJECT_NAME="$(dirname "${REL_PATH}")" # project-name
237
+ ARCHIVE_PROJECT_NAME="$(dirname "${REL_PATH}")" # project-name
238
+ if ! PROJECT_NAME="$(map_project_name "${ARCHIVE_PROJECT_NAME}")"; then
239
+ echo " SKIPPED (no existing mapped target): ${ARCHIVE_PROJECT_NAME}/.env.local"
240
+ (( SKIPPED++ )) || true
241
+ continue
242
+ fi
173
243
  DEST="${TARGET_ROOT}/${PROJECT_NAME}/.env.local"
174
244
  DEST_DIR="$(dirname "${DEST}")"
175
245
 
@@ -204,7 +274,10 @@ AGENT_BACKED_UP=0
204
274
 
205
275
  AGENT_AUTH_SRC="${EXTRACT_DIR}/agent-auth"
206
276
 
207
- if [[ -d "${AGENT_AUTH_SRC}" ]]; then
277
+ if [[ "${SKIP_AGENT_AUTH}" == "true" ]]; then
278
+ echo "Skipping agent-auth restore because --skip-agent-auth was provided."
279
+ echo ""
280
+ elif [[ -d "${AGENT_AUTH_SRC}" ]]; then
208
281
  echo "Restoring agent-auth files…"
209
282
  for ENTRY in "${AGENT_AUTH_MAP[@]}"; do
210
283
  ARCHIVE_NAME="${ENTRY%%:*}"
@@ -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 --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.
31
+ - **Realtime identity/project plane:** `youmd sync --live --daemon` subscribes to Convex websocket updates, mints only a short-lived sync session token, materializes identity files and installed You.md skills locally, and triggers bounded shared stack/project-context syncs. It never prints or returns raw `.env.local` values.
32
+ - **Identity repair plane:** `youmd sync --daemon` (Convex ↔ local profile, memories, preferences). Automated via `com.youmd.identity-sync` daemon as a timer-based fallback. Daemon mode refreshes local files/skills and skips unsafe lossy pushes instead of forcing over richer server data.
32
33
  - **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.
33
34
  - **Env-vault plane:** `cli/scripts/env-vault/restore.sh` for secrets (manual, invoked on new machine); also available as `youmd env restore <vault>`.
34
35
 
@@ -41,6 +42,7 @@ cli/scripts/skillstack-sync/
41
42
  ├── sync.sh Core syncer (bash 3.2 compatible)
42
43
  ├── install-daemons.sh Installs all resident LaunchAgents
43
44
  ├── bootstrap-new-mac.sh New machine setup script
45
+ ├── com.youmd.realtime-sync.plist LaunchAgent: runs `youmd sync --live --daemon` as a resident websocket syncer
44
46
  ├── com.youmd.skillstack-sync.plist LaunchAgent: runs `youmd stack sync` every 5 min
45
47
  ├── com.youmd.identity-sync.plist LaunchAgent: runs `youmd sync --daemon` every 5 min
46
48
  ├── com.youmd.context-sync.plist LaunchAgent: runs `youmd stack context-sync` every 15 min
@@ -103,7 +105,8 @@ Or run the raw script directly:
103
105
  bash cli/scripts/skillstack-sync/install-daemons.sh
104
106
  ```
105
107
 
106
- Installs three LaunchAgents:
108
+ Installs four LaunchAgents:
109
+ - `com.youmd.realtime-sync` → runs `youmd sync --live --daemon`
107
110
  - `com.youmd.skillstack-sync` → runs `youmd stack sync`
108
111
  - `com.youmd.identity-sync` → runs `youmd sync --daemon`
109
112
  - `com.youmd.context-sync` → runs `youmd stack context-sync`
@@ -113,6 +116,7 @@ Check status:
113
116
  youmd stack daemon status
114
117
  # or directly:
115
118
  launchctl list com.youmd.skillstack-sync
119
+ launchctl list com.youmd.realtime-sync
116
120
  launchctl list com.youmd.identity-sync
117
121
  launchctl list com.youmd.context-sync
118
122
  ```
@@ -145,19 +149,33 @@ rm ~/Library/LaunchAgents/com.youmd.context-sync.plist
145
149
 
146
150
  ## New Mac setup
147
151
 
152
+ Preferred path: open the You.md Machine tab on an authenticated source computer,
153
+ copy the fresh-machine Claude/Codex setup prompt, and paste it into Claude Code
154
+ or Codex on the new Mac. That generated prompt checks/install Homebrew, Node 22,
155
+ GitHub CLI, bun, and pnpm; authenticates You.md; requires GitHub auth before
156
+ private clones; clones only setup-eligible active projects into
157
+ `~/Desktop/CODE_YOU`; restores env files with `--map-existing --existing-only
158
+ --skip-agent-auth`; and installs resident daemons.
159
+
160
+ Fallback manual path:
161
+
148
162
  ```bash
149
163
  # 1. Install youmd CLI
150
164
  curl -fsSL https://you.md/install.sh | bash
151
165
 
152
- # 2. Clone this repo (youmd)
166
+ # 2. Authenticate GitHub before private stack/project clones
167
+ gh auth login -h github.com -p https -s repo
168
+
169
+ # 3. Clone this repo (youmd)
153
170
  git clone https://github.com/houstongolden/youmd ~/Desktop/CODE_2025/youmd
154
171
 
155
- # 3. Run bootstrap (clones agent-shared + scistack)
172
+ # 4. Run bootstrap (clones agent-shared + scistack)
156
173
  bash ~/Desktop/CODE_2025/youmd/cli/scripts/skillstack-sync/bootstrap-new-mac.sh
157
174
 
158
- # 4. Restore secrets from env-vault (manual step — see env-vault/README)
175
+ # 5. Restore project env files from env-vault (manual step — see env-vault/README)
176
+ youmd env restore <vault> --root ~/Desktop/CODE_YOU --map-existing --existing-only --skip-agent-auth
159
177
 
160
- # 5. Activate daemons
178
+ # 6. Activate daemons
161
179
  youmd stack daemon install
162
180
  ```
163
181