youmd 0.6.1 → 0.6.4

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 +220 -45
  3. package/dist/commands/chat.js.map +1 -1
  4. package/dist/commands/login.d.ts.map +1 -1
  5. package/dist/commands/login.js +11 -2
  6. package/dist/commands/login.js.map +1 -1
  7. package/dist/commands/logout.d.ts +2 -0
  8. package/dist/commands/logout.d.ts.map +1 -0
  9. package/dist/commands/logout.js +24 -0
  10. package/dist/commands/logout.js.map +1 -0
  11. package/dist/index.js +197 -45
  12. package/dist/index.js.map +1 -1
  13. package/dist/lib/api.d.ts.map +1 -1
  14. package/dist/lib/api.js +2 -4
  15. package/dist/lib/api.js.map +1 -1
  16. package/dist/lib/ascii.d.ts +14 -0
  17. package/dist/lib/ascii.d.ts.map +1 -1
  18. package/dist/lib/ascii.js +177 -11
  19. package/dist/lib/ascii.js.map +1 -1
  20. package/dist/lib/compiler.d.ts.map +1 -1
  21. package/dist/lib/compiler.js +4 -0
  22. package/dist/lib/compiler.js.map +1 -1
  23. package/dist/lib/config.d.ts +9 -0
  24. package/dist/lib/config.d.ts.map +1 -1
  25. package/dist/lib/config.js +35 -2
  26. package/dist/lib/config.js.map +1 -1
  27. package/dist/lib/decompile.d.ts.map +1 -1
  28. package/dist/lib/decompile.js +48 -29
  29. package/dist/lib/decompile.js.map +1 -1
  30. package/dist/lib/update.d.ts +3 -0
  31. package/dist/lib/update.d.ts.map +1 -0
  32. package/dist/lib/update.js +55 -0
  33. package/dist/lib/update.js.map +1 -0
  34. package/dist/mcp/server.js +1 -1
  35. package/dist/postinstall.d.ts +1 -0
  36. package/dist/postinstall.js +18 -2
  37. package/dist/postinstall.js.map +1 -1
  38. package/dist/you.d.ts +2 -0
  39. package/dist/you.d.ts.map +1 -0
  40. package/dist/you.js +15 -0
  41. package/dist/you.js.map +1 -0
  42. package/package.json +6 -5
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAy9BA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAmkBjD"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAioCA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA6lBjD"}
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.chatCommand = chatCommand;
40
40
  const readline = __importStar(require("readline"));
41
41
  const fs = __importStar(require("fs"));
42
+ const os = __importStar(require("os"));
42
43
  const path = __importStar(require("path"));
43
44
  const chalk_1 = __importDefault(require("chalk"));
44
45
  const config_1 = require("../lib/config");
@@ -47,10 +48,13 @@ const compiler_1 = require("../lib/compiler");
47
48
  const api_1 = require("../lib/api");
48
49
  const render_1 = require("../lib/render");
49
50
  const onboarding_1 = require("../lib/onboarding");
51
+ const ascii_1 = require("../lib/ascii");
52
+ const update_1 = require("../lib/update");
50
53
  // ─── URL Detection + Scraping (mirrors web useYouAgent) ──────────────
51
54
  const config_2 = require("../lib/config");
52
55
  const CONVEX_SITE_URL = (0, config_2.getConvexSiteUrl)();
53
56
  const STREAM_URL = `${CONVEX_SITE_URL}/api/v1/chat/stream`;
57
+ const CURRENT_VERSION = "0.6.4";
54
58
  // ─── Streaming LLM client ─────────────────────────────────────────────
55
59
  async function streamLLM(_apiKey, messages, onToken) {
56
60
  const res = await fetch(STREAM_URL, {
@@ -148,7 +152,7 @@ async function callLLMWithStreaming(apiKey, messages, spinnerLabel) {
148
152
  // No tokens received -- clear spinner
149
153
  thinkSpinner.stop();
150
154
  }
151
- return response;
155
+ return { text: response, streamed: !firstToken };
152
156
  }
153
157
  catch {
154
158
  // Streaming failed -- fall back to blocking call
@@ -156,7 +160,7 @@ async function callLLMWithStreaming(apiKey, messages, spinnerLabel) {
156
160
  try {
157
161
  const response = await (0, onboarding_1.callLLM)(apiKey, messages);
158
162
  thinkSpinner.stop();
159
- return response;
163
+ return { text: response, streamed: false };
160
164
  }
161
165
  catch (err) {
162
166
  thinkSpinner.fail(err instanceof Error ? err.message : "failed");
@@ -839,9 +843,149 @@ function extractProfileHint(bundleDir) {
839
843
  }
840
844
  return null;
841
845
  }
846
+ function repoNeedsBootstrap(projectRoot) {
847
+ return (!fs.existsSync(path.join(projectRoot, "AGENTS.md")) ||
848
+ !fs.existsSync(path.join(projectRoot, "project-context")));
849
+ }
850
+ function resolveBundleDirForChat() {
851
+ const localDir = (0, config_1.getLocalBundleDir)();
852
+ if ((0, config_1.bundleLooksInitialized)(localDir))
853
+ return localDir;
854
+ const homeDir = (0, config_1.getHomeBundleDir)();
855
+ if ((0, config_1.bundleLooksInitialized)(homeDir))
856
+ return homeDir;
857
+ return null;
858
+ }
859
+ function readDisplayName(bundleDir) {
860
+ const youJsonPath = path.join(bundleDir, "you.json");
861
+ if (fs.existsSync(youJsonPath)) {
862
+ try {
863
+ const parsed = JSON.parse(fs.readFileSync(youJsonPath, "utf-8"));
864
+ if (parsed.identity?.name)
865
+ return parsed.identity.name;
866
+ }
867
+ catch {
868
+ // non-fatal
869
+ }
870
+ }
871
+ const aboutPath = path.join(bundleDir, "profile", "about.md");
872
+ if (fs.existsSync(aboutPath)) {
873
+ const content = fs.readFileSync(aboutPath, "utf-8");
874
+ const heading = content.split("\n").find((line) => line.startsWith("# "));
875
+ if (heading)
876
+ return heading.slice(2).trim();
877
+ }
878
+ return (0, config_1.readGlobalConfig)().username || "friend";
879
+ }
880
+ function getRecentProjectNames(limit = 3) {
881
+ const projectsRoot = (0, project_1.findProjectsRoot)();
882
+ if (!projectsRoot)
883
+ return [];
884
+ return fs.readdirSync(projectsRoot)
885
+ .filter((entry) => fs.existsSync(path.join(projectsRoot, entry, "project.json")))
886
+ .map((name) => {
887
+ const projectJson = path.join(projectsRoot, name, "project.json");
888
+ let updatedAt = 0;
889
+ try {
890
+ const parsed = JSON.parse(fs.readFileSync(projectJson, "utf-8"));
891
+ updatedAt = parsed.updated_at ? Date.parse(parsed.updated_at) : 0;
892
+ }
893
+ catch {
894
+ // non-fatal
895
+ }
896
+ return { name, updatedAt };
897
+ })
898
+ .sort((a, b) => b.updatedAt - a.updatedAt)
899
+ .slice(0, limit)
900
+ .map((item) => item.name);
901
+ }
902
+ async function printUpdateHint() {
903
+ const latest = await (0, update_1.checkForCliUpdate)(CURRENT_VERSION);
904
+ if (!latest)
905
+ return;
906
+ console.log(" " + chalk_1.default.yellow(`update available: ${CURRENT_VERSION} → ${latest}`));
907
+ console.log(" " + chalk_1.default.dim("refresh U with: ") + chalk_1.default.cyan("curl -fsSL https://you.md/install.sh | bash"));
908
+ console.log(" " + chalk_1.default.dim("or: ") + chalk_1.default.cyan(`npm install -g youmd@${latest}`));
909
+ console.log("");
910
+ }
911
+ async function printChatOpening(bundleDir, projectCtx) {
912
+ const ACCENT = chalk_1.default.hex("#C46A3A");
913
+ const DIM = chalk_1.default.dim;
914
+ const cfg = (0, config_1.readGlobalConfig)();
915
+ const user = cfg.username ? `@${cfg.username}` : "you";
916
+ const displayName = readDisplayName(bundleDir);
917
+ const recentProjects = getRecentProjectNames();
918
+ const launchSurface = process.env.YOUMD_LAUNCH_SURFACE;
919
+ (0, ascii_1.printYouLogo)();
920
+ let didShowPortrait = false;
921
+ if (launchSurface !== "you") {
922
+ didShowPortrait = (0, ascii_1.printSavedPortrait)(bundleDir, { maxLines: 18 });
923
+ }
924
+ if (launchSurface === "you") {
925
+ const portraitLines = await (0, ascii_1.resolvePortraitLines)(bundleDir);
926
+ didShowPortrait = portraitLines
927
+ ? (0, ascii_1.printPortraitEncounter)({
928
+ bundleDir,
929
+ displayName,
930
+ currentProject: projectCtx?.name,
931
+ recentProjects,
932
+ portraitLines,
933
+ })
934
+ : false;
935
+ }
936
+ if (didShowPortrait) {
937
+ console.log("");
938
+ console.log(" " + ACCENT("there you are.") + " " + DIM("your portrait is loaded."));
939
+ }
940
+ console.log("");
941
+ if (launchSurface === "you") {
942
+ console.log(" " + ACCENT("u is here.") + " " + DIM(`good to see you, ${displayName}.`));
943
+ }
944
+ else {
945
+ console.log(" " + ACCENT("u is here.") + " " + DIM(`good to see you, ${user}.`));
946
+ }
947
+ if (projectCtx) {
948
+ console.log(" " + DIM("current project: ") + chalk_1.default.white(projectCtx.name) + DIM(` (${projectCtx.root})`));
949
+ if (repoNeedsBootstrap(projectCtx.root)) {
950
+ console.log(" " + ACCENT("i spotted an opening.") + " " + DIM("this repo still wants AGENTS/project-context wiring."));
951
+ console.log(" " + DIM("say the word or run ") + chalk_1.default.cyan("youmd skill init-project") + DIM(" and i'll set it up."));
952
+ }
953
+ }
954
+ else {
955
+ console.log(" " + DIM("i don't see a repo context here yet, but i can still help with your identity, links, memories, and private context."));
956
+ }
957
+ if (recentProjects.length > 0) {
958
+ console.log(" " + DIM("recently active: ") + recentProjects.map((name) => chalk_1.default.cyan(name)).join(DIM(", ")));
959
+ }
960
+ console.log("");
961
+ console.log(" " + chalk_1.default.bold("you.md chat"));
962
+ console.log(" " + DIM("talk naturally. i'll update your identity, spot useful structure, and suggest next moves."));
963
+ console.log("");
964
+ }
965
+ function buildYouLaunchIntro(projectCtx, bundleDir) {
966
+ const displayName = readDisplayName(bundleDir).split(" ")[0];
967
+ const recentProjects = getRecentProjectNames();
968
+ const lines = [];
969
+ lines.push(`hi ${displayName}. i'm U — i help other agents know you.`);
970
+ if (projectCtx) {
971
+ lines.push(`i already clocked that you're inside ${projectCtx.name}.`);
972
+ if (repoNeedsBootstrap(projectCtx.root)) {
973
+ lines.push("this repo still wants cleaner agent wiring, so i can scaffold that whenever you want.");
974
+ }
975
+ }
976
+ else if (recentProjects.length > 0) {
977
+ lines.push(`recently you've been orbiting ${recentProjects.slice(0, 3).join(", ")}.`);
978
+ }
979
+ else {
980
+ lines.push("clean slate. we can still shape your identity, private context, or project structure from here.");
981
+ }
982
+ lines.push("what are we moving forward right now?");
983
+ return lines.join("\n\n");
984
+ }
842
985
  // ─── Main chat command ────────────────────────────────────────────────
843
986
  async function chatCommand() {
844
- if (!(0, config_1.localBundleExists)()) {
987
+ const bundleDir = resolveBundleDirForChat();
988
+ if (!bundleDir) {
845
989
  console.log("");
846
990
  console.log(chalk_1.default.yellow(" no .youmd/ directory found."));
847
991
  console.log(" run " +
@@ -850,17 +994,16 @@ async function chatCommand() {
850
994
  console.log("");
851
995
  return;
852
996
  }
853
- const bundleDir = (0, config_1.getLocalBundleDir)();
854
997
  const apiKey = (0, onboarding_1.getOpenRouterKey)();
855
998
  const rl = createRL();
856
999
  // Detect project context (legacy detection from config.ts)
857
- const projectCtx = (0, config_1.detectProjectContext)();
1000
+ const rawProjectCtx = (0, config_1.detectProjectContext)();
1001
+ const projectCtx = rawProjectCtx && path.resolve(rawProjectCtx.root) !== path.resolve(os.homedir())
1002
+ ? rawProjectCtx
1003
+ : null;
858
1004
  let projectContextBlock = "";
859
1005
  let activeProjectDir = null;
860
1006
  if (projectCtx) {
861
- console.log("");
862
- console.log(" " + chalk_1.default.hex("#C46A3A")("project:") + " " + chalk_1.default.white(projectCtx.name) +
863
- chalk_1.default.dim(` (${projectCtx.root})`));
864
1007
  // Try the new file-system project context first
865
1008
  const projectsRoot = (0, project_1.findProjectsRoot)();
866
1009
  if (projectsRoot) {
@@ -887,10 +1030,8 @@ async function chatCommand() {
887
1030
  projectContextBlock = `\n\n--- project context ---\n${parts.join("\n")}`;
888
1031
  }
889
1032
  }
890
- console.log("");
891
- console.log(" " + chalk_1.default.bold("you.md chat"));
892
- console.log(chalk_1.default.dim(" talk to update your profile. /help for commands."));
893
- console.log("");
1033
+ await printChatOpening(bundleDir, projectCtx);
1034
+ await printUpdateHint();
894
1035
  // Load current profile as context
895
1036
  const currentBundle = loadCurrentBundle(bundleDir);
896
1037
  // Load agent directives from you.json if available
@@ -927,6 +1068,9 @@ async function chatCommand() {
927
1068
  if (profileHint) {
928
1069
  greetingInstruction = `greet me like you remember me from last time. reference something specific from my profile (like my current focus, a project, or my background) to show you know who i am. then ask what i'd like to update. keep it to 2-3 sentences.`;
929
1070
  }
1071
+ if (projectCtx && repoNeedsBootstrap(projectCtx.root)) {
1072
+ greetingInstruction += " i am inside a repo that is missing some agent/project wiring. briefly mention that you noticed it and that you can set it up if i want, but do not derail the opening message into a long checklist.";
1073
+ }
930
1074
  const messages = [
931
1075
  { role: "system", content: CHAT_SYSTEM_PROMPT },
932
1076
  {
@@ -935,32 +1079,43 @@ async function chatCommand() {
935
1079
  },
936
1080
  ];
937
1081
  // Initial greeting from agent
938
- let response;
939
- try {
940
- response = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
941
- }
942
- catch (err) {
943
- console.log(chalk_1.default.red(` failed to connect: ${err instanceof Error ? err.message : String(err)}`));
944
- console.log(chalk_1.default.dim(" chat requires the AI service. try again later."));
945
- console.log("");
946
- rl.close();
947
- return;
1082
+ if (process.env.YOUMD_LAUNCH_SURFACE === "you") {
1083
+ const proactiveIntro = buildYouLaunchIntro(projectCtx, bundleDir);
1084
+ messages.push({ role: "assistant", content: proactiveIntro });
1085
+ printAgentMessage(proactiveIntro);
948
1086
  }
949
- messages.push({ role: "assistant", content: response });
950
- const initial = (0, onboarding_1.parseUpdatesFromResponse)(response);
951
- // Write any updates (unlikely on greeting, but handle it)
952
- if (initial.updates.length > 0) {
953
- for (const update of initial.updates) {
954
- (0, onboarding_1.writeSectionFile)(bundleDir, update.section, update.content);
955
- }
956
- console.log(chalk_1.default.cyan(` [updated: ${initial.updates.map((u) => (0, onboarding_1.sectionLabel)(u.section)).join(", ")}]`));
957
- console.log("");
1087
+ else {
1088
+ let response;
1089
+ let streamed = false;
1090
+ try {
1091
+ const result = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1092
+ response = result.text;
1093
+ streamed = result.streamed;
1094
+ }
1095
+ catch (err) {
1096
+ console.log(chalk_1.default.red(` failed to connect: ${err instanceof Error ? err.message : String(err)}`));
1097
+ console.log(chalk_1.default.dim(" chat requires the AI service. try again later."));
1098
+ console.log("");
1099
+ rl.close();
1100
+ return;
1101
+ }
1102
+ messages.push({ role: "assistant", content: response });
1103
+ const initial = (0, onboarding_1.parseUpdatesFromResponse)(response);
1104
+ // Write any updates (unlikely on greeting, but handle it)
1105
+ if (initial.updates.length > 0) {
1106
+ for (const update of initial.updates) {
1107
+ (0, onboarding_1.writeSectionFile)(bundleDir, update.section, update.content);
1108
+ }
1109
+ console.log(chalk_1.default.cyan(` [updated: ${initial.updates.map((u) => (0, onboarding_1.sectionLabel)(u.section)).join(", ")}]`));
1110
+ console.log("");
1111
+ }
1112
+ if (!streamed) {
1113
+ printAgentMessage(initial.display);
1114
+ }
958
1115
  }
959
- // Only print via rich renderer if we didn't stream (streaming already wrote output)
960
- // But we still need to display parsed output for non-streamed fallback
961
- // Since streaming writes raw text, print formatted version for updates parsing
962
- printAgentMessage(initial.display);
963
1116
  // ── Conversation loop ──────────────────────────────────────────────
1117
+ let response = "";
1118
+ let streamed = false;
964
1119
  while (true) {
965
1120
  const userInput = await ask(rl, chalk_1.default.green(" > ") + "");
966
1121
  if (!userInput)
@@ -1093,7 +1248,9 @@ async function chatCommand() {
1093
1248
  continue;
1094
1249
  // After research, get an LLM response with the injected context
1095
1250
  try {
1096
- response = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1251
+ const result = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1252
+ response = result.text;
1253
+ streamed = result.streamed;
1097
1254
  }
1098
1255
  catch (err) {
1099
1256
  console.log(chalk_1.default.red(` AI error: ${err instanceof Error ? err.message : String(err)}`));
@@ -1111,7 +1268,9 @@ async function chatCommand() {
1111
1268
  console.log(chalk_1.default.cyan(` [updated: ${researchParsed.updates.map((u) => (0, onboarding_1.sectionLabel)(u.section)).join(", ")}]`));
1112
1269
  console.log("");
1113
1270
  }
1114
- printAgentMessage(researchParsed.display);
1271
+ if (!streamed) {
1272
+ printAgentMessage(researchParsed.display);
1273
+ }
1115
1274
  continue;
1116
1275
  }
1117
1276
  if (lower === "/rebuild") {
@@ -1147,7 +1306,9 @@ async function chatCommand() {
1147
1306
  }
1148
1307
  }
1149
1308
  try {
1150
- response = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1309
+ const result = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1310
+ response = result.text;
1311
+ streamed = result.streamed;
1151
1312
  }
1152
1313
  catch (err) {
1153
1314
  console.log(chalk_1.default.red(` ${err instanceof Error ? err.message : "failed"}`));
@@ -1155,7 +1316,9 @@ async function chatCommand() {
1155
1316
  continue;
1156
1317
  }
1157
1318
  messages.push({ role: "assistant", content: response });
1158
- printAgentMessage((0, onboarding_1.parseUpdatesFromResponse)(response).display);
1319
+ if (!streamed) {
1320
+ printAgentMessage((0, onboarding_1.parseUpdatesFromResponse)(response).display);
1321
+ }
1159
1322
  continue;
1160
1323
  }
1161
1324
  // ── Detect dragged/pasted file paths ──
@@ -1170,7 +1333,9 @@ async function chatCommand() {
1170
1333
  content: `[USER DROPPED IMAGE: ${path.basename(detectedFile)}]\nthe user dropped an image file into the chat.\n![${path.basename(detectedFile)}](${dataUrl})`,
1171
1334
  });
1172
1335
  try {
1173
- response = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1336
+ const result = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1337
+ response = result.text;
1338
+ streamed = result.streamed;
1174
1339
  }
1175
1340
  catch (err) {
1176
1341
  console.log(chalk_1.default.red(` ${err instanceof Error ? err.message : "failed"}`));
@@ -1178,7 +1343,9 @@ async function chatCommand() {
1178
1343
  continue;
1179
1344
  }
1180
1345
  messages.push({ role: "assistant", content: response });
1181
- printAgentMessage((0, onboarding_1.parseUpdatesFromResponse)(response).display);
1346
+ if (!streamed) {
1347
+ printAgentMessage((0, onboarding_1.parseUpdatesFromResponse)(response).display);
1348
+ }
1182
1349
  continue;
1183
1350
  }
1184
1351
  }
@@ -1192,7 +1359,9 @@ async function chatCommand() {
1192
1359
  content: `[USER DROPPED FILE: ${path.basename(detectedFile)}]\n\`\`\`\n${text.slice(0, 10000)}\n\`\`\`\n\nreview this file and suggest how it relates to my identity or profile.`,
1193
1360
  });
1194
1361
  try {
1195
- response = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1362
+ const result = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1363
+ response = result.text;
1364
+ streamed = result.streamed;
1196
1365
  }
1197
1366
  catch (err) {
1198
1367
  console.log(chalk_1.default.red(` ${err instanceof Error ? err.message : "failed"}`));
@@ -1200,7 +1369,9 @@ async function chatCommand() {
1200
1369
  continue;
1201
1370
  }
1202
1371
  messages.push({ role: "assistant", content: response });
1203
- printAgentMessage((0, onboarding_1.parseUpdatesFromResponse)(response).display);
1372
+ if (!streamed) {
1373
+ printAgentMessage((0, onboarding_1.parseUpdatesFromResponse)(response).display);
1374
+ }
1204
1375
  continue;
1205
1376
  }
1206
1377
  }
@@ -1236,7 +1407,9 @@ async function chatCommand() {
1236
1407
  }
1237
1408
  }
1238
1409
  try {
1239
- response = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1410
+ const result = await callLLMWithStreaming(apiKey, messages, (0, onboarding_1.randomThinking)());
1411
+ response = result.text;
1412
+ streamed = result.streamed;
1240
1413
  }
1241
1414
  catch (err) {
1242
1415
  console.log(chalk_1.default.red(` ${err instanceof Error ? err.message : "failed"}`));
@@ -1348,7 +1521,9 @@ async function chatCommand() {
1348
1521
  // non-fatal
1349
1522
  }
1350
1523
  }
1351
- printAgentMessage(parsed.display);
1524
+ if (!streamed) {
1525
+ printAgentMessage(parsed.display);
1526
+ }
1352
1527
  }
1353
1528
  rl.close();
1354
1529
  }