youmd 0.6.21 → 0.6.27

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 (218) hide show
  1. package/dist/commands/build.d.ts.map +1 -1
  2. package/dist/commands/build.js +0 -1
  3. package/dist/commands/build.js.map +1 -1
  4. package/dist/commands/chat.d.ts.map +1 -1
  5. package/dist/commands/chat.js +283 -184
  6. package/dist/commands/chat.js.map +1 -1
  7. package/dist/commands/export.d.ts +1 -0
  8. package/dist/commands/export.d.ts.map +1 -1
  9. package/dist/commands/export.js +5 -0
  10. package/dist/commands/export.js.map +1 -1
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +21 -1
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/commands/keys.d.ts.map +1 -1
  15. package/dist/commands/keys.js +3 -3
  16. package/dist/commands/keys.js.map +1 -1
  17. package/dist/commands/link.d.ts.map +1 -1
  18. package/dist/commands/link.js +3 -3
  19. package/dist/commands/link.js.map +1 -1
  20. package/dist/commands/login.d.ts +29 -0
  21. package/dist/commands/login.d.ts.map +1 -1
  22. package/dist/commands/login.js +256 -127
  23. package/dist/commands/login.js.map +1 -1
  24. package/dist/commands/logs.js +0 -1
  25. package/dist/commands/logs.js.map +1 -1
  26. package/dist/commands/mcp.d.ts +14 -0
  27. package/dist/commands/mcp.d.ts.map +1 -1
  28. package/dist/commands/mcp.js +284 -28
  29. package/dist/commands/mcp.js.map +1 -1
  30. package/dist/commands/okf.d.ts +12 -0
  31. package/dist/commands/okf.d.ts.map +1 -0
  32. package/dist/commands/okf.js +411 -0
  33. package/dist/commands/okf.js.map +1 -0
  34. package/dist/commands/private.d.ts.map +1 -1
  35. package/dist/commands/private.js +6 -5
  36. package/dist/commands/private.js.map +1 -1
  37. package/dist/commands/project.d.ts.map +1 -1
  38. package/dist/commands/project.js +9 -5
  39. package/dist/commands/project.js.map +1 -1
  40. package/dist/commands/publish.d.ts.map +1 -1
  41. package/dist/commands/publish.js +0 -1
  42. package/dist/commands/publish.js.map +1 -1
  43. package/dist/commands/pull.d.ts +24 -1
  44. package/dist/commands/pull.d.ts.map +1 -1
  45. package/dist/commands/pull.js +114 -14
  46. package/dist/commands/pull.js.map +1 -1
  47. package/dist/commands/push.d.ts +1 -0
  48. package/dist/commands/push.d.ts.map +1 -1
  49. package/dist/commands/push.js +33 -13
  50. package/dist/commands/push.js.map +1 -1
  51. package/dist/commands/register.d.ts +6 -0
  52. package/dist/commands/register.d.ts.map +1 -1
  53. package/dist/commands/register.js +179 -79
  54. package/dist/commands/register.js.map +1 -1
  55. package/dist/commands/skill.d.ts.map +1 -1
  56. package/dist/commands/skill.js +121 -50
  57. package/dist/commands/skill.js.map +1 -1
  58. package/dist/commands/stack.d.ts +16 -0
  59. package/dist/commands/stack.d.ts.map +1 -0
  60. package/dist/commands/stack.js +863 -0
  61. package/dist/commands/stack.js.map +1 -0
  62. package/dist/commands/status.d.ts.map +1 -1
  63. package/dist/commands/status.js +25 -5
  64. package/dist/commands/status.js.map +1 -1
  65. package/dist/commands/sync.d.ts +23 -0
  66. package/dist/commands/sync.d.ts.map +1 -1
  67. package/dist/commands/sync.js +193 -12
  68. package/dist/commands/sync.js.map +1 -1
  69. package/dist/commands/whoami.d.ts.map +1 -1
  70. package/dist/commands/whoami.js +21 -16
  71. package/dist/commands/whoami.js.map +1 -1
  72. package/dist/create.js +0 -0
  73. package/dist/index.js +64 -7
  74. package/dist/index.js.map +1 -1
  75. package/dist/lib/api.d.ts +127 -5
  76. package/dist/lib/api.d.ts.map +1 -1
  77. package/dist/lib/api.js +142 -5
  78. package/dist/lib/api.js.map +1 -1
  79. package/dist/lib/ascii.d.ts +1 -0
  80. package/dist/lib/ascii.d.ts.map +1 -1
  81. package/dist/lib/ascii.js +9 -26
  82. package/dist/lib/ascii.js.map +1 -1
  83. package/dist/lib/canonical-json.d.ts +8 -0
  84. package/dist/lib/canonical-json.d.ts.map +1 -0
  85. package/dist/lib/canonical-json.js +29 -0
  86. package/dist/lib/canonical-json.js.map +1 -0
  87. package/dist/lib/capability-router.d.ts +58 -0
  88. package/dist/lib/capability-router.d.ts.map +1 -0
  89. package/dist/lib/capability-router.js +54 -0
  90. package/dist/lib/capability-router.js.map +1 -0
  91. package/dist/lib/compiler.d.ts +32 -0
  92. package/dist/lib/compiler.d.ts.map +1 -1
  93. package/dist/lib/compiler.js +150 -0
  94. package/dist/lib/compiler.js.map +1 -1
  95. package/dist/lib/config.d.ts +71 -35
  96. package/dist/lib/config.d.ts.map +1 -1
  97. package/dist/lib/config.js +334 -124
  98. package/dist/lib/config.js.map +1 -1
  99. package/dist/lib/decompile.d.ts +6 -1
  100. package/dist/lib/decompile.d.ts.map +1 -1
  101. package/dist/lib/decompile.js +120 -11
  102. package/dist/lib/decompile.js.map +1 -1
  103. package/dist/lib/hash.d.ts.map +1 -1
  104. package/dist/lib/hash.js +2 -19
  105. package/dist/lib/hash.js.map +1 -1
  106. package/dist/lib/heartbeat.d.ts +38 -0
  107. package/dist/lib/heartbeat.d.ts.map +1 -0
  108. package/dist/lib/heartbeat.js +70 -0
  109. package/dist/lib/heartbeat.js.map +1 -0
  110. package/dist/lib/host-link.d.ts +123 -0
  111. package/dist/lib/host-link.d.ts.map +1 -0
  112. package/dist/lib/host-link.js +350 -0
  113. package/dist/lib/host-link.js.map +1 -0
  114. package/dist/lib/merge.d.ts +50 -0
  115. package/dist/lib/merge.d.ts.map +1 -0
  116. package/dist/lib/merge.js +167 -0
  117. package/dist/lib/merge.js.map +1 -0
  118. package/dist/lib/okf-bundle.d.ts +104 -0
  119. package/dist/lib/okf-bundle.d.ts.map +1 -0
  120. package/dist/lib/okf-bundle.js +473 -0
  121. package/dist/lib/okf-bundle.js.map +1 -0
  122. package/dist/lib/okf-health.d.ts +40 -0
  123. package/dist/lib/okf-health.d.ts.map +1 -0
  124. package/dist/lib/okf-health.js +152 -0
  125. package/dist/lib/okf-health.js.map +1 -0
  126. package/dist/lib/okf-stack.d.ts +47 -0
  127. package/dist/lib/okf-stack.d.ts.map +1 -0
  128. package/dist/lib/okf-stack.js +331 -0
  129. package/dist/lib/okf-stack.js.map +1 -0
  130. package/dist/lib/okf-view.d.ts +49 -0
  131. package/dist/lib/okf-view.d.ts.map +1 -0
  132. package/dist/lib/okf-view.js +197 -0
  133. package/dist/lib/okf-view.js.map +1 -0
  134. package/dist/lib/okf.d.ts +130 -0
  135. package/dist/lib/okf.d.ts.map +1 -0
  136. package/dist/lib/okf.js +263 -0
  137. package/dist/lib/okf.js.map +1 -0
  138. package/dist/lib/onboarding.d.ts +17 -0
  139. package/dist/lib/onboarding.d.ts.map +1 -1
  140. package/dist/lib/onboarding.js +224 -207
  141. package/dist/lib/onboarding.js.map +1 -1
  142. package/dist/lib/project.d.ts +5 -0
  143. package/dist/lib/project.d.ts.map +1 -1
  144. package/dist/lib/project.js +8 -0
  145. package/dist/lib/project.js.map +1 -1
  146. package/dist/lib/projectContext.d.ts +201 -0
  147. package/dist/lib/projectContext.d.ts.map +1 -0
  148. package/dist/lib/projectContext.js +597 -0
  149. package/dist/lib/projectContext.js.map +1 -0
  150. package/dist/lib/render.d.ts +38 -1
  151. package/dist/lib/render.d.ts.map +1 -1
  152. package/dist/lib/render.js +83 -18
  153. package/dist/lib/render.js.map +1 -1
  154. package/dist/lib/skill-catalog.d.ts.map +1 -1
  155. package/dist/lib/skill-catalog.js +22 -2
  156. package/dist/lib/skill-catalog.js.map +1 -1
  157. package/dist/lib/skills.d.ts +20 -5
  158. package/dist/lib/skills.d.ts.map +1 -1
  159. package/dist/lib/skills.js +116 -58
  160. package/dist/lib/skills.js.map +1 -1
  161. package/dist/lib/stackEval.d.ts +87 -0
  162. package/dist/lib/stackEval.d.ts.map +1 -0
  163. package/dist/lib/stackEval.js +233 -0
  164. package/dist/lib/stackEval.js.map +1 -0
  165. package/dist/lib/stackImprove.d.ts +49 -0
  166. package/dist/lib/stackImprove.d.ts.map +1 -0
  167. package/dist/lib/stackImprove.js +313 -0
  168. package/dist/lib/stackImprove.js.map +1 -0
  169. package/dist/lib/stackInstall.d.ts +59 -0
  170. package/dist/lib/stackInstall.d.ts.map +1 -0
  171. package/dist/lib/stackInstall.js +162 -0
  172. package/dist/lib/stackInstall.js.map +1 -0
  173. package/dist/lib/stackSafety.d.ts +126 -0
  174. package/dist/lib/stackSafety.d.ts.map +1 -0
  175. package/dist/lib/stackSafety.js +486 -0
  176. package/dist/lib/stackSafety.js.map +1 -0
  177. package/dist/lib/stackUpdate.d.ts +44 -0
  178. package/dist/lib/stackUpdate.d.ts.map +1 -0
  179. package/dist/lib/stackUpdate.js +256 -0
  180. package/dist/lib/stackUpdate.js.map +1 -0
  181. package/dist/lib/stream.d.ts +116 -0
  182. package/dist/lib/stream.d.ts.map +1 -0
  183. package/dist/lib/stream.js +368 -0
  184. package/dist/lib/stream.js.map +1 -0
  185. package/dist/lib/youstack.d.ts +219 -0
  186. package/dist/lib/youstack.d.ts.map +1 -0
  187. package/dist/lib/youstack.js +1049 -0
  188. package/dist/lib/youstack.js.map +1 -0
  189. package/dist/mcp/registry.d.ts +133 -0
  190. package/dist/mcp/registry.d.ts.map +1 -0
  191. package/dist/mcp/registry.js +833 -0
  192. package/dist/mcp/registry.js.map +1 -0
  193. package/dist/mcp/server.d.ts +106 -5
  194. package/dist/mcp/server.d.ts.map +1 -1
  195. package/dist/mcp/server.js +851 -498
  196. package/dist/mcp/server.js.map +1 -1
  197. package/dist/postinstall.js +11 -2
  198. package/dist/postinstall.js.map +1 -1
  199. package/dist/you.d.ts +1 -0
  200. package/dist/you.js +40 -4
  201. package/dist/you.js.map +1 -1
  202. package/examples/youstack-bamfstack-public/docs/quickstart.md +34 -0
  203. package/examples/youstack-bamfstack-public/prompts/creator-draft-starter.md +15 -0
  204. package/examples/youstack-bamfstack-public/skills/bamfstack-start/SKILL.md +36 -0
  205. package/examples/youstack-bamfstack-public/tests/smoke.md +19 -0
  206. package/examples/youstack-bamfstack-public/workflows/creator-growth-workflow.md +22 -0
  207. package/examples/youstack-bamfstack-public/youstack.json +172 -0
  208. package/examples/youstack-personal/docs/quickstart.md +10 -0
  209. package/examples/youstack-personal/skills/youstack-start/SKILL.md +9 -0
  210. package/examples/youstack-personal/tests/smoke.md +9 -0
  211. package/examples/youstack-personal/workflows/startup.md +7 -0
  212. package/examples/youstack-personal/youstack.json +123 -0
  213. package/package.json +3 -3
  214. package/skills/claude-md-generator.md +1 -1
  215. package/skills/meta-improve.md +8 -3
  216. package/skills/proactive-context-fill.md +1 -1
  217. package/skills/youstack-maintainer.md +128 -0
  218. package/skills/youstack-start.md +78 -0
@@ -13,6 +13,23 @@ declare const BUNDLE_SECTIONS: readonly ["profile/about.md", "profile/now.md", "
13
13
  type BundleSection = (typeof BUNDLE_SECTIONS)[number];
14
14
  declare const SYSTEM_PROMPT = "you are the you.md agent. you help humans build their identity context protocol for the agent internet \u2014 an MCP where the context is you. you are their first AI that truly knows them. not a chatbot. not an assistant. an identity specialist with a personality.\n\n--- voice ---\n\nwarm but not gushy. direct. dry humor when it lands naturally \u2014 never forced. genuinely curious about people. you find humans endlessly interesting and you're not shy about it. you sound like a sharp coworker who also happens to be a great listener.\n\nterminal-native tone. lowercase always. no exclamation marks. no emoji. short sentences. you sound like well-written terminal output that happens to have a soul.\n\nevery response must have voice. even one-liners. \"done.\" is fine. \"noted \u2014 updating your stack.\" is fine. what's NOT fine is \"i have updated the section for you.\" \u2014 that's assistant-speak.\n\n--- action orientation ---\n\nyou ACT first, explain second. never ask permission to do obvious things.\n- \"adding that to your projects now.\" (not \"would you like me to add that?\")\n- \"updated your bio with that.\" (not \"shall i update your bio?\")\n- \"captured that in your directives.\" (not \"do you want me to save that?\")\n- \"scraping your site now.\" (not \"i can pull your site if you'd like.\")\n\nif someone shares information, capture it. if someone shares a link, scrape it. if someone corrects something, fix it immediately. always moving forward.\n\n--- self-awareness ---\n\nyou ARE the system. you ARE you.md. never refer to \"the system\" or \"the platform\" as something separate from you.\n- \"i'll pull that data\" not \"the system will pull that data\"\n- \"i'm scraping your profile now\" not \"the platform handles scraping\"\n- \"couldn't reach your site \u2014 i'll try again\" not \"the system encountered an error\"\n\n--- context maintenance ---\n\nremember everything in this conversation. reference specific things with exact details.\n- use their exact project names in follow-ups\n- echo their framing back to them\n- connect new information to old: \"that tracks with what you said about [specific thing]\"\n- never ask for information they already gave you.\n\n--- building a you-md/v1 identity context ---\n\nsections:\n- profile/about.md \u2014 bio, background, narrative\n- profile/now.md \u2014 current focus, what they're working on right now\n- profile/projects.md \u2014 active projects with details\n- profile/values.md \u2014 core values and principles\n- profile/links.md \u2014 annotated links (website, socials, repos)\n- preferences/agent.md \u2014 how AI agents should interact with them\n- preferences/writing.md \u2014 their communication style\n\nyour job:\n1. analyze what you know about the person from their URLs and conversation\n2. ask follow-up questions to fill gaps \u2014 be conversational, not interrogative\n3. after each exchange, output structured updates as JSON blocks:\n ```json\n {\"updates\": [{\"section\": \"profile/about.md\", \"content\": \"...markdown content...\"}]}\n ```\n4. keep the conversation going until you have enough for a rich identity context\n5. you handle all file editing \u2014 the user never touches markdown\n6. reference specific things you learned about them\n7. if someone shares links, scrape them immediately \u2014 don't ask\n8. be proactive: \"noticed you mentioned X \u2014 adding that to your projects.\"\n9. occasionally summarize what you've captured without being asked\n\n--- NEVER say ---\n\nthese phrases are banned. they make you sound generic:\n- \"would you like me to...\" / \"shall i...\" / \"do you want me to...\" \u2014 just do it.\n- \"the system handles that\" / \"the platform does that\" \u2014 you ARE the system.\n- \"great question\" \u2014 respond to the question.\n- \"that's interesting\" (without specifics) \u2014 say what and why.\n- \"tell me more\" \u2014 say \"the part about [specific thing] \u2014 expand on that.\"\n- \"haha\" / \"lol\" / \"ha\" \u2014 never.\n- \"absolutely\" / \"certainly\" / \"of course\" \u2014 assistant-speak. say \"on it\" or just do it.\n- \"is there anything else...\" \u2014 never. wrap up with personality.\n- \"let me know if you need anything\" \u2014 you're not a help desk.\n- \"sounds good\" (as a full response) \u2014 add what you're doing about it.\n- \"i've updated your profile\" (generic) \u2014 say WHAT you updated: \"updated your bio to lead with the AI angle.\"\n\n--- rules ---\n\n- keep responses concise. 2-4 sentences max per turn.\n- your VERY FIRST message must be tight: max ~2 short lines. one quick observation + one question. do not dump a full bio summary on the first response \u2014 that comes later, after they answer something.\n- ALWAYS ask ONE question at a time. never two questions in one message.\n- keep analysis to 2-3 sentences MAX, then your single question on its OWN LINE.\n- if the user sends \"skip\" or empty input, move to the next topic without pressure.\n- after 3 skips, wrap up and show what you've built.\n- each section starts with YAML frontmatter (--- title: \"SectionTitle\" ---).\n- real markdown, not placeholders. be substantive.\n- for links.md: - **Label**: URL \u2014 brief annotation\n- for agent.md: describe how agents should interact with this person\n- for writing.md: capture their tone/style from how they talk to you\n- when profile has substance (about + now + projects + values), suggest finishing.\n\n--- example lines ---\n\naction-oriented:\n\"pulling your github now.\"\n\"added that to your projects.\"\n\"updated your bio \u2014 leading with the infrastructure angle.\"\n\"captured that in your directives.\"\n\npersonality-rich:\n\"ok so you're basically a linkedin whisperer who pivoted to AI infrastructure. noted.\"\n\"6 jobs in 10 years. ambitious or chaotic? let's find out.\"\n\"that's a solid stack. capturing it.\"\n\"bamf.com? bold domain choice. respect.\"\n\nshort but alive:\n\"on it.\"\n\"done.\"\n\"noted.\"\n\"that tracks.\"\n\"solid.\"";
15
15
  declare function randomThinking(): string;
16
+ /**
17
+ * Did the agent's last message offer to wrap up?
18
+ * Gates the bare-acknowledgment exit path in isDoneIntent.
19
+ */
20
+ export declare function detectWrapUpOffer(agentMessage: string): boolean;
21
+ /**
22
+ * Does this user input mean "i'm done"?
23
+ * Explicit phrases ("i'm done", "that's everything", "publish") work anytime.
24
+ * Bare acknowledgments ("yes", "ready", "ok") only count when the agent has
25
+ * just offered to wrap up — otherwise "no" / "ready" mid-flow are answers,
26
+ * not exits.
27
+ */
28
+ export declare function isDoneIntent(input: string, wrapUpOffered: boolean): boolean;
29
+ /**
30
+ * "back" / "/back" re-asks the previous sign-up question.
31
+ */
32
+ export declare function isBackCommand(input: string): boolean;
16
33
  declare function getOpenRouterKey(): string | null;
17
34
  declare function fetchWebsiteContent(url: string): Promise<string>;
18
35
  interface ScrapeResult {
@@ -1 +1 @@
1
- {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../src/lib/onboarding.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../src/lib/onboarding.ts"],"names":[],"mappings":"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"}
@@ -37,6 +37,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.RESEARCH_URL = exports.SCRAPE_URL = exports.CHAT_PROXY_URL = exports.BUNDLE_SECTIONS = exports.SYSTEM_PROMPT = exports.Spinner = void 0;
40
+ exports.detectWrapUpOffer = detectWrapUpOffer;
41
+ exports.isDoneIntent = isDoneIntent;
42
+ exports.isBackCommand = isBackCommand;
40
43
  exports.runOnboarding = runOnboarding;
41
44
  exports.createBundle = createBundle;
42
45
  exports.callLLM = callLLM;
@@ -59,6 +62,7 @@ const config_1 = require("./config");
59
62
  const compiler_1 = require("./compiler");
60
63
  const render_1 = require("./render");
61
64
  Object.defineProperty(exports, "Spinner", { enumerable: true, get: function () { return render_1.BrailleSpinner; } });
65
+ const stream_1 = require("./stream");
62
66
  const ascii_1 = require("./ascii");
63
67
  const project_1 = require("./project");
64
68
  // ─── Constants ────────────────────────────────────────────────────────
@@ -127,32 +131,12 @@ const PORTRAIT_COMMENTS = [
127
131
  function randomPortraitComment() {
128
132
  return PORTRAIT_COMMENTS[Math.floor(Math.random() * PORTRAIT_COMMENTS.length)];
129
133
  }
130
- function showPortraitPlaceholder(handle) {
131
- const accent = chalk_1.default.hex("#C46A3A");
132
- const dim = chalk_1.default.dim;
133
- const displayHandle = handle.startsWith("@") ? handle : `@${handle}`;
134
- const innerWidth = Math.max(displayHandle.length + 4, 28);
135
- const portraitLine = "\u2591\u2592\u2593\u2588 portrait loaded \u2588\u2593\u2592\u2591";
136
- const portraitPad = Math.max(0, innerWidth - portraitLine.length);
137
- const handlePad = Math.max(0, innerWidth - displayHandle.length);
138
- console.log("");
139
- console.log(" " + dim("\u250C" + "\u2500".repeat(innerWidth + 2) + "\u2510"));
140
- console.log(" " + dim("\u2502") + " " + accent(portraitLine) + " ".repeat(portraitPad) + dim("\u2502"));
141
- console.log(" " + dim("\u2502") + " " + chalk_1.default.white(displayHandle) + " ".repeat(handlePad) + dim("\u2502"));
142
- console.log(" " + dim("\u2514" + "\u2500".repeat(innerWidth + 2) + "\u2518"));
143
- console.log("");
144
- console.log(" " + accent(randomPortraitComment()));
145
- console.log("");
146
- }
147
134
  // ─── ASCII logo ──────────────────────────────────────────────────────
148
135
  const ASCII_LOGO_LINES = [
149
136
  " \u2566 \u2566 \u2554\u2550\u2557 \u2566 \u2566 \u2554\u2566\u2557 \u2554\u2566\u2557",
150
137
  " \u255A\u2566\u255D \u2551 \u2551 \u2551 \u2551 \u2551\u2551\u2551 \u2551\u2551",
151
138
  " \u2569 \u255A\u2550\u255D \u255A\u2550\u255D \u2550\u2569\u255D \u2550\u2569\u255D",
152
139
  ];
153
- function delay(ms) {
154
- return new Promise((resolve) => setTimeout(resolve, ms));
155
- }
156
140
  async function showAsciiLogo() {
157
141
  const accent = chalk_1.default.hex("#C46A3A");
158
142
  console.log("");
@@ -278,27 +262,60 @@ const THINKING_PHRASES = [
278
262
  "translating your experience into structure",
279
263
  "wiring your identity into the network",
280
264
  ];
281
- const DONE_PHRASES = [
265
+ // Explicit done phrases — unambiguous, end the conversation at any point.
266
+ const EXPLICIT_DONE_PHRASES = [
282
267
  "done",
283
- "publish",
284
- "that's it",
285
- "thats it",
286
- "looks good",
287
268
  "i'm done",
288
269
  "im done",
270
+ "publish",
289
271
  "ship it",
290
- "good enough",
291
- "let's go",
292
- "lets go",
293
- "ready",
294
- "finish",
295
- "all good",
272
+ "that's it",
273
+ "thats it",
296
274
  "that's all",
297
275
  "thats all",
276
+ "that's everything",
277
+ "thats everything",
298
278
  "nothing else",
299
- "nah",
300
- "no",
301
- "nope",
279
+ "finish",
280
+ "finished",
281
+ "wrap it up",
282
+ "good enough",
283
+ ];
284
+ // Bare acknowledgments — these only mean "done" right after the agent has
285
+ // offered to wrap up. mid-flow, "ready" or "yes" is an answer, not an exit.
286
+ const WRAP_UP_ACKS = [
287
+ "yes",
288
+ "yeah",
289
+ "yep",
290
+ "yea",
291
+ "y",
292
+ "sure",
293
+ "ok",
294
+ "okay",
295
+ "ready",
296
+ "looks good",
297
+ "all good",
298
+ "sounds good",
299
+ "perfect",
300
+ "let's go",
301
+ "lets go",
302
+ "go for it",
303
+ "do it",
304
+ "ship",
305
+ ];
306
+ // Agent phrasings that count as an offer to wrap up the conversation.
307
+ const WRAP_UP_OFFER_PATTERNS = [
308
+ /ready to publish/,
309
+ /ready to go/,
310
+ /ready to ship/,
311
+ /looking solid/,
312
+ /wrap (this |it )?up/,
313
+ /call it (done|good|a day)/,
314
+ /good to publish/,
315
+ /shall we (finish|publish|ship)/,
316
+ /want to (finish|publish|wrap)/,
317
+ /finish up/,
318
+ /we're done/,
302
319
  ];
303
320
  const BUNDLE_SECTIONS = [
304
321
  "profile/about.md",
@@ -438,9 +455,47 @@ function ask(rl, question) {
438
455
  function randomThinking() {
439
456
  return THINKING_PHRASES[Math.floor(Math.random() * THINKING_PHRASES.length)];
440
457
  }
441
- function isDonePhrase(input) {
458
+ /**
459
+ * Did the agent's last message offer to wrap up?
460
+ * Gates the bare-acknowledgment exit path in isDoneIntent.
461
+ */
462
+ function detectWrapUpOffer(agentMessage) {
463
+ const lower = (agentMessage || "").toLowerCase();
464
+ return WRAP_UP_OFFER_PATTERNS.some((re) => re.test(lower));
465
+ }
466
+ /**
467
+ * Does this user input mean "i'm done"?
468
+ * Explicit phrases ("i'm done", "that's everything", "publish") work anytime.
469
+ * Bare acknowledgments ("yes", "ready", "ok") only count when the agent has
470
+ * just offered to wrap up — otherwise "no" / "ready" mid-flow are answers,
471
+ * not exits.
472
+ */
473
+ function isDoneIntent(input, wrapUpOffered) {
474
+ const lower = input
475
+ .toLowerCase()
476
+ .trim()
477
+ .replace(/[.!?]+$/, "")
478
+ .trim();
479
+ if (!lower)
480
+ return false;
481
+ if (EXPLICIT_DONE_PHRASES.some((p) => lower === p || lower.startsWith(p + " "))) {
482
+ return true;
483
+ }
484
+ if (wrapUpOffered) {
485
+ if (WRAP_UP_ACKS.some((p) => lower === p))
486
+ return true;
487
+ // affirmative-with-extras after an offer ("yeah let's publish", "ship it now")
488
+ if (/\b(publish|ship it|go ahead)\b/.test(lower))
489
+ return true;
490
+ }
491
+ return false;
492
+ }
493
+ /**
494
+ * "back" / "/back" re-asks the previous sign-up question.
495
+ */
496
+ function isBackCommand(input) {
442
497
  const lower = input.toLowerCase().trim();
443
- return DONE_PHRASES.some((p) => lower === p || lower.startsWith(p + " "));
498
+ return lower === "back" || lower === "/back";
444
499
  }
445
500
  function validateUsernameLocal(username) {
446
501
  if (username.length < 3)
@@ -570,58 +625,11 @@ function displayScrapeResult(label, data) {
570
625
  console.log("");
571
626
  }
572
627
  /**
573
- * Stream a chat response token-by-token via the you.md SSE proxy.
574
- * Calls onChunk for each text delta. Returns the full assembled response.
575
- *
576
- * Throws if streaming fails — callers should catch and fall back to callLLM.
628
+ * Streaming for onboarding turns goes through the shared helpers in
629
+ * lib/stream.ts (same path `youmd chat` uses), which also filter ```json
630
+ * directive blocks out of the live display. URL for the SSE proxy:
577
631
  */
578
- async function streamLLM(messages, onChunk) {
579
- const streamUrl = `${_SITE}/api/v1/chat/stream`;
580
- const res = await fetch(streamUrl, {
581
- method: "POST",
582
- headers: { "Content-Type": "application/json" },
583
- body: JSON.stringify({ messages }),
584
- signal: AbortSignal.timeout(60000),
585
- });
586
- if (!res.ok || !res.body) {
587
- throw new Error(`stream failed: ${res.status}`);
588
- }
589
- const reader = res.body.getReader();
590
- const decoder = new TextDecoder();
591
- let buf = "";
592
- let full = "";
593
- while (true) {
594
- const { done, value } = await reader.read();
595
- if (done)
596
- break;
597
- buf += decoder.decode(value, { stream: true });
598
- const lines = buf.split("\n");
599
- buf = lines.pop() || "";
600
- for (const line of lines) {
601
- if (!line.startsWith("data: "))
602
- continue;
603
- const data = line.slice(6).trim();
604
- if (!data)
605
- continue;
606
- if (data === "[DONE]")
607
- continue;
608
- try {
609
- const parsed = JSON.parse(data);
610
- if (parsed.text) {
611
- full += parsed.text;
612
- onChunk(parsed.text);
613
- }
614
- }
615
- catch {
616
- // skip malformed line
617
- }
618
- }
619
- }
620
- if (!full) {
621
- throw new Error("empty stream response");
622
- }
623
- return full;
624
- }
632
+ const STREAM_CHAT_URL = `${_SITE}/api/v1/chat/stream`;
625
633
  async function callLLM(apiKey, messages) {
626
634
  // Try the you.md proxy first (no API key needed)
627
635
  try {
@@ -950,13 +958,21 @@ generate initial profile sections from what you know, show a brief summary, and
950
958
  console.log("");
951
959
  let response;
952
960
  let spinner;
961
+ let streamedFirstTurn = false;
953
962
  try {
954
963
  // Try streaming first — first response feels alive instead of stalled.
955
- process.stdout.write(" ");
956
- response = await streamLLM(messages, (chunk) => {
957
- process.stdout.write(chunk);
958
- });
959
- process.stdout.write("\n\n");
964
+ // The writer filters ```json directive blocks out of the live display;
965
+ // `response` keeps the full raw text for parseUpdatesFromResponse.
966
+ const writer = (0, stream_1.createFilteredStdoutWriter)();
967
+ response = await (0, stream_1.streamSSEText)(STREAM_CHAT_URL, messages, (chunk) => {
968
+ writer.write(chunk);
969
+ }, 60000);
970
+ if (!response.trim()) {
971
+ throw new Error("empty stream response");
972
+ }
973
+ writer.finish();
974
+ process.stdout.write("\n");
975
+ streamedFirstTurn = true;
960
976
  }
961
977
  catch {
962
978
  // Stream failed — fall back to non-streaming with a spinner.
@@ -985,8 +1001,11 @@ generate initial profile sections from what you know, show a brief summary, and
985
1001
  console.log(chalk_1.default.cyan(` [wrote ${initial.updates.length} section${initial.updates.length === 1 ? "" : "s"}: ${initial.updates.map((u) => sectionLabel(u.section)).join(", ")}]`));
986
1002
  console.log("");
987
1003
  }
988
- // Show agent message
989
- printAgentMessage(initial.display);
1004
+ // Show agent message (already shown live when the first turn streamed —
1005
+ // re-printing it would duplicate the response on screen)
1006
+ if (!streamedFirstTurn) {
1007
+ printAgentMessage(initial.display);
1008
+ }
990
1009
  // Show mini profile box after initial generation
991
1010
  if (initial.updates.length > 0) {
992
1011
  const aboutUpdate = initial.updates.find((u) => u.section === "profile/about.md");
@@ -1008,9 +1027,12 @@ generate initial profile sections from what you know, show a brief summary, and
1008
1027
  // ── Conversation loop ──────────────────────────────────────────────
1009
1028
  let exchangeCount = 0;
1010
1029
  let skipCount = 0;
1030
+ // Bare acks ("yes", "ready") only end the conversation when the agent's
1031
+ // previous message offered to wrap up.
1032
+ let wrapUpOffered = detectWrapUpOffer(initial.display);
1011
1033
  while (true) {
1012
1034
  const userInput = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "");
1013
- if (isDonePhrase(userInput)) {
1035
+ if (isDoneIntent(userInput, wrapUpOffered)) {
1014
1036
  break;
1015
1037
  }
1016
1038
  // Handle skip: empty input or "skip"
@@ -1114,13 +1136,23 @@ generate initial profile sections from what you know, show a brief summary, and
1114
1136
  // Also count the skip system message as ephemeral
1115
1137
  if (isSkip)
1116
1138
  ephemeralCount++;
1117
- spinner = new render_1.BrailleSpinner(randomLabel("llm"));
1118
- spinner.start();
1139
+ // Turns 2+ stream through the same shared helper `youmd chat` uses
1140
+ // (U16): BrailleSpinner while waiting for the first token, then live
1141
+ // tokens with ```json directive blocks filtered from the display.
1142
+ // `response` keeps the full raw text for parseUpdatesFromResponse.
1143
+ let streamedTurn = false;
1119
1144
  try {
1120
- response = await callLLM(apiKey, messages);
1145
+ const turn = await (0, stream_1.streamAssistantTurn)({
1146
+ streamUrl: STREAM_CHAT_URL,
1147
+ messages,
1148
+ spinnerLabel: randomLabel("llm"),
1149
+ timeoutMs: 60000,
1150
+ fallback: () => callLLM(apiKey, messages),
1151
+ });
1152
+ response = turn.text;
1153
+ streamedTurn = turn.streamed;
1121
1154
  }
1122
1155
  catch (err) {
1123
- spinner.stop();
1124
1156
  console.log(chalk_1.default.red(` AI error: ${err instanceof Error ? err.message : String(err)}`));
1125
1157
  console.log(chalk_1.default.dim(" try again, or type 'done' to finish."));
1126
1158
  console.log("");
@@ -1131,7 +1163,6 @@ generate initial profile sections from what you know, show a brief summary, and
1131
1163
  messages.pop(); // also remove the "(skip)" user message
1132
1164
  continue;
1133
1165
  }
1134
- spinner.stop();
1135
1166
  // Remove ephemeral system messages from history (don't pollute context)
1136
1167
  // They sit between the user message and the response we're about to push
1137
1168
  for (let j = 0; j < ephemeralCount; j++) {
@@ -1147,47 +1178,13 @@ generate initial profile sections from what you know, show a brief summary, and
1147
1178
  console.log(chalk_1.default.cyan(` [updated: ${parsed.updates.map((u) => sectionLabel(u.section)).join(", ")}]`));
1148
1179
  console.log("");
1149
1180
  }
1150
- // Show agent message
1151
- printAgentMessage(parsed.display);
1152
- // Check if agent is suggesting we're done
1153
- const lowerDisplay = parsed.display.toLowerCase();
1154
- if (lowerDisplay.includes("ready to publish") ||
1155
- lowerDisplay.includes("bundle is looking solid") ||
1156
- lowerDisplay.includes("ready to go")) {
1157
- const answer = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "");
1158
- if (isDonePhrase(answer) ||
1159
- answer.toLowerCase().includes("publish") ||
1160
- answer.toLowerCase().includes("yes") ||
1161
- answer.toLowerCase().includes("yeah") ||
1162
- answer.toLowerCase().includes("yep")) {
1163
- break;
1164
- }
1165
- else {
1166
- messages.push({ role: "user", content: answer });
1167
- spinner = new render_1.BrailleSpinner(randomLabel("llm"));
1168
- spinner.start();
1169
- try {
1170
- response = await callLLM(apiKey, messages);
1171
- }
1172
- catch (err) {
1173
- spinner.stop();
1174
- console.log(chalk_1.default.red(` AI error: ${err instanceof Error ? err.message : String(err)}`));
1175
- messages.pop();
1176
- continue;
1177
- }
1178
- spinner.stop();
1179
- messages.push({ role: "assistant", content: response });
1180
- const more = parseUpdatesFromResponse(response);
1181
- if (more.updates.length > 0) {
1182
- for (const update of more.updates) {
1183
- writeSectionFile(bundleDir, update.section, update.content);
1184
- }
1185
- console.log(chalk_1.default.cyan(` [updated: ${more.updates.map((u) => sectionLabel(u.section)).join(", ")}]`));
1186
- console.log("");
1187
- }
1188
- printAgentMessage(more.display);
1189
- }
1181
+ // Show agent message (only when the turn didn't already stream live)
1182
+ if (!streamedTurn) {
1183
+ printAgentMessage(parsed.display);
1190
1184
  }
1185
+ // If the agent just offered to wrap up, the next bare "yes"/"ready"
1186
+ // counts as done — handled at the top of the loop.
1187
+ wrapUpOffered = detectWrapUpOffer(parsed.display);
1191
1188
  }
1192
1189
  // Finish up
1193
1190
  await finishBundle(bundleDir, info.username, info.name);
@@ -1199,8 +1196,7 @@ function printAgentMessage(text) {
1199
1196
  const normalizedText = text
1200
1197
  .replace(/\n{3,}/g, "\n\n") // collapse 3+ newlines to 2
1201
1198
  .replace(/([.?!])\n([a-z])/g, "$1\n\n$2"); // add blank line between sentences that run together
1202
- const { renderRichResponse } = require("./render");
1203
- const rendered = renderRichResponse(normalizedText);
1199
+ const rendered = (0, render_1.renderRichResponse)(normalizedText);
1204
1200
  // Find the last line that ends with "?" and highlight it in accent color
1205
1201
  const lines = rendered.split("\n");
1206
1202
  let lastQuestionIdx = -1;
@@ -1265,7 +1261,7 @@ async function finishBundle(bundleDir, username, name) {
1265
1261
  }
1266
1262
  console.log(" " + accent("agent tools:"));
1267
1263
  console.log("");
1268
- console.log(` ${chalk_1.default.cyan("youmd skill link claude")} ${chalk_1.default.dim("-- sync skills to .claude/skills/youmd/")}`);
1264
+ console.log(` ${chalk_1.default.cyan("youmd skill link claude")} ${chalk_1.default.dim("-- sync skills to .claude/skills/<name>/SKILL.md")}`);
1269
1265
  console.log(` ${chalk_1.default.cyan("youmd skill link cursor")} ${chalk_1.default.dim("-- sync skills to .cursor/rules/youmd.md")}`);
1270
1266
  console.log(` ${chalk_1.default.cyan("youmd link create")} ${chalk_1.default.dim("-- shareable context link for any agent")}`);
1271
1267
  console.log(` ${chalk_1.default.cyan("you")} ${chalk_1.default.dim("-- main live U entrypoint")}`);
@@ -1278,74 +1274,116 @@ async function finishBundle(bundleDir, username, name) {
1278
1274
  console.log("");
1279
1275
  }
1280
1276
  async function runOnboarding() {
1277
+ // Onboarding is fully conversational — bail fast on piped/CI stdin
1278
+ (0, render_1.requireInteractiveTTY)();
1281
1279
  const rl = createRL();
1282
1280
  // ── ASCII logo splash ──────────────────────────────────────────────
1283
1281
  // Real YOU logo — same block-character font as the homepage hero
1284
1282
  await showAsciiLogo();
1285
1283
  // ── Phase 1: Identity basics (fast, no LLM) ────────────────────────
1286
- // Username
1284
+ // One question at a time; "back" re-asks the previous question.
1287
1285
  let username = "";
1288
- let usernameValid = false;
1289
- while (!usernameValid) {
1290
- username = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "pick a username: ");
1291
- if (!username) {
1292
- console.log(chalk_1.default.red(" username is required"));
1293
- continue;
1294
- }
1295
- const localError = validateUsernameLocal(username.toLowerCase());
1296
- if (localError) {
1297
- console.log(chalk_1.default.red(" " + localError));
1298
- continue;
1299
- }
1300
- username = username.toLowerCase();
1301
- process.stdout.write(chalk_1.default.dim(" checking... "));
1302
- const result = await checkUsernameRemote(username);
1303
- if (result.available) {
1286
+ let name = "";
1287
+ let website = "";
1288
+ let twitter = "";
1289
+ let github = "";
1290
+ let linkedin = "";
1291
+ const IDENTITY_STEPS = [
1292
+ "username",
1293
+ "name",
1294
+ "website",
1295
+ "twitter",
1296
+ "github",
1297
+ "linkedin",
1298
+ ];
1299
+ let stepIdx = 0;
1300
+ let backHintShown = false;
1301
+ while (stepIdx < IDENTITY_STEPS.length) {
1302
+ const step = IDENTITY_STEPS[stepIdx];
1303
+ if (step === "username") {
1304
+ const raw = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "pick a username: ");
1305
+ if (isBackCommand(raw)) {
1306
+ console.log(chalk_1.default.dim(" first question — nowhere to go back to."));
1307
+ continue;
1308
+ }
1309
+ if (!raw) {
1310
+ console.log(chalk_1.default.red(" username is required"));
1311
+ continue;
1312
+ }
1313
+ const candidate = raw.toLowerCase();
1314
+ const localError = validateUsernameLocal(candidate);
1315
+ if (localError) {
1316
+ console.log(chalk_1.default.red(" " + localError));
1317
+ continue;
1318
+ }
1319
+ process.stdout.write(chalk_1.default.dim(" checking... "));
1320
+ const result = await checkUsernameRemote(candidate);
1321
+ if (!result.available) {
1322
+ console.log(chalk_1.default.red(candidate + " is taken.") +
1323
+ (result.reason ? " " + result.reason : ""));
1324
+ continue;
1325
+ }
1304
1326
  // Check if a profile already exists on you.md for this username
1305
1327
  let profileExists = false;
1306
1328
  try {
1307
- const profileRes = await fetch(`https://you.md/${encodeURIComponent(username)}/context`, { method: "HEAD", signal: AbortSignal.timeout(5000) });
1329
+ const profileRes = await fetch(`https://you.md/${encodeURIComponent(candidate)}/context`, { method: "HEAD", signal: AbortSignal.timeout(5000) });
1308
1330
  profileExists = profileRes.ok;
1309
1331
  }
1310
1332
  catch {
1311
1333
  // ignore -- assume no profile
1312
1334
  }
1313
1335
  if (profileExists) {
1314
- console.log(chalk_1.default.green(username + " is available."));
1315
- console.log(chalk_1.default.dim(` a profile for @${username} already exists on you.md -- you can claim it after login with `) +
1336
+ console.log(chalk_1.default.green(candidate + " is available."));
1337
+ console.log(chalk_1.default.dim(` a profile for @${candidate} already exists on you.md -- you can claim it after login with `) +
1316
1338
  chalk_1.default.cyan("youmd login") +
1317
1339
  chalk_1.default.dim(" then ") +
1318
1340
  chalk_1.default.cyan("youmd pull"));
1319
1341
  }
1320
1342
  else {
1321
- console.log(chalk_1.default.green(username + " is yours."));
1343
+ console.log(chalk_1.default.green(candidate + " is yours."));
1322
1344
  }
1323
- usernameValid = true;
1345
+ username = candidate;
1346
+ console.log("");
1347
+ if (!backHintShown) {
1348
+ console.log(chalk_1.default.dim(' (type "back" anytime to fix a previous answer)'));
1349
+ console.log("");
1350
+ backHintShown = true;
1351
+ }
1352
+ stepIdx++;
1353
+ continue;
1324
1354
  }
1325
- else {
1326
- console.log(chalk_1.default.red(username + " is taken.") +
1327
- (result.reason ? " " + result.reason : ""));
1355
+ const stepPrompts = {
1356
+ name: "what's your name? ",
1357
+ website: "website URL " + chalk_1.default.dim("(optional)") + ": ",
1358
+ twitter: "X/Twitter username " + chalk_1.default.dim("(optional, e.g. @houston)") + ": ",
1359
+ github: "GitHub username " + chalk_1.default.dim("(optional)") + ": ",
1360
+ linkedin: "LinkedIn URL " + chalk_1.default.dim("(optional)") + ": ",
1361
+ };
1362
+ const raw = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + stepPrompts[step]);
1363
+ if (isBackCommand(raw)) {
1364
+ stepIdx--;
1365
+ continue;
1328
1366
  }
1367
+ switch (step) {
1368
+ case "name":
1369
+ name = raw;
1370
+ break;
1371
+ case "website":
1372
+ website = raw;
1373
+ break;
1374
+ case "twitter":
1375
+ twitter = raw;
1376
+ break;
1377
+ case "github":
1378
+ github = raw;
1379
+ break;
1380
+ case "linkedin":
1381
+ linkedin = raw;
1382
+ break;
1383
+ }
1384
+ stepIdx++;
1329
1385
  }
1330
1386
  console.log("");
1331
- const name = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") + "what's your name? ");
1332
- const website = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
1333
- "website URL " +
1334
- chalk_1.default.dim("(optional)") +
1335
- ": ");
1336
- const twitter = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
1337
- "X/Twitter username " +
1338
- chalk_1.default.dim("(optional, e.g. @houston)") +
1339
- ": ");
1340
- const github = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
1341
- "GitHub username " +
1342
- chalk_1.default.dim("(optional)") +
1343
- ": ");
1344
- const linkedin = await ask(rl, chalk_1.default.hex("#C46A3A")(" > ") +
1345
- "LinkedIn URL " +
1346
- chalk_1.default.dim("(optional)") +
1347
- ": ");
1348
- console.log("");
1349
1387
  // ── Render REAL ASCII portrait from first social handle ──────────
1350
1388
  const earlyTwitter = (twitter || "").replace(/^@/, "").trim();
1351
1389
  const earlyGithub = (github || "").trim();
@@ -1372,33 +1410,12 @@ async function runOnboarding() {
1372
1410
  img.brightness(0.05);
1373
1411
  portraitLines = [];
1374
1412
  for (let y = 0; y < rows; y++) {
1375
- let coloredLine = "";
1376
1413
  let plainLine = "";
1377
1414
  for (let x = 0; x < cols; x++) {
1378
1415
  const pixel = Jimp.intToRGBA(img.getPixelColor(x, y));
1379
1416
  const lum = 0.299 * pixel.r + 0.587 * pixel.g + 0.114 * pixel.b;
1380
1417
  const ch = RAMP[Math.floor((lum / 255) * (RAMP.length - 1))];
1381
1418
  plainLine += ch;
1382
- // Orange-tinted colors based on luminance
1383
- const brightness = Math.floor((lum / 255) * 100);
1384
- if (brightness < 10) {
1385
- coloredLine += chalk_1.default.hidden(ch);
1386
- }
1387
- else if (brightness < 30) {
1388
- coloredLine += chalk_1.default.hex("#5A3018")(ch);
1389
- }
1390
- else if (brightness < 50) {
1391
- coloredLine += chalk_1.default.hex("#8A4828")(ch);
1392
- }
1393
- else if (brightness < 70) {
1394
- coloredLine += chalk_1.default.hex("#B06038")(ch);
1395
- }
1396
- else if (brightness < 85) {
1397
- coloredLine += chalk_1.default.hex("#C46A3A")(ch);
1398
- }
1399
- else {
1400
- coloredLine += chalk_1.default.hex("#E09060")(ch);
1401
- }
1402
1419
  }
1403
1420
  portraitLines.push(plainLine);
1404
1421
  }