youmd 0.13.11 → 0.13.14

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 (74) hide show
  1. package/dist/commands/chat.d.ts +5 -0
  2. package/dist/commands/chat.d.ts.map +1 -1
  3. package/dist/commands/chat.js +137 -21
  4. package/dist/commands/chat.js.map +1 -1
  5. package/dist/commands/doctor.d.ts +33 -2
  6. package/dist/commands/doctor.d.ts.map +1 -1
  7. package/dist/commands/doctor.js +69 -3
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/founder.d.ts.map +1 -1
  10. package/dist/commands/founder.js +47 -3
  11. package/dist/commands/founder.js.map +1 -1
  12. package/dist/commands/push.d.ts +4 -1
  13. package/dist/commands/push.d.ts.map +1 -1
  14. package/dist/commands/push.js +30 -6
  15. package/dist/commands/push.js.map +1 -1
  16. package/dist/commands/stack.d.ts.map +1 -1
  17. package/dist/commands/stack.js +7 -2
  18. package/dist/commands/stack.js.map +1 -1
  19. package/dist/commands/sync.d.ts +5 -0
  20. package/dist/commands/sync.d.ts.map +1 -1
  21. package/dist/commands/sync.js +120 -19
  22. package/dist/commands/sync.js.map +1 -1
  23. package/dist/commands/tasks.d.ts.map +1 -1
  24. package/dist/commands/tasks.js +24 -1
  25. package/dist/commands/tasks.js.map +1 -1
  26. package/dist/generated/capability-registry.d.ts +32 -0
  27. package/dist/generated/capability-registry.d.ts.map +1 -1
  28. package/dist/generated/capability-registry.js +36 -0
  29. package/dist/generated/capability-registry.js.map +1 -1
  30. package/dist/index.js +4 -2
  31. package/dist/index.js.map +1 -1
  32. package/dist/lib/api-failure.d.ts +19 -0
  33. package/dist/lib/api-failure.d.ts.map +1 -0
  34. package/dist/lib/api-failure.js +67 -0
  35. package/dist/lib/api-failure.js.map +1 -0
  36. package/dist/lib/api.d.ts +30 -0
  37. package/dist/lib/api.d.ts.map +1 -1
  38. package/dist/lib/api.js +19 -0
  39. package/dist/lib/api.js.map +1 -1
  40. package/dist/lib/chat-errors.d.ts +47 -0
  41. package/dist/lib/chat-errors.d.ts.map +1 -0
  42. package/dist/lib/chat-errors.js +151 -0
  43. package/dist/lib/chat-errors.js.map +1 -0
  44. package/dist/lib/daemon.d.ts +13 -1
  45. package/dist/lib/daemon.d.ts.map +1 -1
  46. package/dist/lib/daemon.js +41 -2
  47. package/dist/lib/daemon.js.map +1 -1
  48. package/dist/lib/onboarding.d.ts.map +1 -1
  49. package/dist/lib/onboarding.js +53 -7
  50. package/dist/lib/onboarding.js.map +1 -1
  51. package/dist/lib/stream.d.ts.map +1 -1
  52. package/dist/lib/stream.js +2 -1
  53. package/dist/lib/stream.js.map +1 -1
  54. package/dist/lib/workspace-brief.d.ts +91 -0
  55. package/dist/lib/workspace-brief.d.ts.map +1 -0
  56. package/dist/lib/workspace-brief.js +402 -0
  57. package/dist/lib/workspace-brief.js.map +1 -0
  58. package/dist/mcp/registry.d.ts +1 -0
  59. package/dist/mcp/registry.d.ts.map +1 -1
  60. package/dist/mcp/registry.js +36 -0
  61. package/dist/mcp/registry.js.map +1 -1
  62. package/dist/mcp/server.d.ts +11 -0
  63. package/dist/mcp/server.d.ts.map +1 -1
  64. package/dist/mcp/server.js +126 -22
  65. package/dist/mcp/server.js.map +1 -1
  66. package/native/prebuilds/darwin-universal/youmd_safe_open.node +0 -0
  67. package/package.json +1 -1
  68. package/scripts/skillstack-sync/com.you.context-sync.plist +6 -6
  69. package/scripts/skillstack-sync/com.you.identity-sync.plist +6 -6
  70. package/scripts/skillstack-sync/com.you.orchestrator-watch.plist +7 -6
  71. package/scripts/skillstack-sync/com.you.realtime-sync.plist +7 -6
  72. package/scripts/skillstack-sync/com.you.skillstack-sync.plist +6 -6
  73. package/scripts/skillstack-sync/context-sync.sh +46 -6
  74. package/scripts/skillstack-sync/install-daemons.sh +42 -1
@@ -1,2 +1,7 @@
1
+ /**
2
+ * Render a failed You Agent turn with the real reason. Billing / key
3
+ * problems get the fix line; transient outages get "try again".
4
+ */
5
+ export declare function printChatFailure(err: unknown): void;
1
6
  export declare function chatCommand(): Promise<void>;
2
7
  //# sourceMappingURL=chat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AA+gEA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAqlBjD"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AA2sBA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAUnD;AAy7CD,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAklBjD"}
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.printChatFailure = printChatFailure;
39
40
  exports.chatCommand = chatCommand;
40
41
  const readline = __importStar(require("readline"));
41
42
  const fs = __importStar(require("fs"));
@@ -57,6 +58,8 @@ const version_1 = require("../lib/version");
57
58
  const machine_bootstrap_prompt_1 = require("../lib/machine-bootstrap-prompt");
58
59
  // ─── URL Detection + Scraping (mirrors web useYouAgent) ──────────────
59
60
  const config_2 = require("../lib/config");
61
+ const chat_errors_1 = require("../lib/chat-errors");
62
+ const workspace_brief_1 = require("../lib/workspace-brief");
60
63
  const CONVEX_SITE_URL = (0, config_2.getConvexSiteUrl)();
61
64
  const STREAM_URL = `${CONVEX_SITE_URL}/api/v1/chat/stream`;
62
65
  const CURRENT_VERSION = (0, version_1.readCliVersion)();
@@ -401,6 +404,10 @@ your job:
401
404
 
402
405
  rules: each section starts with YAML frontmatter. real markdown, not placeholders. output FULL section content each time. be substantive — write from what you actually know.
403
406
 
407
+ --- fresh workspace block ---
408
+
409
+ the user message may carry a "fresh local workspace" block: git branch, recent commits, uncommitted files, current-state and open items per repo, plus machine-level agent directives. it was read from disk seconds ago. treat it as ground truth over anything in the stored profile, now.md, or private projects. when the stored profile says one thing and the fresh block says another, say so plainly and offer to update the stale section. reference real commit subjects and file names, never invent activity. when asked what the user is working on, answer from this block first.
410
+
404
411
  --- project context updates ---
405
412
 
406
413
  if the user is working in a project (you'll see a [PROJECT CONTEXT] block), you can update project files. when you learn something about the project — a decision made, a task completed, a feature shipped, a new requirement — output:
@@ -595,6 +602,23 @@ async function handlePublish(bundleDir) {
595
602
  console.log("");
596
603
  }
597
604
  }
605
+ /**
606
+ * Render a failed You Agent turn with the real reason. Billing / key
607
+ * problems get the fix line; transient outages get "try again".
608
+ */
609
+ function printChatFailure(err) {
610
+ const [headline, ...why] = (0, chat_errors_1.explainChatError)(err);
611
+ console.log(chalk_1.default.red(` ${headline}`));
612
+ for (const line of why)
613
+ console.log(chalk_1.default.dim(` ${line}`));
614
+ if (err instanceof chat_errors_1.ChatServiceError && err.operatorActionable) {
615
+ console.log(chalk_1.default.dim(" fix that, then send your message again. ") + chalk_1.default.cyan("you doctor") + chalk_1.default.dim(" shows live provider status."));
616
+ }
617
+ else {
618
+ console.log(chalk_1.default.dim(" try again. ") + chalk_1.default.cyan("you doctor") + chalk_1.default.dim(" shows live provider status."));
619
+ }
620
+ console.log("");
621
+ }
598
622
  function handleRebuild(bundleDir) {
599
623
  console.log("");
600
624
  const result = (0, compiler_1.compileBundle)(bundleDir);
@@ -812,6 +836,32 @@ async function printUpdateHint() {
812
836
  console.log(" " + chalk_1.default.dim("or: ") + chalk_1.default.cyan(`npm install -g youmd@${latest}`));
813
837
  console.log("");
814
838
  }
839
+ function insightFromBrief(b) {
840
+ return {
841
+ name: b.name,
842
+ slug: b.name,
843
+ projectDir: b.projectDir,
844
+ updatedAt: b.freshness || Date.now(),
845
+ signals: [b.headline],
846
+ summary: `${b.name}: ${b.headline}`,
847
+ suggestedCommand: `cd ${b.projectDir} && you`,
848
+ };
849
+ }
850
+ /**
851
+ * Build the fresh workspace brief for the launch: the current repo first,
852
+ * then the most recently touched workspace projects. Read-only and bounded.
853
+ */
854
+ function buildLaunchBrief(projectCtx, limit = 6) {
855
+ const candidates = [];
856
+ if (projectCtx)
857
+ candidates.push({ name: projectCtx.name, projectDir: projectCtx.root });
858
+ for (const item of [...scanRecentWorkspaceProjects(limit * 2), ...(0, project_1.getRecentProjectInsights)(process.cwd(), limit * 2)]) {
859
+ if (fs.existsSync(path.join(item.projectDir, ".git")) || fs.existsSync(path.join(item.projectDir, "project-context"))) {
860
+ candidates.push({ name: item.name, projectDir: item.projectDir });
861
+ }
862
+ }
863
+ return (0, workspace_brief_1.buildWorkspaceBrief)(candidates, { maxProjects: limit });
864
+ }
815
865
  function countMarkdownFiles(dir) {
816
866
  if (!fs.existsSync(dir))
817
867
  return 0;
@@ -1022,6 +1072,27 @@ async function runYouLaunchInvestigation(bundleDir, projectCtx, recentProjects)
1022
1072
  }
1023
1073
  const homeSignals = collectHomeAgentSignals();
1024
1074
  findings.push(...homeSignals.slice(0, 2));
1075
+ // Ground everything in what git says is actually moving. This replaces
1076
+ // "already looks pretty well-shaped" and first-TODO-line guesses with the
1077
+ // latest commit, branch, and uncommitted work of the freshest repo.
1078
+ let brief;
1079
+ try {
1080
+ spinner.update("reading recent commits across your repos");
1081
+ brief = buildLaunchBrief(projectCtx);
1082
+ if (brief.strongestProject) {
1083
+ const top = brief.strongestProject;
1084
+ findings.unshift(`freshest repo: ${top.name} — ${top.headline}.`);
1085
+ const inProjectWithBootstrapNeed = projectCtx && repoNeedsBootstrap(projectCtx.root);
1086
+ if (!inProjectWithBootstrapNeed && brief.strongestMove) {
1087
+ strongestMove = brief.strongestMove;
1088
+ strongestProject = insightFromBrief(top);
1089
+ strongestCommand = `cd ${top.projectDir} && you`;
1090
+ }
1091
+ }
1092
+ }
1093
+ catch {
1094
+ // brief is best-effort; keep the marker-based findings
1095
+ }
1025
1096
  spinner.stop("looked through local context");
1026
1097
  return {
1027
1098
  findings: findings.slice(0, 3),
@@ -1029,6 +1100,7 @@ async function runYouLaunchInvestigation(bundleDir, projectCtx, recentProjects)
1029
1100
  strongestCommand,
1030
1101
  strongestProject,
1031
1102
  recentProjects: recentProjects.slice(0, 3),
1103
+ brief,
1032
1104
  };
1033
1105
  }
1034
1106
  finally {
@@ -1100,9 +1172,15 @@ function buildYouLaunchIntro(projectCtx, bundleDir, investigation) {
1100
1172
  }
1101
1173
  else if (recentProjects.length > 0) {
1102
1174
  lines.push(`recent orbit: ${recentProjects.slice(0, 3).join(", ")}.`);
1103
- const topOpportunity = (0, project_1.getTopProjectOpportunity)(recentInsights);
1104
- if (topOpportunity) {
1105
- lines.push(`strongest opening: ${topOpportunity.summary}`);
1175
+ const briefTop = investigation.brief?.strongestProject;
1176
+ if (briefTop) {
1177
+ lines.push(`freshest: ${briefTop.name} — ${briefTop.headline}.`);
1178
+ }
1179
+ else {
1180
+ const topOpportunity = (0, project_1.getTopProjectOpportunity)(recentInsights);
1181
+ if (topOpportunity) {
1182
+ lines.push(`strongest opening: ${topOpportunity.summary}`);
1183
+ }
1106
1184
  }
1107
1185
  }
1108
1186
  else {
@@ -1117,10 +1195,13 @@ function buildYouLaunchIntro(projectCtx, bundleDir, investigation) {
1117
1195
  : null)
1118
1196
  || (0, project_1.getTopProjectOpportunity)(recentInsights)?.summary
1119
1197
  || null;
1120
- if (strongestMove) {
1198
+ if (strongestMove && !lines.some((line) => line.includes(strongestMove))) {
1121
1199
  lines.push(`next strongest move: ${strongestMove}`);
1122
1200
  lines.push("say \"start there\" and i'll take it.");
1123
1201
  }
1202
+ else if (strongestMove) {
1203
+ lines.push("say \"start there\" and i'll pick that thread up.");
1204
+ }
1124
1205
  else {
1125
1206
  lines.push("point me at the next thing and i'll move first.");
1126
1207
  }
@@ -1520,9 +1601,12 @@ function formatProjectReadToolResult(project) {
1520
1601
  .slice(0, 5);
1521
1602
  const managedContext = (0, project_1.readProjectContext)(project.projectDir);
1522
1603
  const actionItems = extractProjectActionItems(project.projectDir);
1523
- const recommendedMove = actionItems.length > 0
1524
- ? actionItems[0]
1525
- : `tighten ${project.name}'s current-state and TODO docs from actual repo context.`;
1604
+ const brief = (0, workspace_brief_1.buildProjectBrief)({ name: project.name, projectDir: project.projectDir });
1605
+ const recommendedMove = brief.activity.commits.length > 0 || brief.currentState || brief.openItems.length > 0
1606
+ ? (0, workspace_brief_1.buildStrongestMove)(brief)
1607
+ : actionItems.length > 0
1608
+ ? actionItems[0]
1609
+ : `tighten ${project.name}'s current-state and TODO docs from actual repo context.`;
1526
1610
  return [
1527
1611
  "tool: read_project_context",
1528
1612
  "status: ok",
@@ -1532,6 +1616,9 @@ function formatProjectReadToolResult(project) {
1532
1616
  managedContext ? `managed_project_context: ${managedContext.meta.name}` : "managed_project_context: none",
1533
1617
  actionItems.length > 0 ? `action_items: ${actionItems.join(" | ")}` : "action_items: none found in TODO/current-state files",
1534
1618
  "",
1619
+ "--- git activity (read just now) ---",
1620
+ (0, workspace_brief_1.formatProjectActivityForTool)(brief),
1621
+ "",
1535
1622
  snippets.length > 0 ? snippets.join("\n\n---\n\n") : "no readable project-context or agent entrypoint files found yet.",
1536
1623
  "",
1537
1624
  `recommended_next_move: ${recommendedMove}`,
@@ -1737,7 +1824,19 @@ async function handleLocalChatIntent(args) {
1737
1824
  `next strongest move: ${recommended || "continue from the local result above."}`,
1738
1825
  ].join("\n");
1739
1826
  };
1740
- const recordLocalToolResult = (toolResult) => {
1827
+ // The tool result is ground truth; the model turns it into an actual read
1828
+ // of the project. Before this, the CLI printed a canned template and never
1829
+ // asked the model at all, so "start there" twice produced the same lines.
1830
+ const recordLocalToolResult = async (toolResult, toolName) => {
1831
+ const projectLine = toolResult.match(/^project:\s*(.+)$/m)?.[1]?.trim()
1832
+ ?? toolResult.match(/^recommended_next_project:\s*(.+)$/m)?.[1]?.trim()
1833
+ ?? "";
1834
+ const toolKey = `${toolName}:${projectLine}`;
1835
+ const repeats = args.launchInvestigation.lastToolKey === toolKey
1836
+ ? (args.launchInvestigation.lastToolRepeats ?? 0) + 1
1837
+ : 0;
1838
+ args.launchInvestigation.lastToolKey = toolKey;
1839
+ args.launchInvestigation.lastToolRepeats = repeats;
1741
1840
  args.messages.push({ role: "user", content: args.userInput });
1742
1841
  args.messages.push({
1743
1842
  role: "user",
@@ -1751,14 +1850,31 @@ async function handleLocalChatIntent(args) {
1751
1850
  "Do not ask the user what the local project is if the tool result already names it.",
1752
1851
  "State what the local host actually found, make one concrete recommendation, and end with the exact phrase `next strongest move: ...`.",
1753
1852
  "For workspace scans, reuse recommended_next_move as the final next strongest move. The supported follow-up command is `start there`; do not invent commands like `open PROJECT`.",
1754
- "For read_project_context results, summarize what you actually saw and recommend the next concrete local tool action.",
1853
+ "For read_project_context results: lead with what the git activity says is in motion (latest commits, branch, uncommitted work), then what the agent docs and current-state say, then one sharp observation about drift between the stored profile and this reality. Name real commit subjects and files. Do not list every file you were shown.",
1755
1854
  "For sync_identity results, state whether local compile or remote publish happened.",
1756
- "Do not end with a question. Keep it under 8 lines. No generic help-desk closer.",
1757
- ].join("\n"),
1855
+ repeats > 0
1856
+ ? `The user already saw this ${toolName} result ${repeats} time${repeats === 1 ? "" : "s"} in this session. Do not repeat the file list or the same recommendation. Go deeper: what changed most recently, what is half-finished, what the next concrete action is, and offer to update the stale profile or private project entry if the stored picture is behind.`
1857
+ : "",
1858
+ "Do not end with a question. Keep it under 10 lines. No generic help-desk closer.",
1859
+ ].filter(Boolean).join("\n"),
1758
1860
  });
1759
- const summary = summarizeLocalToolResult(toolResult);
1760
- args.messages.push({ role: "assistant", content: summary });
1761
- printAgentMessage(summary);
1861
+ try {
1862
+ const result = await callLLMWithStreaming(args.apiKey, args.messages, repeats > 0 ? "reading it properly this time" : "making sense of what i found");
1863
+ const parsed = (0, onboarding_1.parseUpdatesFromResponse)(result.text);
1864
+ args.messages.push({ role: "assistant", content: result.text });
1865
+ if (!result.streamed)
1866
+ printAgentMessage(parsed.display);
1867
+ }
1868
+ catch (err) {
1869
+ // Model unavailable: fall back to the deterministic summary so the
1870
+ // user still sees what the host found, plus the real reason.
1871
+ const summary = summarizeLocalToolResult(toolResult);
1872
+ args.messages.push({ role: "assistant", content: summary });
1873
+ printAgentMessage(summary);
1874
+ const [headline] = (0, chat_errors_1.explainChatError)(err);
1875
+ console.log(chalk_1.default.dim(` (model unavailable — ${headline})`));
1876
+ console.log("");
1877
+ }
1762
1878
  };
1763
1879
  if (isLocalToolLoopCandidate(args.userInput)) {
1764
1880
  const routeSpinner = new render_1.BrailleSpinner("choosing local tool");
@@ -1780,7 +1896,7 @@ async function handleLocalChatIntent(args) {
1780
1896
  const toolResult = await executeLocalHostTool(toolCall, args);
1781
1897
  const statusLine = toolResult.match(/^status: (.+)$/m)?.[1] || "done";
1782
1898
  spinner.stop(statusLine);
1783
- recordLocalToolResult(toolResult);
1899
+ await recordLocalToolResult(toolResult, toolCall.tool);
1784
1900
  }
1785
1901
  catch (err) {
1786
1902
  const message = err instanceof Error ? err.message : "local tool failed";
@@ -1890,11 +2006,14 @@ async function chatCommand() {
1890
2006
  if (projectCtx && repoNeedsBootstrap(projectCtx.root)) {
1891
2007
  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.";
1892
2008
  }
2009
+ const workspaceBriefBlock = launchInvestigation.brief?.text
2010
+ ? `\n\n${launchInvestigation.brief.text}`
2011
+ : "";
1893
2012
  const messages = [
1894
2013
  { role: "system", content: CHAT_SYSTEM_PROMPT },
1895
2014
  {
1896
2015
  role: "user",
1897
- content: `here is my current identity bundle:\n\n${currentBundle}${directivesContext}${projectContextBlock}\n\n${greetingInstruction}`,
2016
+ content: `here is my current identity bundle:\n\n${currentBundle}${directivesContext}${projectContextBlock}${workspaceBriefBlock}\n\n${greetingInstruction}`,
1898
2017
  },
1899
2018
  ];
1900
2019
  // Initial greeting is local and action-aware. The remote model should not invent
@@ -2047,8 +2166,7 @@ async function chatCommand() {
2047
2166
  streamed = result.streamed;
2048
2167
  }
2049
2168
  catch (err) {
2050
- console.log(chalk_1.default.red(` AI error: ${err instanceof Error ? err.message : String(err)}`));
2051
- console.log(chalk_1.default.dim(" try again."));
2169
+ printChatFailure(err);
2052
2170
  console.log("");
2053
2171
  messages.pop();
2054
2172
  continue;
@@ -2215,9 +2333,7 @@ async function chatCommand() {
2215
2333
  streamed = result.streamed;
2216
2334
  }
2217
2335
  catch (err) {
2218
- console.log(chalk_1.default.red(` ${err instanceof Error ? err.message : "failed"}`));
2219
- console.log(chalk_1.default.dim(" try again."));
2220
- console.log("");
2336
+ printChatFailure(err);
2221
2337
  messages.pop();
2222
2338
  if (newSources.length > 0)
2223
2339
  messages.pop(); // remove scrape context too