youmd 0.4.9 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/dist/__tests__/api.test.d.ts +2 -0
  2. package/dist/__tests__/api.test.d.ts.map +1 -0
  3. package/dist/__tests__/api.test.js +84 -0
  4. package/dist/__tests__/api.test.js.map +1 -0
  5. package/dist/__tests__/compiler.test.d.ts +2 -0
  6. package/dist/__tests__/compiler.test.d.ts.map +1 -0
  7. package/dist/__tests__/compiler.test.js +127 -0
  8. package/dist/__tests__/compiler.test.js.map +1 -0
  9. package/dist/__tests__/config.test.d.ts +2 -0
  10. package/dist/__tests__/config.test.d.ts.map +1 -0
  11. package/dist/__tests__/config.test.js +79 -0
  12. package/dist/__tests__/config.test.js.map +1 -0
  13. package/dist/__tests__/decompile.test.d.ts +2 -0
  14. package/dist/__tests__/decompile.test.d.ts.map +1 -0
  15. package/dist/__tests__/decompile.test.js +102 -0
  16. package/dist/__tests__/decompile.test.js.map +1 -0
  17. package/dist/__tests__/hash.test.d.ts +2 -0
  18. package/dist/__tests__/hash.test.d.ts.map +1 -0
  19. package/dist/__tests__/hash.test.js +44 -0
  20. package/dist/__tests__/hash.test.js.map +1 -0
  21. package/dist/__tests__/integration.test.d.ts +2 -0
  22. package/dist/__tests__/integration.test.d.ts.map +1 -0
  23. package/dist/__tests__/integration.test.js +277 -0
  24. package/dist/__tests__/integration.test.js.map +1 -0
  25. package/dist/__tests__/skill-renderer.test.d.ts +2 -0
  26. package/dist/__tests__/skill-renderer.test.d.ts.map +1 -0
  27. package/dist/__tests__/skill-renderer.test.js +68 -0
  28. package/dist/__tests__/skill-renderer.test.js.map +1 -0
  29. package/dist/commands/add.d.ts.map +1 -1
  30. package/dist/commands/add.js +6 -3
  31. package/dist/commands/add.js.map +1 -1
  32. package/dist/commands/agents.d.ts +2 -0
  33. package/dist/commands/agents.d.ts.map +1 -0
  34. package/dist/commands/agents.js +93 -0
  35. package/dist/commands/agents.js.map +1 -0
  36. package/dist/commands/build.d.ts.map +1 -1
  37. package/dist/commands/build.js +96 -21
  38. package/dist/commands/build.js.map +1 -1
  39. package/dist/commands/chat.d.ts.map +1 -1
  40. package/dist/commands/chat.js +110 -11
  41. package/dist/commands/chat.js.map +1 -1
  42. package/dist/commands/diff.d.ts +1 -1
  43. package/dist/commands/diff.d.ts.map +1 -1
  44. package/dist/commands/diff.js +402 -16
  45. package/dist/commands/diff.js.map +1 -1
  46. package/dist/commands/export.js +1 -1
  47. package/dist/commands/export.js.map +1 -1
  48. package/dist/commands/init.d.ts +1 -0
  49. package/dist/commands/init.d.ts.map +1 -1
  50. package/dist/commands/init.js +138 -0
  51. package/dist/commands/init.js.map +1 -1
  52. package/dist/commands/link.d.ts +1 -0
  53. package/dist/commands/link.d.ts.map +1 -1
  54. package/dist/commands/link.js +77 -13
  55. package/dist/commands/link.js.map +1 -1
  56. package/dist/commands/login.d.ts.map +1 -1
  57. package/dist/commands/login.js +48 -27
  58. package/dist/commands/login.js.map +1 -1
  59. package/dist/commands/logs.d.ts +7 -0
  60. package/dist/commands/logs.d.ts.map +1 -0
  61. package/dist/commands/logs.js +115 -0
  62. package/dist/commands/logs.js.map +1 -0
  63. package/dist/commands/mcp.d.ts +6 -0
  64. package/dist/commands/mcp.d.ts.map +1 -0
  65. package/dist/commands/mcp.js +258 -0
  66. package/dist/commands/mcp.js.map +1 -0
  67. package/dist/commands/preview.d.ts.map +1 -1
  68. package/dist/commands/preview.js +191 -7
  69. package/dist/commands/preview.js.map +1 -1
  70. package/dist/commands/private.d.ts.map +1 -1
  71. package/dist/commands/private.js +248 -0
  72. package/dist/commands/private.js.map +1 -1
  73. package/dist/commands/prompts.d.ts +12 -0
  74. package/dist/commands/prompts.d.ts.map +1 -0
  75. package/dist/commands/prompts.js +245 -0
  76. package/dist/commands/prompts.js.map +1 -0
  77. package/dist/commands/publish.d.ts.map +1 -1
  78. package/dist/commands/publish.js +69 -6
  79. package/dist/commands/publish.js.map +1 -1
  80. package/dist/commands/pull.d.ts.map +1 -1
  81. package/dist/commands/pull.js +110 -137
  82. package/dist/commands/pull.js.map +1 -1
  83. package/dist/commands/push.d.ts +1 -0
  84. package/dist/commands/push.d.ts.map +1 -1
  85. package/dist/commands/push.js +236 -38
  86. package/dist/commands/push.js.map +1 -1
  87. package/dist/commands/register.d.ts.map +1 -1
  88. package/dist/commands/register.js +40 -84
  89. package/dist/commands/register.js.map +1 -1
  90. package/dist/commands/skill.d.ts +8 -0
  91. package/dist/commands/skill.d.ts.map +1 -0
  92. package/dist/commands/skill.js +1226 -0
  93. package/dist/commands/skill.js.map +1 -0
  94. package/dist/commands/status.d.ts.map +1 -1
  95. package/dist/commands/status.js +221 -69
  96. package/dist/commands/status.js.map +1 -1
  97. package/dist/commands/sync.d.ts.map +1 -1
  98. package/dist/commands/sync.js +12 -0
  99. package/dist/commands/sync.js.map +1 -1
  100. package/dist/commands/whoami.d.ts.map +1 -1
  101. package/dist/commands/whoami.js +62 -33
  102. package/dist/commands/whoami.js.map +1 -1
  103. package/dist/index.js +187 -6
  104. package/dist/index.js.map +1 -1
  105. package/dist/lib/api.d.ts +169 -12
  106. package/dist/lib/api.d.ts.map +1 -1
  107. package/dist/lib/api.js +183 -33
  108. package/dist/lib/api.js.map +1 -1
  109. package/dist/lib/ascii.d.ts.map +1 -1
  110. package/dist/lib/ascii.js +20 -48
  111. package/dist/lib/ascii.js.map +1 -1
  112. package/dist/lib/compiler.d.ts +16 -33
  113. package/dist/lib/compiler.d.ts.map +1 -1
  114. package/dist/lib/compiler.js +499 -84
  115. package/dist/lib/compiler.js.map +1 -1
  116. package/dist/lib/config.d.ts +27 -0
  117. package/dist/lib/config.d.ts.map +1 -1
  118. package/dist/lib/config.js +50 -0
  119. package/dist/lib/config.js.map +1 -1
  120. package/dist/lib/decompile.d.ts +21 -0
  121. package/dist/lib/decompile.d.ts.map +1 -0
  122. package/dist/lib/decompile.js +304 -0
  123. package/dist/lib/decompile.js.map +1 -0
  124. package/dist/lib/hash.d.ts +3 -0
  125. package/dist/lib/hash.d.ts.map +1 -0
  126. package/dist/lib/hash.js +31 -0
  127. package/dist/lib/hash.js.map +1 -0
  128. package/dist/lib/onboarding.d.ts +4 -4
  129. package/dist/lib/onboarding.d.ts.map +1 -1
  130. package/dist/lib/onboarding.js +228 -81
  131. package/dist/lib/onboarding.js.map +1 -1
  132. package/dist/lib/skill-catalog.d.ts +57 -0
  133. package/dist/lib/skill-catalog.d.ts.map +1 -0
  134. package/dist/lib/skill-catalog.js +245 -0
  135. package/dist/lib/skill-catalog.js.map +1 -0
  136. package/dist/lib/skill-renderer.d.ts +55 -0
  137. package/dist/lib/skill-renderer.d.ts.map +1 -0
  138. package/dist/lib/skill-renderer.js +382 -0
  139. package/dist/lib/skill-renderer.js.map +1 -0
  140. package/dist/lib/skills.d.ts +130 -0
  141. package/dist/lib/skills.d.ts.map +1 -0
  142. package/dist/lib/skills.js +876 -0
  143. package/dist/lib/skills.js.map +1 -0
  144. package/dist/lib/vault.d.ts +40 -0
  145. package/dist/lib/vault.d.ts.map +1 -0
  146. package/dist/lib/vault.js +187 -0
  147. package/dist/lib/vault.js.map +1 -0
  148. package/dist/mcp/server.d.ts +21 -0
  149. package/dist/mcp/server.d.ts.map +1 -0
  150. package/dist/mcp/server.js +1283 -0
  151. package/dist/mcp/server.js.map +1 -0
  152. package/examples/houston/directives/agent.md +13 -0
  153. package/examples/houston/preferences/agent.md +14 -0
  154. package/examples/houston/profile/about.md +15 -0
  155. package/examples/houston/profile/links.md +10 -0
  156. package/examples/houston/profile/projects.md +37 -0
  157. package/examples/houston/profile/values.md +9 -0
  158. package/examples/houston/voice/voice.md +13 -0
  159. package/examples/jordan/directives/agent.md +13 -0
  160. package/examples/jordan/preferences/agent.md +16 -0
  161. package/examples/jordan/profile/about.md +15 -0
  162. package/examples/jordan/profile/links.md +10 -0
  163. package/examples/jordan/profile/projects.md +29 -0
  164. package/examples/jordan/profile/values.md +9 -0
  165. package/examples/jordan/voice/voice.md +12 -0
  166. package/examples/priya/directives/agent.md +13 -0
  167. package/examples/priya/preferences/agent.md +15 -0
  168. package/examples/priya/profile/about.md +15 -0
  169. package/examples/priya/profile/links.md +9 -0
  170. package/examples/priya/profile/projects.md +28 -0
  171. package/examples/priya/profile/values.md +9 -0
  172. package/examples/priya/voice/voice.md +12 -0
  173. package/package.json +15 -6
  174. package/skills/claude-md-generator.md +91 -0
  175. package/skills/meta-improve.md +84 -0
  176. package/skills/proactive-context-fill.md +52 -0
  177. package/skills/project-context-init.md +77 -0
  178. package/skills/voice-sync.md +89 -0
  179. package/skills/you-logs.md +71 -0
@@ -1,4 +1,11 @@
1
1
  "use strict";
2
+ /**
3
+ * Bundle compiler — reads markdown files from profile/, preferences/, voice/,
4
+ * directives/ and compiles them into the nested server format (you-md/v1).
5
+ *
6
+ * Output matches convex/lib/compile.ts so the server stores it correctly
7
+ * and the web can render it without transformation.
8
+ */
2
9
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
10
  if (k2 === undefined) k2 = k;
4
11
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -36,67 +43,335 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
43
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
44
  };
38
45
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.readDirectory = readDirectory;
40
46
  exports.compileBundle = compileBundle;
41
47
  exports.writeBundle = writeBundle;
42
48
  const fs = __importStar(require("fs"));
43
49
  const path = __importStar(require("path"));
44
50
  const gray_matter_1 = __importDefault(require("gray-matter"));
51
+ const config_1 = require("./config");
52
+ // ─── Helpers ───────────────────────────────────────────────────────
45
53
  function simpleHash(content) {
46
54
  let hash = 0;
47
55
  for (let i = 0; i < content.length; i++) {
48
56
  const char = content.charCodeAt(i);
49
57
  hash = ((hash << 5) - hash) + char;
50
- hash = hash & hash; // Convert to 32bit integer
58
+ hash = hash & hash;
51
59
  }
52
60
  return Math.abs(hash).toString(16).padStart(8, "0");
53
61
  }
54
- function slugFromFilename(filename) {
55
- return path.basename(filename, ".md");
56
- }
57
- function titleFromSlug(slug) {
58
- return slug
59
- .split(/[-_]/)
60
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
61
- .join(" ");
62
+ function readDirectory(dirPath) {
63
+ if (!fs.existsSync(dirPath))
64
+ return [];
65
+ return fs.readdirSync(dirPath).filter((f) => f.endsWith(".md")).sort();
62
66
  }
63
67
  function readMarkdownFile(filePath) {
64
68
  const raw = fs.readFileSync(filePath, "utf-8");
65
69
  const { data, content } = (0, gray_matter_1.default)(raw);
66
- const slug = slugFromFilename(filePath);
67
- const title = data.title || titleFromSlug(slug);
68
- return {
69
- slug,
70
- title,
71
- content: content.trim(),
72
- metadata: data,
73
- };
70
+ const slug = path.basename(filePath, ".md");
71
+ const title = data.title || slug.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
72
+ return { slug, title, content: content.trim(), metadata: data };
74
73
  }
75
- function readDirectory(dirPath) {
76
- if (!fs.existsSync(dirPath)) {
77
- return [];
74
+ // ─── Section parsers ───────────────────────────────────────────────
75
+ function parseAboutMd(content) {
76
+ const lines = content.split("\n");
77
+ let name = "";
78
+ let tagline = "";
79
+ let location = "";
80
+ const bodyLines = [];
81
+ let foundName = false;
82
+ let seenNonEmpty = false;
83
+ for (const line of lines) {
84
+ const trimmed = line.trim();
85
+ if (trimmed.startsWith("# ")) {
86
+ name = trimmed.slice(2).trim();
87
+ foundName = true;
88
+ }
89
+ else if (trimmed.startsWith("*") && trimmed.endsWith("*") && !trimmed.startsWith("**")) {
90
+ location = trimmed.replace(/^\*|\*$/g, "").trim();
91
+ }
92
+ else if (!foundName && !trimmed) {
93
+ continue;
94
+ }
95
+ else if (foundName && !seenNonEmpty && !tagline && trimmed && !trimmed.startsWith("#") && !trimmed.startsWith("-") && !trimmed.startsWith("**")) {
96
+ // First non-empty line after name that isn't location → tagline candidate
97
+ // Accept taglines even if they end with period (common in real usage)
98
+ if (trimmed.length < 120) {
99
+ tagline = trimmed;
100
+ }
101
+ else {
102
+ bodyLines.push(trimmed);
103
+ }
104
+ seenNonEmpty = true;
105
+ }
106
+ else if (trimmed) {
107
+ bodyLines.push(trimmed);
108
+ seenNonEmpty = true;
109
+ }
110
+ }
111
+ const long = bodyLines.join("\n").trim();
112
+ const medium = long.split("\n").slice(0, 3).join("\n").trim();
113
+ const short = long.split(/\.\s/)[0]?.trim() || medium.split("\n")[0]?.trim() || "";
114
+ // Restore the period if we split on it
115
+ const shortWithPeriod = short && long.startsWith(short) && long[short.length] === "." ? short + "." : short;
116
+ return { name, tagline, location, bio: { short: shortWithPeriod, medium, long } };
117
+ }
118
+ function parseProjectsMd(content) {
119
+ const projects = [];
120
+ const sections = content.split(/^## /m).filter(Boolean);
121
+ for (const section of sections) {
122
+ const lines = section.split("\n");
123
+ const name = lines[0]?.trim() || "";
124
+ if (!name || name.startsWith("#"))
125
+ continue;
126
+ let role = "";
127
+ let status = "active";
128
+ let url = "";
129
+ const descLines = [];
130
+ for (const line of lines.slice(1)) {
131
+ const trimmed = line.trim();
132
+ if (trimmed.match(/^\*?\*?Role:?\*?\*?\s*/i)) {
133
+ role = trimmed.replace(/^\*?\*?Role:?\*?\*?\s*/i, "").trim();
134
+ }
135
+ else if (trimmed.match(/^\*?\*?Status:?\*?\*?\s*/i)) {
136
+ status = trimmed.replace(/^\*?\*?Status:?\*?\*?\s*/i, "").trim();
137
+ }
138
+ else if (trimmed.match(/^\*?\*?URL:?\*?\*?\s*/i)) {
139
+ url = trimmed.replace(/^\*?\*?URL:?\*?\*?\s*/i, "").trim();
140
+ }
141
+ else if (trimmed.match(/^- Role:\s*/i)) {
142
+ role = trimmed.replace(/^- Role:\s*/i, "").trim();
143
+ }
144
+ else if (trimmed.match(/^- Status:\s*/i)) {
145
+ status = trimmed.replace(/^- Status:\s*/i, "").trim();
146
+ }
147
+ else if (trimmed.match(/^- URL:\s*/i)) {
148
+ url = trimmed.replace(/^- URL:\s*/i, "").trim();
149
+ }
150
+ else if (trimmed) {
151
+ descLines.push(trimmed);
152
+ }
153
+ }
154
+ projects.push({
155
+ name,
156
+ role,
157
+ status,
158
+ url,
159
+ description: descLines.join("\n").trim(),
160
+ });
161
+ }
162
+ return projects;
163
+ }
164
+ function parseListMd(content) {
165
+ // Primary: bullet items (-, *, +)
166
+ const bullets = content.split("\n")
167
+ .map((l) => l.trim())
168
+ .filter((l) => /^[-*+]\s/.test(l))
169
+ .map((l) => l.replace(/^[-*+]\s+/, "").trim())
170
+ .filter(Boolean);
171
+ if (bullets.length > 0)
172
+ return bullets;
173
+ // Fallback: ## headings (common in values sections from onboarding)
174
+ const headings = content.split("\n")
175
+ .map((l) => l.trim())
176
+ .filter((l) => l.startsWith("## "))
177
+ .map((l) => l.slice(3).trim())
178
+ .filter(Boolean);
179
+ if (headings.length > 0)
180
+ return headings;
181
+ // Last resort: non-empty paragraphs that aren't headings or frontmatter
182
+ const paragraphs = content.split("\n")
183
+ .map((l) => l.trim())
184
+ .filter((l) => l && !l.startsWith("#") && !l.startsWith("---") && !l.startsWith("<!--") && !l.startsWith("("))
185
+ .map((l) => l.trim());
186
+ return paragraphs.length > 0 ? paragraphs : [];
187
+ }
188
+ function parseLinksMd(content) {
189
+ const links = {};
190
+ for (const line of content.split("\n")) {
191
+ const trimmed = line.trim();
192
+ // "- **platform**: url" or "- **platform:** url" or "- platform: url"
193
+ const boldMatch = trimmed.match(/^-\s+\*\*(.+?)\*\*:?\s+(.+)$/);
194
+ if (boldMatch) {
195
+ links[boldMatch[1].replace(/:$/, "").trim()] = boldMatch[2].trim();
196
+ continue;
197
+ }
198
+ const simpleMatch = trimmed.match(/^-\s+(.+?):\s+(.+)$/);
199
+ if (simpleMatch) {
200
+ links[simpleMatch[1].trim()] = simpleMatch[2].trim();
201
+ }
202
+ }
203
+ return links;
204
+ }
205
+ function parseAgentPrefsMd(content) {
206
+ let tone = "";
207
+ let formality = "casual-professional";
208
+ let avoid = [];
209
+ for (const line of content.split("\n")) {
210
+ const trimmed = line.trim();
211
+ if (trimmed.match(/^\*?\*?Tone:?\*?\*?\s*/i)) {
212
+ tone = trimmed.replace(/^\*?\*?Tone:?\*?\*?\s*/i, "").trim();
213
+ }
214
+ else if (trimmed.match(/^\*?\*?Formality:?\*?\*?\s*/i)) {
215
+ formality = trimmed.replace(/^\*?\*?Formality:?\*?\*?\s*/i, "").trim();
216
+ }
217
+ else if (trimmed.match(/^\*?\*?Avoid:?\*?\*?\s*/i)) {
218
+ const avoidStr = trimmed.replace(/^\*?\*?Avoid:?\*?\*?\s*/i, "").trim();
219
+ avoid = avoidStr.split(",").map((a) => a.trim()).filter(Boolean);
220
+ }
221
+ else if (trimmed.match(/^Tone:\s*/i)) {
222
+ tone = trimmed.replace(/^Tone:\s*/i, "").trim();
223
+ }
224
+ else if (trimmed.match(/^Formality:\s*/i)) {
225
+ formality = trimmed.replace(/^Formality:\s*/i, "").trim();
226
+ }
227
+ else if (trimmed.match(/^Avoid:\s*/i)) {
228
+ const avoidStr = trimmed.replace(/^Avoid:\s*/i, "").trim();
229
+ avoid = avoidStr.split(",").map((a) => a.trim()).filter(Boolean);
230
+ }
231
+ }
232
+ return { tone, formality, avoid };
233
+ }
234
+ function parseWritingPrefsMd(content) {
235
+ let style = "";
236
+ let format = "";
237
+ for (const line of content.split("\n")) {
238
+ const trimmed = line.trim();
239
+ if (trimmed.match(/^\*?\*?Style:?\*?\*?\s*/i)) {
240
+ style = trimmed.replace(/^\*?\*?Style:?\*?\*?\s*/i, "").trim();
241
+ }
242
+ else if (trimmed.match(/^\*?\*?Format:?\*?\*?\s*/i)) {
243
+ format = trimmed.replace(/^\*?\*?Format:?\*?\*?\s*/i, "").trim();
244
+ }
245
+ else if (trimmed.match(/^Style:\s*/i)) {
246
+ style = trimmed.replace(/^Style:\s*/i, "").trim();
247
+ }
248
+ else if (trimmed.match(/^Format:\s*/i)) {
249
+ format = trimmed.replace(/^Format:\s*/i, "").trim();
250
+ }
78
251
  }
79
- return fs
80
- .readdirSync(dirPath)
81
- .filter((f) => f.endsWith(".md"))
82
- .sort();
252
+ return { style, format };
83
253
  }
254
+ function parseVoiceMd(content) {
255
+ // Strip frontmatter heading, return the body
256
+ return content.replace(/^#\s+.+\n*/m, "").trim();
257
+ }
258
+ function parseDirectivesMd(content) {
259
+ let communication_style = "";
260
+ let negative_prompts = [];
261
+ let default_stack = "";
262
+ let decision_framework = "";
263
+ let current_goal = "";
264
+ const allLines = content.split("\n");
265
+ let inNeverBlock = false;
266
+ for (const line of allLines) {
267
+ const trimmed = line.trim();
268
+ if (trimmed.match(/^\*?\*?Communication Style:?\*?\*?\s*/i)) {
269
+ communication_style = trimmed.replace(/^\*?\*?Communication Style:?\*?\*?\s*/i, "").trim();
270
+ inNeverBlock = false;
271
+ }
272
+ else if (trimmed.match(/^\*?\*?Never:?\*?\*?\s*/i)) {
273
+ const inline = trimmed.replace(/^\*?\*?Never:?\*?\*?\s*/i, "").trim();
274
+ if (inline) {
275
+ // Inline format: "Never: don't ask permission. avoid jargon."
276
+ // Split on sentence boundaries but be careful with abbreviations
277
+ // Use comma-separation if commas present, otherwise sentence-split
278
+ if (inline.includes(",")) {
279
+ negative_prompts = inline.split(",").map((s) => s.trim().replace(/\.$/, "")).filter(Boolean);
280
+ }
281
+ else {
282
+ negative_prompts = inline.split(/(?<=[a-z])\.\s+/i).map((s) => s.trim().replace(/\.$/, "")).filter(Boolean);
283
+ }
284
+ }
285
+ inNeverBlock = true;
286
+ }
287
+ else if (trimmed.match(/^\*?\*?Default Stack:?\*?\*?\s*/i)) {
288
+ default_stack = trimmed.replace(/^\*?\*?Default Stack:?\*?\*?\s*/i, "").trim();
289
+ inNeverBlock = false;
290
+ }
291
+ else if (trimmed.match(/^\*?\*?Decision Framework:?\*?\*?\s*/i)) {
292
+ decision_framework = trimmed.replace(/^\*?\*?Decision Framework:?\*?\*?\s*/i, "").trim();
293
+ inNeverBlock = false;
294
+ }
295
+ else if (trimmed.match(/^\*?\*?Current Goal:?\*?\*?\s*/i)) {
296
+ current_goal = trimmed.replace(/^\*?\*?Current Goal:?\*?\*?\s*/i, "").trim();
297
+ inNeverBlock = false;
298
+ }
299
+ else if (/^[-*+]\s/.test(trimmed)) {
300
+ // List item — collect as negative prompt if we're in a Never block,
301
+ // otherwise as a generic directive
302
+ const item = trimmed.replace(/^[-*+]\s+/, "").trim();
303
+ if (item) {
304
+ if (inNeverBlock) {
305
+ negative_prompts.push(item);
306
+ }
307
+ else if (!communication_style && !default_stack) {
308
+ // Unlabeled list items before any labeled field → generic directives
309
+ // Treat as communication style hints
310
+ negative_prompts.push(item);
311
+ }
312
+ }
313
+ }
314
+ else if (trimmed && !trimmed.startsWith("#")) {
315
+ // Non-labeled, non-list line — if we're in a never block, treat as continuation
316
+ if (inNeverBlock && trimmed) {
317
+ negative_prompts.push(trimmed);
318
+ }
319
+ // A labeled field resets the never block
320
+ if (trimmed.includes(":")) {
321
+ inNeverBlock = false;
322
+ }
323
+ }
324
+ }
325
+ return { communication_style, negative_prompts, default_stack, decision_framework, current_goal };
326
+ }
327
+ // ─── Main compilation ──────────────────────────────────────────────
84
328
  function compileBundle(bundleDir) {
85
329
  const profileDir = path.join(bundleDir, "profile");
86
330
  const preferencesDir = path.join(bundleDir, "preferences");
331
+ const voiceDir = path.join(bundleDir, "voice");
332
+ const directivesDir = path.join(bundleDir, "directives");
87
333
  const profileFiles = readDirectory(profileDir);
88
334
  const preferenceFiles = readDirectory(preferencesDir);
335
+ const voiceFiles = readDirectory(voiceDir);
336
+ const directiveFiles = readDirectory(directivesDir);
89
337
  const filesRead = [];
90
- // Read profile sections
338
+ // Read all files
91
339
  const profileSections = profileFiles.map((file) => {
92
340
  filesRead.push({ type: "profile", file });
93
341
  return readMarkdownFile(path.join(profileDir, file));
94
342
  });
95
- // Read preference sections
96
- const preferenceSections = preferenceFiles.map((file) => {
343
+ const prefSections = preferenceFiles.map((file) => {
97
344
  filesRead.push({ type: "preference", file });
98
345
  return readMarkdownFile(path.join(preferencesDir, file));
99
346
  });
347
+ const voiceSections = voiceFiles.map((file) => {
348
+ filesRead.push({ type: "voice", file });
349
+ return readMarkdownFile(path.join(voiceDir, file));
350
+ });
351
+ const directiveSections = directiveFiles.map((file) => {
352
+ filesRead.push({ type: "directive", file });
353
+ return readMarkdownFile(path.join(directivesDir, file));
354
+ });
355
+ // Load existing skeleton from you.json or base.json to preserve fields we don't model
356
+ let skeleton = {};
357
+ const youJsonPath = path.join(bundleDir, "you.json");
358
+ const baseJsonPath = path.join(bundleDir, "base.json");
359
+ if (fs.existsSync(youJsonPath)) {
360
+ try {
361
+ const existing = JSON.parse(fs.readFileSync(youJsonPath, "utf-8"));
362
+ // Only use as skeleton if it's nested format (has identity or schema)
363
+ if (existing.identity || existing.schema === "you-md/v1") {
364
+ skeleton = existing;
365
+ }
366
+ }
367
+ catch { /* ignore corrupt */ }
368
+ }
369
+ if (Object.keys(skeleton).length === 0 && fs.existsSync(baseJsonPath)) {
370
+ try {
371
+ skeleton = JSON.parse(fs.readFileSync(baseJsonPath, "utf-8"));
372
+ }
373
+ catch { /* ignore */ }
374
+ }
100
375
  // Determine version
101
376
  const manifestPath = path.join(bundleDir, "manifest.json");
102
377
  let version = 1;
@@ -105,74 +380,214 @@ function compileBundle(bundleDir) {
105
380
  const existingManifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
106
381
  version = (existingManifest.version || 0) + 1;
107
382
  }
383
+ catch { /* version 1 */ }
384
+ }
385
+ const now = new Date().toISOString();
386
+ // Read username from skeleton, falling back to global config
387
+ let username = skeleton.username || "";
388
+ if (!username) {
389
+ try {
390
+ const globalConfig = (0, config_1.readGlobalConfig)();
391
+ username = globalConfig.username || "";
392
+ }
108
393
  catch {
109
- // Start at version 1 if manifest is corrupted
394
+ // config not available
110
395
  }
111
396
  }
112
- const generatedAt = new Date().toISOString();
113
- // Build you.json bundle
114
- const bundle = {
115
- version,
116
- generatedAt,
117
- profile: profileSections,
118
- preferences: preferenceSections,
397
+ // ── Parse profile files ────────────────────────────────────────
398
+ const aboutSection = profileSections.find((s) => s.slug === "about");
399
+ const projectsSection = profileSections.find((s) => s.slug === "projects");
400
+ const nowSection = profileSections.find((s) => s.slug === "now");
401
+ const valuesSection = profileSections.find((s) => s.slug === "values");
402
+ const linksSection = profileSections.find((s) => s.slug === "links");
403
+ const about = aboutSection ? parseAboutMd(aboutSection.content) : { name: "", tagline: "", location: "", bio: { short: "", medium: "", long: "" } };
404
+ const projects = projectsSection ? parseProjectsMd(projectsSection.content) : [];
405
+ const nowItems = nowSection ? parseListMd(nowSection.content) : [];
406
+ const values = valuesSection ? parseListMd(valuesSection.content) : [];
407
+ const links = linksSection ? parseLinksMd(linksSection.content) : {};
408
+ // Collect custom sections (any profile file not in the standard set)
409
+ const standardSlugs = new Set(["about", "now", "projects", "values", "links", "skills", "experience"]);
410
+ const customSections = profileSections
411
+ .filter((s) => !standardSlugs.has(s.slug))
412
+ .map((s) => ({ id: s.slug, title: s.title, content: s.content }));
413
+ // ── Parse preferences ──────────────────────────────────────────
414
+ const agentPrefSection = prefSections.find((s) => s.slug === "agent");
415
+ const writingPrefSection = prefSections.find((s) => s.slug === "writing");
416
+ const agentPrefs = agentPrefSection ? parseAgentPrefsMd(agentPrefSection.content) : { tone: "", formality: "casual-professional", avoid: [] };
417
+ const writingPrefs = writingPrefSection ? parseWritingPrefsMd(writingPrefSection.content) : { style: "", format: "" };
418
+ // ── Parse voice ────────────────────────────────────────────────
419
+ const voiceOverall = voiceSections.find((s) => s.slug === "voice");
420
+ const voicePlatforms = {};
421
+ for (const v of voiceSections) {
422
+ if (v.slug.startsWith("voice.")) {
423
+ const platform = v.slug.slice("voice.".length);
424
+ voicePlatforms[platform] = parseVoiceMd(v.content);
425
+ }
426
+ }
427
+ // ── Parse directives ───────────────────────────────────────────
428
+ const agentDirective = directiveSections.find((s) => s.slug === "agent");
429
+ const directives = agentDirective ? parseDirectivesMd(agentDirective.content) : {
430
+ communication_style: "", negative_prompts: [], default_stack: "", decision_framework: "", current_goal: ""
119
431
  };
120
- // Build you.md composite markdown
121
- const markdownParts = [];
122
- markdownParts.push("# You.md Identity Bundle");
123
- markdownParts.push("");
124
- markdownParts.push(`> Generated at ${generatedAt} (v${version})`);
125
- markdownParts.push("");
126
- if (profileSections.length > 0) {
127
- markdownParts.push("## Profile");
128
- markdownParts.push("");
129
- for (const section of profileSections) {
130
- markdownParts.push(`### ${section.title}`);
131
- markdownParts.push("");
132
- markdownParts.push(section.content);
133
- markdownParts.push("");
134
- }
135
- }
136
- if (preferenceSections.length > 0) {
137
- markdownParts.push("## Preferences");
138
- markdownParts.push("");
139
- for (const section of preferenceSections) {
140
- markdownParts.push(`### ${section.title}`);
141
- markdownParts.push("");
142
- markdownParts.push(section.content);
143
- markdownParts.push("");
144
- }
145
- }
146
- const markdown = markdownParts.join("\n").trimEnd() + "\n";
147
- // Build manifest
148
- const manifestEntries = [];
149
- for (const file of profileFiles) {
150
- const content = fs.readFileSync(path.join(profileDir, file), "utf-8");
151
- manifestEntries.push({
152
- file: `profile/${file}`,
153
- type: "profile",
154
- slug: slugFromFilename(file),
155
- hash: simpleHash(content),
156
- });
432
+ // ── Build youJson (nested server format) ───────────────────────
433
+ const youJson = {
434
+ schema: "you-md/v1",
435
+ username,
436
+ generated_at: now,
437
+ identity: {
438
+ name: about.name || skeleton?.identity?.name || "",
439
+ tagline: about.tagline || skeleton?.identity?.tagline || "",
440
+ location: about.location || skeleton?.identity?.location || "",
441
+ bio: {
442
+ short: about.bio.short || skeleton?.identity?.bio?.short || "",
443
+ medium: about.bio.medium || skeleton?.identity?.bio?.medium || "",
444
+ long: about.bio.long || skeleton?.identity?.bio?.long || "",
445
+ },
446
+ },
447
+ now: {
448
+ focus: nowItems.length > 0 ? nowItems : (skeleton?.now?.focus || []),
449
+ updated_at: now.split("T")[0],
450
+ },
451
+ projects: projects.length > 0 ? projects : (skeleton?.projects || []),
452
+ values: values.length > 0 ? values : (skeleton?.values || []),
453
+ links: Object.keys(links).length > 0 ? links : (skeleton?.links || {}),
454
+ preferences: {
455
+ agent: {
456
+ tone: agentPrefs.tone || skeleton?.preferences?.agent?.tone || "",
457
+ formality: agentPrefs.formality || skeleton?.preferences?.agent?.formality || "casual-professional",
458
+ avoid: agentPrefs.avoid.length > 0 ? agentPrefs.avoid : (skeleton?.preferences?.agent?.avoid || []),
459
+ },
460
+ writing: {
461
+ style: writingPrefs.style || skeleton?.preferences?.writing?.style || "",
462
+ format: writingPrefs.format || skeleton?.preferences?.writing?.format || "",
463
+ },
464
+ },
465
+ voice: {
466
+ overall: voiceOverall ? parseVoiceMd(voiceOverall.content) : (skeleton?.voice?.overall || ""),
467
+ platforms: {
468
+ linkedin: voicePlatforms.linkedin || skeleton?.voice?.platforms?.linkedin || null,
469
+ x: voicePlatforms.x || skeleton?.voice?.platforms?.x || null,
470
+ blog: voicePlatforms.blog || skeleton?.voice?.platforms?.blog || null,
471
+ ...(Object.fromEntries(Object.entries(voicePlatforms).filter(([k]) => !["linkedin", "x", "blog"].includes(k)))),
472
+ },
473
+ },
474
+ analysis: skeleton?.analysis || {
475
+ topics: [],
476
+ voice_summary: voiceOverall ? parseVoiceMd(voiceOverall.content) : "",
477
+ credibility_signals: [],
478
+ },
479
+ social_images: skeleton?.social_images || {},
480
+ agent_directives: {
481
+ communication_style: directives.communication_style || skeleton?.agent_directives?.communication_style || "",
482
+ negative_prompts: directives.negative_prompts.length > 0 ? directives.negative_prompts : (skeleton?.agent_directives?.negative_prompts || []),
483
+ default_stack: directives.default_stack || skeleton?.agent_directives?.default_stack || "",
484
+ decision_framework: directives.decision_framework || skeleton?.agent_directives?.decision_framework || "",
485
+ current_goal: directives.current_goal || skeleton?.agent_directives?.current_goal || "",
486
+ },
487
+ agent_guide: skeleton?.agent_guide || {
488
+ summary: "this is a you-md/v1 identity context protocol. use it to understand who this person is before working with them.",
489
+ quick_context: [
490
+ "identity.bio.short -- one-line summary",
491
+ "now.focus -- what they're working on right now",
492
+ "agent_directives -- behavioral instructions for how to interact",
493
+ "preferences.agent -- communication tone preferences",
494
+ "projects -- their active projects with context",
495
+ "voice.overall -- their communication style",
496
+ ],
497
+ for_writing: "check preferences.writing and voice.platforms for platform-specific style",
498
+ for_coding: "check projects for tech stack context, agent_directives.default_stack for preferred stack",
499
+ for_research: "check analysis.topics and links for their areas of expertise",
500
+ },
501
+ custom_sections: customSections.length > 0 ? customSections : (skeleton?.custom_sections || []),
502
+ meta: {
503
+ sources_used: skeleton?.meta?.sources_used || [],
504
+ last_updated: now,
505
+ compiler_version: "0.5.0",
506
+ },
507
+ verification: skeleton?.verification || null,
508
+ };
509
+ // ── Build you.md ───────────────────────────────────────────────
510
+ const mdParts = [];
511
+ mdParts.push(`---\nschema: you-md/v1\nname: ${about.name || username}\nusername: ${username}\ngenerated_at: ${now}\n---`);
512
+ mdParts.push(`\n# ${about.name || username}`);
513
+ if (about.tagline)
514
+ mdParts.push(about.tagline);
515
+ if (about.location)
516
+ mdParts.push(`*${about.location}*`);
517
+ if (about.bio.long)
518
+ mdParts.push(`\n## About\n\n${about.bio.long}`);
519
+ if (nowItems.length > 0)
520
+ mdParts.push(`\n## Now\n\n${nowItems.map((f) => `- ${f}`).join("\n")}`);
521
+ if (projects.length > 0) {
522
+ const pLines = projects.map((p) => `- **${p.name}**${p.description ? ` -- ${p.description}` : ""}${p.role ? ` (${p.role})` : ""}`);
523
+ mdParts.push(`\n## Projects\n\n${pLines.join("\n")}`);
157
524
  }
158
- for (const file of preferenceFiles) {
159
- const content = fs.readFileSync(path.join(preferencesDir, file), "utf-8");
160
- manifestEntries.push({
161
- file: `preferences/${file}`,
162
- type: "preference",
163
- slug: slugFromFilename(file),
164
- hash: simpleHash(content),
165
- });
525
+ if (values.length > 0)
526
+ mdParts.push(`\n## Values\n\n${values.map((v) => `- ${v}`).join("\n")}`);
527
+ if (agentPrefs.tone) {
528
+ const prefLines = [];
529
+ prefLines.push(`Tone: ${agentPrefs.tone}`);
530
+ if (agentPrefs.avoid.length > 0)
531
+ prefLines.push(`Avoid: ${agentPrefs.avoid.join(", ")}`);
532
+ mdParts.push(`\n## Agent Preferences\n\n${prefLines.join("\n")}`);
533
+ }
534
+ const linkEntries = Object.entries(links).filter(([, url]) => url);
535
+ if (linkEntries.length > 0)
536
+ mdParts.push(`\n## Links\n\n${linkEntries.map(([p, u]) => `- ${p}: ${u}`).join("\n")}`);
537
+ if (voiceOverall)
538
+ mdParts.push(`\n## Voice\n\n${parseVoiceMd(voiceOverall.content)}`);
539
+ for (const cs of customSections) {
540
+ mdParts.push(`\n## ${cs.title}\n\n${cs.content}`);
541
+ }
542
+ mdParts.push(`\n---\n\n> **For agents**: this is a you-md/v1 identity context protocol.\n> Quick context: check identity.bio.short, now.focus, and preferences.agent.\n> For writing help: check voice section and preferences.writing.\n> Full structured data: see you.json.`);
543
+ const markdown = mdParts.join("\n") + "\n";
544
+ // ── Build manifest ─────────────────────────────────────────────
545
+ const manifestEntries = [];
546
+ const allDirs = [
547
+ [profileDir, "profile", profileFiles],
548
+ [preferencesDir, "preference", preferenceFiles],
549
+ [voiceDir, "voice", voiceFiles],
550
+ [directivesDir, "directive", directiveFiles],
551
+ ];
552
+ for (const [dir, type, files] of allDirs) {
553
+ for (const file of files) {
554
+ const content = fs.readFileSync(path.join(dir, file), "utf-8");
555
+ const dirName = path.basename(dir);
556
+ manifestEntries.push({
557
+ file: `${dirName}/${file}`,
558
+ type,
559
+ slug: path.basename(file, ".md"),
560
+ hash: simpleHash(content),
561
+ });
562
+ }
166
563
  }
167
564
  const manifest = {
168
565
  version,
169
- generatedAt,
566
+ generatedAt: now,
170
567
  entries: manifestEntries,
171
568
  };
172
- return { bundle, markdown, manifest, filesRead };
569
+ // Count filled sections
570
+ const allSections = [...profileSections, ...prefSections, ...voiceSections, ...directiveSections];
571
+ const filledSections = allSections.filter((s) => s.content.split("\n").filter((l) => l.trim() && !l.startsWith("<!--") && !l.startsWith("(")).length > 0).length;
572
+ const directories = ["profile", "preferences", "voice", "directives"].filter((d) => {
573
+ const dir = path.join(bundleDir, d);
574
+ return fs.existsSync(dir) && readDirectory(dir).length > 0;
575
+ });
576
+ return {
577
+ youJson,
578
+ markdown,
579
+ manifest,
580
+ filesRead,
581
+ stats: {
582
+ version,
583
+ totalSections: allSections.length,
584
+ filledSections,
585
+ directories,
586
+ },
587
+ };
173
588
  }
174
589
  function writeBundle(bundleDir, result) {
175
- fs.writeFileSync(path.join(bundleDir, "you.json"), JSON.stringify(result.bundle, null, 2) + "\n");
590
+ fs.writeFileSync(path.join(bundleDir, "you.json"), JSON.stringify(result.youJson, null, 2) + "\n");
176
591
  fs.writeFileSync(path.join(bundleDir, "you.md"), result.markdown);
177
592
  fs.writeFileSync(path.join(bundleDir, "manifest.json"), JSON.stringify(result.manifest, null, 2) + "\n");
178
593
  }