youmd 0.8.0 → 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/commands/chat.d.ts.map +1 -1
  2. package/dist/commands/chat.js +20 -2
  3. package/dist/commands/chat.js.map +1 -1
  4. package/dist/commands/env.d.ts +21 -4
  5. package/dist/commands/env.d.ts.map +1 -1
  6. package/dist/commands/env.js +250 -24
  7. package/dist/commands/env.js.map +1 -1
  8. package/dist/commands/login.d.ts +1 -0
  9. package/dist/commands/login.d.ts.map +1 -1
  10. package/dist/commands/login.js +20 -4
  11. package/dist/commands/login.js.map +1 -1
  12. package/dist/commands/machine.d.ts +28 -1
  13. package/dist/commands/machine.d.ts.map +1 -1
  14. package/dist/commands/machine.js +529 -4
  15. package/dist/commands/machine.js.map +1 -1
  16. package/dist/commands/project.d.ts.map +1 -1
  17. package/dist/commands/project.js +862 -17
  18. package/dist/commands/project.js.map +1 -1
  19. package/dist/commands/prompts.d.ts +19 -0
  20. package/dist/commands/prompts.d.ts.map +1 -1
  21. package/dist/commands/prompts.js +304 -1
  22. package/dist/commands/prompts.js.map +1 -1
  23. package/dist/commands/pull.d.ts.map +1 -1
  24. package/dist/commands/pull.js +28 -8
  25. package/dist/commands/pull.js.map +1 -1
  26. package/dist/commands/push.d.ts +3 -1
  27. package/dist/commands/push.d.ts.map +1 -1
  28. package/dist/commands/push.js +26 -12
  29. package/dist/commands/push.js.map +1 -1
  30. package/dist/commands/skill.d.ts.map +1 -1
  31. package/dist/commands/skill.js +19 -15
  32. package/dist/commands/skill.js.map +1 -1
  33. package/dist/commands/stack.d.ts.map +1 -1
  34. package/dist/commands/stack.js +20 -17
  35. package/dist/commands/stack.js.map +1 -1
  36. package/dist/commands/status.d.ts.map +1 -1
  37. package/dist/commands/status.js +38 -0
  38. package/dist/commands/status.js.map +1 -1
  39. package/dist/commands/sync.d.ts +2 -0
  40. package/dist/commands/sync.d.ts.map +1 -1
  41. package/dist/commands/sync.js +235 -2
  42. package/dist/commands/sync.js.map +1 -1
  43. package/dist/index.js +157 -31
  44. package/dist/index.js.map +1 -1
  45. package/dist/lib/api.d.ts +398 -0
  46. package/dist/lib/api.d.ts.map +1 -1
  47. package/dist/lib/api.js +104 -0
  48. package/dist/lib/api.js.map +1 -1
  49. package/dist/lib/daemon.d.ts +26 -0
  50. package/dist/lib/daemon.d.ts.map +1 -0
  51. package/dist/lib/daemon.js +187 -0
  52. package/dist/lib/daemon.js.map +1 -0
  53. package/dist/lib/first-run.d.ts +1 -1
  54. package/dist/lib/first-run.d.ts.map +1 -1
  55. package/dist/lib/first-run.js +8 -0
  56. package/dist/lib/first-run.js.map +1 -1
  57. package/dist/lib/machine-bootstrap-prompt.d.ts +17 -0
  58. package/dist/lib/machine-bootstrap-prompt.d.ts.map +1 -0
  59. package/dist/lib/machine-bootstrap-prompt.js +426 -0
  60. package/dist/lib/machine-bootstrap-prompt.js.map +1 -0
  61. package/dist/lib/machine-projects.d.ts +71 -0
  62. package/dist/lib/machine-projects.d.ts.map +1 -0
  63. package/dist/lib/machine-projects.js +510 -0
  64. package/dist/lib/machine-projects.js.map +1 -0
  65. package/dist/lib/machine-verify.d.ts +175 -0
  66. package/dist/lib/machine-verify.d.ts.map +1 -0
  67. package/dist/lib/machine-verify.js +684 -0
  68. package/dist/lib/machine-verify.js.map +1 -0
  69. package/dist/lib/portfolio-audit.d.ts +34 -0
  70. package/dist/lib/portfolio-audit.d.ts.map +1 -0
  71. package/dist/lib/portfolio-audit.js +254 -0
  72. package/dist/lib/portfolio-audit.js.map +1 -0
  73. package/dist/lib/portfolio-graph.d.ts +104 -0
  74. package/dist/lib/portfolio-graph.d.ts.map +1 -0
  75. package/dist/lib/portfolio-graph.js +281 -0
  76. package/dist/lib/portfolio-graph.js.map +1 -0
  77. package/dist/lib/project-strategy.d.ts +45 -0
  78. package/dist/lib/project-strategy.d.ts.map +1 -0
  79. package/dist/lib/project-strategy.js +234 -0
  80. package/dist/lib/project-strategy.js.map +1 -0
  81. package/dist/lib/realtime-sync.d.ts +128 -0
  82. package/dist/lib/realtime-sync.d.ts.map +1 -0
  83. package/dist/lib/realtime-sync.js +215 -0
  84. package/dist/lib/realtime-sync.js.map +1 -0
  85. package/dist/lib/reusable-patterns.d.ts +30 -0
  86. package/dist/lib/reusable-patterns.d.ts.map +1 -0
  87. package/dist/lib/reusable-patterns.js +375 -0
  88. package/dist/lib/reusable-patterns.js.map +1 -0
  89. package/dist/lib/skill-catalog.d.ts.map +1 -1
  90. package/dist/lib/skill-catalog.js +20 -0
  91. package/dist/lib/skill-catalog.js.map +1 -1
  92. package/dist/lib/skills.d.ts +1 -0
  93. package/dist/lib/skills.d.ts.map +1 -1
  94. package/dist/lib/skills.js +43 -13
  95. package/dist/lib/skills.js.map +1 -1
  96. package/dist/lib/version.d.ts +2 -0
  97. package/dist/lib/version.d.ts.map +1 -0
  98. package/dist/lib/version.js +63 -0
  99. package/dist/lib/version.js.map +1 -0
  100. package/dist/mcp/registry.d.ts +5 -1
  101. package/dist/mcp/registry.d.ts.map +1 -1
  102. package/dist/mcp/registry.js +426 -2
  103. package/dist/mcp/registry.js.map +1 -1
  104. package/dist/mcp/server.d.ts +2 -0
  105. package/dist/mcp/server.d.ts.map +1 -1
  106. package/dist/mcp/server.js +50 -5
  107. package/dist/mcp/server.js.map +1 -1
  108. package/package.json +2 -1
  109. package/scripts/env-vault/README.md +21 -2
  110. package/scripts/env-vault/backup.sh +24 -1
  111. package/scripts/env-vault/restore.sh +123 -8
  112. package/scripts/skillstack-sync/README.md +35 -10
  113. package/scripts/skillstack-sync/bootstrap-new-mac.sh +31 -0
  114. package/scripts/skillstack-sync/capture-agent-config.sh +1 -0
  115. package/scripts/skillstack-sync/com.youmd.context-sync.plist +38 -0
  116. package/scripts/skillstack-sync/com.youmd.identity-sync.plist +3 -1
  117. package/scripts/skillstack-sync/com.youmd.realtime-sync.plist +35 -0
  118. package/scripts/skillstack-sync/com.youmd.skillstack-sync.plist +1 -1
  119. package/scripts/skillstack-sync/context-sync.sh +69 -1
  120. package/scripts/skillstack-sync/install-daemons.sh +10 -3
  121. package/skills/machine-bootstrap.md +312 -0
@@ -40,12 +40,18 @@ exports.projectCommand = projectCommand;
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const readline = __importStar(require("readline"));
43
+ const child_process_1 = require("child_process");
43
44
  const chalk_1 = __importDefault(require("chalk"));
44
45
  const project_1 = require("../lib/project");
45
46
  const project_log_1 = require("./project-log");
46
47
  const config_1 = require("../lib/config");
48
+ const api_1 = require("../lib/api");
47
49
  const projectContext_1 = require("../lib/projectContext");
50
+ const portfolio_audit_1 = require("../lib/portfolio-audit");
51
+ const reusable_patterns_1 = require("../lib/reusable-patterns");
52
+ const project_strategy_1 = require("../lib/project-strategy");
48
53
  // ─── Helpers ──────────────────────────────────────────────────────────
54
+ const ACCENT = chalk_1.default.hex("#C46A3A");
49
55
  function createRL() {
50
56
  return readline.createInterface({
51
57
  input: process.stdin,
@@ -385,9 +391,845 @@ async function editFile(args) {
385
391
  console.log(` ${fullPath}`);
386
392
  console.log("");
387
393
  }
394
+ function readFlagValue(args, flag) {
395
+ const inline = args.find((arg) => arg.startsWith(`${flag}=`));
396
+ if (inline)
397
+ return inline.slice(flag.length + 1);
398
+ const index = args.indexOf(flag);
399
+ if (index === -1)
400
+ return undefined;
401
+ return args[index + 1];
402
+ }
403
+ function hasFlag(args, flag) {
404
+ return args.includes(flag) || args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
405
+ }
406
+ function readAllFlagValues(args, flag) {
407
+ const values = [];
408
+ for (let i = 0; i < args.length; i += 1) {
409
+ const arg = args[i];
410
+ if (arg === flag && args[i + 1])
411
+ values.push(args[i + 1]);
412
+ if (arg.startsWith(`${flag}=`))
413
+ values.push(arg.slice(flag.length + 1));
414
+ }
415
+ return values;
416
+ }
417
+ function nonFlagArgs(args, valueFlags) {
418
+ const out = [];
419
+ for (let i = 0; i < args.length; i += 1) {
420
+ const arg = args[i];
421
+ const inlineValueFlag = valueFlags.some((flag) => arg.startsWith(`${flag}=`));
422
+ if (inlineValueFlag)
423
+ continue;
424
+ if (valueFlags.includes(arg)) {
425
+ i += 1;
426
+ continue;
427
+ }
428
+ if (arg.startsWith("--"))
429
+ continue;
430
+ out.push(arg);
431
+ }
432
+ return out;
433
+ }
434
+ function csvFlag(args, flag) {
435
+ const value = readFlagValue(args, flag);
436
+ return value ? value.split(",").map((item) => item.trim()).filter(Boolean) : [];
437
+ }
438
+ function normalizeProjectSlug(value) {
439
+ if (!value)
440
+ return undefined;
441
+ const slug = value
442
+ .trim()
443
+ .toLowerCase()
444
+ .replace(/[^a-z0-9]+/g, "-")
445
+ .replace(/^-+|-+$/g, "")
446
+ .slice(0, 90);
447
+ if (!slug || slug === "personal" || slug === "uncategorized")
448
+ return undefined;
449
+ return slug;
450
+ }
451
+ function printPortfolioWriteResult(label, res) {
452
+ if (!res.ok || !res.data?.success) {
453
+ console.log("");
454
+ console.log(chalk_1.default.red(` ${label} failed`) + chalk_1.default.dim(` — ${(0, api_1.apiErrorMessage)(res.data) || `HTTP ${res.status}`}`));
455
+ console.log("");
456
+ return false;
457
+ }
458
+ const snapshot = res.data.snapshot;
459
+ console.log("");
460
+ console.log(chalk_1.default.green(` ${label} saved`));
461
+ if (res.data.taskId)
462
+ console.log(chalk_1.default.dim(` task: ${res.data.taskId}`));
463
+ if (res.data.captureId)
464
+ console.log(chalk_1.default.dim(` capture: ${res.data.captureId}`));
465
+ if (res.data.taskIds?.length)
466
+ console.log(chalk_1.default.dim(` routed tasks: ${res.data.taskIds.join(", ")}`));
467
+ if (snapshot) {
468
+ console.log(chalk_1.default.dim(` snapshot: ${snapshot.path}`));
469
+ if (snapshot.bundleVersion)
470
+ console.log(chalk_1.default.dim(` bundle: v${snapshot.bundleVersion}`));
471
+ if (snapshot.publishVersion)
472
+ console.log(chalk_1.default.dim(` published: v${snapshot.publishVersion}`));
473
+ if (snapshot.repoSync.attempted) {
474
+ if (snapshot.repoSync.ok) {
475
+ const push = snapshot.repoSync.push;
476
+ console.log(chalk_1.default.dim(` repo sync: ${push?.via || "pr"}${push?.merged === false ? " pending" : " merged/current"}`));
477
+ if (push?.prUrl)
478
+ console.log(chalk_1.default.dim(` pr: ${push.prUrl}`));
479
+ }
480
+ else {
481
+ console.log(chalk_1.default.hex("#C46A3A")(` repo sync failed: ${snapshot.repoSync.error || "unknown error"}`));
482
+ }
483
+ }
484
+ else {
485
+ console.log(chalk_1.default.dim(" repo sync skipped"));
486
+ }
487
+ }
488
+ console.log("");
489
+ return true;
490
+ }
491
+ function printProjectHelp(topic) {
492
+ const normalized = (topic || "").toLowerCase();
493
+ if (normalized === "task" || normalized === "tasks") {
494
+ console.log("");
495
+ console.log(" " + chalk_1.default.bold("youmd project task") + chalk_1.default.dim(" — create or update portfolio tasks"));
496
+ console.log("");
497
+ console.log(" " + chalk_1.default.cyan("create:"));
498
+ console.log(` ${chalk_1.default.cyan("youmd project task agent youmd: verify sync proof")} ${chalk_1.default.dim("# agent-owned project task")}`);
499
+ console.log(` ${chalk_1.default.cyan("youmd project task me personal: follow up with billing")} ${chalk_1.default.dim("# human-owned personal task")}`);
500
+ console.log(` ${chalk_1.default.cyan("youmd project task --owner agent --project youmd --title \"Audit MCP slice\" --no-sync")}`);
501
+ console.log("");
502
+ console.log(" " + chalk_1.default.cyan("update:"));
503
+ console.log(` ${chalk_1.default.cyan("youmd project task update <task-id> --status in_progress --priority high")}`);
504
+ console.log(` ${chalk_1.default.cyan("youmd project task update <task-id> --owner human --owner-label Houston --personal")}`);
505
+ console.log("");
506
+ console.log(" " + chalk_1.default.cyan("common flags:"));
507
+ console.log(` ${chalk_1.default.cyan("--owner <agent|human|me>")} ${chalk_1.default.dim("task owner")}`);
508
+ console.log(` ${chalk_1.default.cyan("--owner-label <name>")} ${chalk_1.default.dim("specific human/agent label")}`);
509
+ console.log(` ${chalk_1.default.cyan("--project <slug>|--personal")} ${chalk_1.default.dim("project scope or uncategorized personal")}`);
510
+ console.log(` ${chalk_1.default.cyan("--status <state>")} ${chalk_1.default.dim("proposed, open, in_progress, done, snoozed, cancelled")}`);
511
+ console.log(` ${chalk_1.default.cyan("--priority <level>")} ${chalk_1.default.dim("low, normal, high, urgent")}`);
512
+ console.log(` ${chalk_1.default.cyan("--tags a,b,c")} ${chalk_1.default.dim("searchable tags")}`);
513
+ console.log(` ${chalk_1.default.cyan("--no-sync")} ${chalk_1.default.dim("save to You.md without pushing repo snapshot")}`);
514
+ console.log("");
515
+ return;
516
+ }
517
+ if (normalized === "braindump" || normalized === "brain-dump" || normalized === "dump") {
518
+ console.log("");
519
+ console.log(" " + chalk_1.default.bold("youmd project braindump") + chalk_1.default.dim(" — preserve raw dumps and route tasks"));
520
+ console.log("");
521
+ console.log(" " + chalk_1.default.cyan("examples:"));
522
+ console.log(` ${chalk_1.default.cyan("youmd project braindump project:youmd raw idea text --agent-task \"Audit MCP slice\"")}`);
523
+ console.log(` ${chalk_1.default.cyan("youmd project braindump --project badapp --summary \"Watch workout capture\" --text \"raw transcript\"")}`);
524
+ console.log(` ${chalk_1.default.cyan("youmd project braindump --project youmd --text \"raw\" --human-task \"Decide priority\" --no-sync")}`);
525
+ console.log("");
526
+ console.log(" " + chalk_1.default.cyan("common flags:"));
527
+ console.log(` ${chalk_1.default.cyan("--project <slug>")} ${chalk_1.default.dim("portfolio project association")}`);
528
+ console.log(` ${chalk_1.default.cyan("--summary <text>")} ${chalk_1.default.dim("short searchable synthesis")}`);
529
+ console.log(` ${chalk_1.default.cyan("--text|--raw <text>")} ${chalk_1.default.dim("raw source text, preserved first")}`);
530
+ console.log(` ${chalk_1.default.cyan("--insights a,b,c")} ${chalk_1.default.dim("through-lines or reusable ideas")}`);
531
+ console.log(` ${chalk_1.default.cyan("--agent-task <title>")} ${chalk_1.default.dim("proposed agent-owned task")}`);
532
+ console.log(` ${chalk_1.default.cyan("--human-task <title>")} ${chalk_1.default.dim("proposed Houston-owned task")}`);
533
+ console.log(` ${chalk_1.default.cyan("--task agent: <title>")} ${chalk_1.default.dim("alternate task syntax")}`);
534
+ console.log(` ${chalk_1.default.cyan("--no-sync")} ${chalk_1.default.dim("save to You.md without pushing repo snapshot")}`);
535
+ console.log("");
536
+ return;
537
+ }
538
+ console.log("");
539
+ console.log(" " + chalk_1.default.bold("youmd project") + chalk_1.default.dim(" — manage project agent context"));
540
+ console.log("");
541
+ console.log(" " + chalk_1.default.cyan("subcommands:"));
542
+ console.log("");
543
+ console.log(` ${chalk_1.default.cyan("init [name]".padEnd(28))} ${chalk_1.default.dim("initialize a new project")}`);
544
+ console.log(` ${chalk_1.default.cyan("list".padEnd(28))} ${chalk_1.default.dim("list all projects")}`);
545
+ console.log(` ${chalk_1.default.cyan("show [name]".padEnd(28))} ${chalk_1.default.dim("show project details (auto-detects in project dir)")}`);
546
+ console.log(` ${chalk_1.default.cyan("memories <name>".padEnd(28))} ${chalk_1.default.dim("list project memories")}`);
547
+ console.log(` ${chalk_1.default.cyan("remember <name> <cat> <msg>".padEnd(28))} ${chalk_1.default.dim("add a memory to a project")}`);
548
+ console.log(` ${chalk_1.default.cyan("edit <name> <file>".padEnd(28))} ${chalk_1.default.dim("show a project file path for editing")}`);
549
+ console.log(` ${chalk_1.default.cyan("log <message...>".padEnd(28))} ${chalk_1.default.dim("append an agent activity entry to project-context/you.md/log.md")}`);
550
+ console.log(` ${chalk_1.default.cyan("log".padEnd(28))} ${chalk_1.default.dim("read last 15 entries from the activity log")}`);
551
+ console.log(` ${chalk_1.default.cyan("task agent youmd: ...".padEnd(28))} ${chalk_1.default.dim("save an agent/human portfolio task and sync the repo snapshot")}`);
552
+ console.log(` ${chalk_1.default.cyan("task update <id> --status ...".padEnd(28))} ${chalk_1.default.dim("edit task owner, project scope, metadata, and triage fields")}`);
553
+ console.log(` ${chalk_1.default.cyan("braindump project:youmd ...".padEnd(28))} ${chalk_1.default.dim("capture raw dump text, route tasks, and sync the repo snapshot")}`);
554
+ console.log(` ${chalk_1.default.cyan("portfolio-audit".padEnd(28))} ${chalk_1.default.dim("scan recent projects and env key names without printing secrets")}`);
555
+ console.log(` ${chalk_1.default.cyan("portfolio-hydrate".padEnd(28))} ${chalk_1.default.dim("hydrate portfolio graph from GitHub + local auditor results")}`);
556
+ console.log(` ${chalk_1.default.cyan("env-audit --fingerprints".padEnd(28))} ${chalk_1.default.dim("detect reused key values by local salted HMAC")}`);
557
+ console.log("");
558
+ console.log(chalk_1.default.dim(" help: youmd project task --help | youmd project braindump --help"));
559
+ console.log(chalk_1.default.dim(" projects are stored in .youmd/projects/<name>/"));
560
+ console.log(chalk_1.default.dim(" run from a project directory for auto-detection."));
561
+ console.log("");
562
+ }
563
+ function parseTaskArgs(args) {
564
+ const ownerFlag = readFlagValue(args, "--owner");
565
+ let ownerType = ownerFlag === "me" || ownerFlag === "human" ? "human" : "agent";
566
+ const ownerLabel = readFlagValue(args, "--owner-label");
567
+ let projectSlug = normalizeProjectSlug(readFlagValue(args, "--project"));
568
+ const priority = readFlagValue(args, "--priority") || "normal";
569
+ const status = readFlagValue(args, "--status") || "open";
570
+ const description = readFlagValue(args, "--description") || readFlagValue(args, "--notes");
571
+ const tags = csvFlag(args, "--tags");
572
+ let title = readFlagValue(args, "--title");
573
+ const rawParts = nonFlagArgs(args, [
574
+ "--owner", "--owner-label", "--project", "--priority", "--status",
575
+ "--description", "--notes", "--tags", "--title",
576
+ ]);
577
+ if (rawParts[0] === "add")
578
+ rawParts.shift();
579
+ let raw = rawParts.join(" ").trim();
580
+ if (!title) {
581
+ const ownerMatch = raw.match(/^(me|human|agent)\s+(.+)$/i);
582
+ if (ownerMatch) {
583
+ ownerType = ownerMatch[1].toLowerCase() === "agent" ? "agent" : "human";
584
+ raw = ownerMatch[2].trim();
585
+ }
586
+ const scoped = raw.match(/^([^:]{1,90}):\s*(.+)$/);
587
+ if (scoped) {
588
+ projectSlug = normalizeProjectSlug(scoped[1]);
589
+ title = scoped[2].trim();
590
+ }
591
+ else {
592
+ title = raw;
593
+ }
594
+ }
595
+ if (!title?.trim())
596
+ return null;
597
+ return {
598
+ projectSlug,
599
+ title: title.trim(),
600
+ description,
601
+ ownerType,
602
+ ownerLabel,
603
+ status,
604
+ priority,
605
+ tags,
606
+ sourceType: "cli",
607
+ syncRepo: !hasFlag(args, "--no-sync"),
608
+ agentName: "youmd CLI",
609
+ };
610
+ }
611
+ function parseTaskUpdateArgs(args) {
612
+ const rawArgs = [...args];
613
+ if (["update", "edit", "triage"].includes((rawArgs[0] || "").toLowerCase())) {
614
+ rawArgs.shift();
615
+ }
616
+ const taskId = readFlagValue(rawArgs, "--task-id") || readFlagValue(rawArgs, "--id") || nonFlagArgs(rawArgs, [
617
+ "--task-id", "--id", "--owner", "--owner-label", "--project", "--priority", "--status",
618
+ "--description", "--notes", "--title", "--tags", "--due",
619
+ ])[0];
620
+ if (!taskId?.trim())
621
+ return null;
622
+ const ownerFlag = readFlagValue(rawArgs, "--owner");
623
+ const projectFlagPresent = hasFlag(rawArgs, "--project") || hasFlag(rawArgs, "--personal");
624
+ const projectFlag = hasFlag(rawArgs, "--personal") ? "personal" : readFlagValue(rawArgs, "--project");
625
+ const dueFlagPresent = hasFlag(rawArgs, "--due") || hasFlag(rawArgs, "--clear-due");
626
+ const dueFlag = hasFlag(rawArgs, "--clear-due") ? "clear" : readFlagValue(rawArgs, "--due");
627
+ const payload = {
628
+ taskId: taskId.trim(),
629
+ syncRepo: !hasFlag(rawArgs, "--no-sync"),
630
+ agentName: "youmd CLI",
631
+ };
632
+ const title = readFlagValue(rawArgs, "--title");
633
+ if (title !== undefined)
634
+ payload.title = title;
635
+ if (hasFlag(rawArgs, "--description") || hasFlag(rawArgs, "--notes") || hasFlag(rawArgs, "--clear-description")) {
636
+ payload.description = hasFlag(rawArgs, "--clear-description")
637
+ ? null
638
+ : (readFlagValue(rawArgs, "--description") ?? readFlagValue(rawArgs, "--notes") ?? null);
639
+ }
640
+ if (ownerFlag) {
641
+ payload.ownerType = ownerFlag === "me" || ownerFlag === "human" ? "human" : "agent";
642
+ }
643
+ if (hasFlag(rawArgs, "--owner-label") || hasFlag(rawArgs, "--clear-owner-label")) {
644
+ payload.ownerLabel = hasFlag(rawArgs, "--clear-owner-label")
645
+ ? null
646
+ : (readFlagValue(rawArgs, "--owner-label") ?? null);
647
+ }
648
+ if (projectFlagPresent) {
649
+ payload.projectSlug = normalizeProjectSlug(projectFlag) ?? null;
650
+ }
651
+ const status = readFlagValue(rawArgs, "--status");
652
+ if (status !== undefined)
653
+ payload.status = status;
654
+ const priority = readFlagValue(rawArgs, "--priority");
655
+ if (priority !== undefined)
656
+ payload.priority = priority;
657
+ if (dueFlagPresent) {
658
+ if (!dueFlag || ["clear", "none", "null"].includes(dueFlag.toLowerCase())) {
659
+ payload.dueAt = null;
660
+ }
661
+ else {
662
+ const parsed = Date.parse(dueFlag);
663
+ payload.dueAt = Number.isFinite(parsed) ? parsed : Number(dueFlag);
664
+ }
665
+ }
666
+ if (hasFlag(rawArgs, "--tags")) {
667
+ payload.tags = csvFlag(rawArgs, "--tags");
668
+ }
669
+ const changedFields = [
670
+ "projectSlug", "title", "description", "ownerType", "ownerLabel",
671
+ "status", "priority", "dueAt", "tags",
672
+ ];
673
+ return changedFields.some((field) => payload[field] !== undefined) ? payload : null;
674
+ }
675
+ function parseBrainDumpTask(value, ownerType, projectSlug) {
676
+ const title = value.trim();
677
+ if (!title)
678
+ return null;
679
+ return {
680
+ projectSlug,
681
+ title,
682
+ ownerType,
683
+ status: "proposed",
684
+ priority: "normal",
685
+ tags: ["braindump"],
686
+ };
687
+ }
688
+ function parseBrainDumpArgs(args) {
689
+ let projectSlug = normalizeProjectSlug(readFlagValue(args, "--project"));
690
+ const summary = readFlagValue(args, "--summary");
691
+ const tags = csvFlag(args, "--tags");
692
+ const insights = csvFlag(args, "--insights");
693
+ const textFlag = readFlagValue(args, "--text") || readFlagValue(args, "--raw");
694
+ const rawParts = nonFlagArgs(args, [
695
+ "--project", "--summary", "--tags", "--insights", "--text", "--raw",
696
+ "--task", "--agent-task", "--human-task",
697
+ ]);
698
+ if (rawParts[0] === "add" || rawParts[0] === "capture")
699
+ rawParts.shift();
700
+ let rawText = (textFlag || rawParts.join(" ")).trim();
701
+ const projectPrefix = rawText.match(/^project:([a-zA-Z0-9_-]+)\s+(.+)$/);
702
+ if (projectPrefix) {
703
+ projectSlug = normalizeProjectSlug(projectPrefix[1]);
704
+ rawText = projectPrefix[2].trim();
705
+ }
706
+ if (!rawText)
707
+ return null;
708
+ const tasks = [
709
+ ...readAllFlagValues(args, "--task")
710
+ .map((value) => {
711
+ const match = value.match(/^(me|human|agent):\s*(.+)$/i);
712
+ if (match) {
713
+ return parseBrainDumpTask(match[2], match[1].toLowerCase() === "agent" ? "agent" : "human", projectSlug);
714
+ }
715
+ return parseBrainDumpTask(value, "agent", projectSlug);
716
+ })
717
+ .filter((task) => task !== null),
718
+ ...readAllFlagValues(args, "--agent-task")
719
+ .map((value) => parseBrainDumpTask(value, "agent", projectSlug))
720
+ .filter((task) => task !== null),
721
+ ...readAllFlagValues(args, "--human-task")
722
+ .map((value) => parseBrainDumpTask(value, "human", projectSlug))
723
+ .filter((task) => task !== null),
724
+ ];
725
+ return {
726
+ rawText,
727
+ summary,
728
+ projectSlugs: projectSlug ? [projectSlug] : [],
729
+ tags,
730
+ insights,
731
+ source: "cli",
732
+ tasks,
733
+ metadata: {
734
+ command: "youmd project braindump",
735
+ capturedAt: new Date().toISOString(),
736
+ },
737
+ syncRepo: !hasFlag(args, "--no-sync"),
738
+ agentName: "youmd CLI",
739
+ };
740
+ }
741
+ async function saveTaskFromCli(args) {
742
+ if (!(0, config_1.isAuthenticated)()) {
743
+ console.log("");
744
+ console.log(chalk_1.default.yellow(" not authenticated. run: youmd login"));
745
+ console.log("");
746
+ return;
747
+ }
748
+ if (["update", "edit", "triage"].includes((args[0] || "").toLowerCase())) {
749
+ const payload = parseTaskUpdateArgs(args);
750
+ if (!payload) {
751
+ console.log("");
752
+ console.log(chalk_1.default.yellow(" usage: youmd project task update <task-id> --status in_progress --owner agent --project youmd"));
753
+ console.log(chalk_1.default.dim(" clear scope: youmd project task update <task-id> --personal"));
754
+ console.log("");
755
+ return;
756
+ }
757
+ const res = await (0, api_1.updatePortfolioTask)(payload);
758
+ printPortfolioWriteResult("portfolio task update", res);
759
+ return;
760
+ }
761
+ const payload = parseTaskArgs(args);
762
+ if (!payload) {
763
+ console.log("");
764
+ console.log(chalk_1.default.yellow(" usage: youmd project task agent youmd: verify sync proof"));
765
+ console.log(chalk_1.default.dim(" or: youmd project task --owner me --project personal --title \"follow up\""));
766
+ console.log(chalk_1.default.dim(" edit: youmd project task update <task-id> --status in_progress --priority high"));
767
+ console.log("");
768
+ return;
769
+ }
770
+ const res = await (0, api_1.savePortfolioTask)(payload);
771
+ printPortfolioWriteResult("portfolio task", res);
772
+ }
773
+ async function saveBrainDumpFromCli(args) {
774
+ if (!(0, config_1.isAuthenticated)()) {
775
+ console.log("");
776
+ console.log(chalk_1.default.yellow(" not authenticated. run: youmd login"));
777
+ console.log("");
778
+ return;
779
+ }
780
+ const payload = parseBrainDumpArgs(args);
781
+ if (!payload) {
782
+ console.log("");
783
+ console.log(chalk_1.default.yellow(" usage: youmd project braindump project:youmd raw idea text --agent-task \"follow up\""));
784
+ console.log(chalk_1.default.dim(" or: youmd project braindump --project youmd --summary \"...\" --text \"...\""));
785
+ console.log("");
786
+ return;
787
+ }
788
+ const res = await (0, api_1.saveBrainDump)(payload);
789
+ printPortfolioWriteResult("brain dump", res);
790
+ }
791
+ function showPortfolioAudit(args) {
792
+ const root = readFlagValue(args, "--root");
793
+ const json = hasFlag(args, "--json");
794
+ const includeDotenv = hasFlag(args, "--include-dotenv");
795
+ const fingerprints = hasFlag(args, "--fingerprints");
796
+ const result = (0, portfolio_audit_1.runPortfolioAudit)({ root, includeDotenv, fingerprints, activeDays: 365 });
797
+ if (json) {
798
+ console.log(JSON.stringify(result, null, 2));
799
+ return;
800
+ }
801
+ console.log("");
802
+ console.log(" " + chalk_1.default.bold("portfolio audit") + chalk_1.default.dim(" — project/api/env map"));
803
+ console.log("");
804
+ console.log(chalk_1.default.dim(` root: ${result.root}`));
805
+ console.log(chalk_1.default.dim(` projects scanned: ${result.projectsScanned}`));
806
+ console.log(chalk_1.default.dim(` env files scanned: ${result.envFilesScanned}`));
807
+ console.log(chalk_1.default.dim(` providers detected: ${result.providers.length}`));
808
+ console.log("");
809
+ if (!fingerprints) {
810
+ console.log(chalk_1.default.dim(" secret values were not read. add --fingerprints to compare reused values by local salted HMAC."));
811
+ console.log("");
812
+ }
813
+ else {
814
+ console.log(chalk_1.default.dim(" fingerprints are local salted HMAC prefixes, not raw secret values."));
815
+ console.log("");
816
+ }
817
+ for (const provider of result.providers.slice(0, 20)) {
818
+ const shared = provider.sharedFingerprints.length > 0
819
+ ? ACCENT(` / ${provider.sharedFingerprints.length} shared value group${provider.sharedFingerprints.length === 1 ? "" : "s"}`)
820
+ : "";
821
+ console.log(` ${chalk_1.default.cyan(provider.provider)} ${chalk_1.default.dim(`(${provider.category})`)}${shared}`);
822
+ console.log(chalk_1.default.dim(` projects: ${provider.projects.join(", ") || "none"}`));
823
+ console.log(chalk_1.default.dim(` key names: ${provider.keyNames.slice(0, 8).join(", ")}${provider.keyNames.length > 8 ? ", ..." : ""}`));
824
+ if (provider.sharedFingerprints.length > 0) {
825
+ for (const group of provider.sharedFingerprints.slice(0, 3)) {
826
+ console.log(chalk_1.default.dim(` shared ${group.fingerprint}: ${group.projects.join(", ")} (${group.keyNames.join(", ")})`));
827
+ }
828
+ }
829
+ console.log("");
830
+ }
831
+ console.log(chalk_1.default.dim(" dashboard: /api or /env"));
832
+ console.log(chalk_1.default.dim(" skill: /skill use portfolio-graph-auditor"));
833
+ console.log("");
834
+ }
835
+ function inferStackName(name, projectPath) {
836
+ const key = `${name} ${projectPath}`.toLowerCase();
837
+ if (key.includes("youmd"))
838
+ return "YouStack";
839
+ if (key.includes("bamfaiapp") || key.includes("bamf.ai"))
840
+ return "BAMFStack";
841
+ if (key.includes("bamfsite") || key.includes("bamfos"))
842
+ return "BAMFOSStack";
843
+ if (key.includes("badapp"))
844
+ return "BadStack";
845
+ if (key.includes("foldermd"))
846
+ return "FolderMD Stack";
847
+ if (key.includes("bigbounce"))
848
+ return "AstroStack";
849
+ if (key.includes("hubify"))
850
+ return "HubStack";
851
+ if (key.includes("myo"))
852
+ return "Myo Stack";
853
+ return "Project Stack";
854
+ }
855
+ function shouldHydrateLocalProject(project, includeNested) {
856
+ if (!project.name || project.path === ".")
857
+ return false;
858
+ if (project.path.includes(".reference-repos/") || project.path.startsWith(".dev-skills/"))
859
+ return false;
860
+ if (project.path.includes("/project-context/"))
861
+ return false;
862
+ if (includeNested)
863
+ return true;
864
+ const depth = project.path.split("/").filter(Boolean).length;
865
+ return depth === 1 || (depth <= 2 && project.envFiles.length > 0);
866
+ }
867
+ function readTextIfExists(filePath) {
868
+ try {
869
+ if (!fs.existsSync(filePath))
870
+ return "";
871
+ return fs.readFileSync(filePath, "utf-8");
872
+ }
873
+ catch {
874
+ return "";
875
+ }
876
+ }
877
+ function cleanDocSnippet(text, limit = 420) {
878
+ const cleaned = text
879
+ .replace(/```[\s\S]*?```/g, " ")
880
+ .replace(/<!--[\s\S]*?-->/g, " ")
881
+ .split(/\n\s*\n/)
882
+ .map((block) => block.replace(/[#*_>`[\]]/g, " ").replace(/\s+/g, " ").trim())
883
+ .find((block) => {
884
+ const lower = block.toLowerCase();
885
+ const setupBoilerplate = (/\b(requires?|install(?:ation)?|getting started|quick start|setup|setup script|dependencies|run|start)\b/.test(lower) &&
886
+ /\b(node\.?js|npm|pnpm|yarn|bun|nvm|localhost|\.env|docker|dev server|chmod|script|dependencies)\b/.test(lower)) || (/\b(?:copy|create|configure|add|set|update)\b/.test(lower) &&
887
+ /\.env(?:\.example|\.local)?/.test(lower)) || /\b(?:localhost|preview\/web|dev server)\b/.test(lower) ||
888
+ /\bcursor will expose\b/.test(lower) ||
889
+ (/\b(?:compile|build)\b/.test(lower) && /\b(?:app|production|bundle|dist)\b/.test(lower)) ||
890
+ /^(?:.+\s+)?product requirements document(?:\s*\(prd\))?$/.test(lower) ||
891
+ /^(?:version|last updated|founder|status|date):\s/.test(lower) ||
892
+ /^\s*(npm|pnpm|yarn|bun|nvm|docker)\s+/i.test(block);
893
+ return block.length > 24 && !setupBoilerplate;
894
+ }) ?? "";
895
+ return cleaned ? cleaned.slice(0, limit) : undefined;
896
+ }
897
+ function repoFullNameFromRemote(absPath) {
898
+ try {
899
+ const remote = (0, child_process_1.execFileSync)("git", ["-C", absPath, "config", "--get", "remote.origin.url"], {
900
+ encoding: "utf-8",
901
+ stdio: ["ignore", "pipe", "ignore"],
902
+ }).trim();
903
+ const match = remote.match(/github\.com[:/](.+?)(?:\.git)?$/);
904
+ return match?.[1];
905
+ }
906
+ catch {
907
+ return undefined;
908
+ }
909
+ }
910
+ function githubCommitUrl(repoFullName, sha) {
911
+ return repoFullName ? `https://github.com/${repoFullName}/commit/${sha}` : undefined;
912
+ }
913
+ function readRecentCommitEvents(absPath, relPath, days) {
914
+ try {
915
+ const output = (0, child_process_1.execFileSync)("git", [
916
+ "-C",
917
+ absPath,
918
+ "log",
919
+ `--since=${Math.max(1, Math.min(days, 365))} days ago`,
920
+ "--max-count=40",
921
+ "--pretty=format:%H%x1f%ct%x1f%s",
922
+ ], {
923
+ encoding: "utf-8",
924
+ stdio: ["ignore", "pipe", "ignore"],
925
+ }).trim();
926
+ if (!output)
927
+ return [];
928
+ const repoFullName = repoFullNameFromRemote(absPath);
929
+ return output.split("\n").map((line) => {
930
+ const [sha, epoch, subject] = line.split("\x1f");
931
+ const occurredAt = Number(epoch) * 1000;
932
+ return {
933
+ kind: "commit",
934
+ title: subject || "Commit",
935
+ summary: subject || undefined,
936
+ url: sha ? githubCommitUrl(repoFullName, sha) : undefined,
937
+ source: "local-git",
938
+ evidencePath: relPath,
939
+ dedupeKey: sha ? `commit:${sha}` : undefined,
940
+ tags: ["commit", "shipped"],
941
+ metadata: { sha, repoFullName },
942
+ occurredAt: Number.isFinite(occurredAt) ? occurredAt : Date.now(),
943
+ };
944
+ });
945
+ }
946
+ catch {
947
+ return [];
948
+ }
949
+ }
950
+ function readRecentPrEvents(absPath, relPath, days) {
951
+ const repoFullName = repoFullNameFromRemote(absPath);
952
+ if (!repoFullName)
953
+ return [];
954
+ const since = new Date(Date.now() - Math.max(1, Math.min(days, 365)) * 86400000).toISOString().slice(0, 10);
955
+ try {
956
+ const output = (0, child_process_1.execFileSync)("gh", [
957
+ "pr",
958
+ "list",
959
+ "--repo",
960
+ repoFullName,
961
+ "--state",
962
+ "all",
963
+ "--search",
964
+ `updated:>=${since}`,
965
+ "--limit",
966
+ "30",
967
+ "--json",
968
+ "number,title,state,updatedAt,mergedAt,url",
969
+ ], {
970
+ encoding: "utf-8",
971
+ stdio: ["ignore", "pipe", "ignore"],
972
+ }).trim();
973
+ const rows = JSON.parse(output || "[]");
974
+ return rows.map((row) => {
975
+ const occurredAt = Date.parse(row.mergedAt || row.updatedAt || "");
976
+ return {
977
+ kind: "pull-request",
978
+ title: row.title || `Pull request #${row.number ?? "?"}`,
979
+ summary: row.state ? `${row.state.toLowerCase()} pull request #${row.number ?? "?"}` : undefined,
980
+ url: row.url,
981
+ source: "github-pr",
982
+ evidencePath: relPath,
983
+ dedupeKey: row.number ? `pr:${repoFullName}:${row.number}` : undefined,
984
+ tags: ["pull-request", row.state?.toLowerCase() || "pr"].filter(Boolean),
985
+ metadata: { repoFullName, number: row.number, state: row.state },
986
+ occurredAt: Number.isFinite(occurredAt) ? occurredAt : Date.now(),
987
+ };
988
+ });
989
+ }
990
+ catch {
991
+ return [];
992
+ }
993
+ }
994
+ function readProjectDocs(absPath) {
995
+ const docs = [];
996
+ const readmePath = ["README.md", "readme.md"].map((file) => path.join(absPath, file)).find((file) => fs.existsSync(file));
997
+ const overviewPath = path.join(absPath, "project-context", "overview.md");
998
+ const currentStatePath = path.join(absPath, "project-context", "CURRENT_STATE.md");
999
+ const todoPath = path.join(absPath, "project-context", "TODO.md");
1000
+ const tasksMdPath = path.join(absPath, "project-context", "tasks.md");
1001
+ const tasksJsonPath = path.join(absPath, "project-context", "tasks.json");
1002
+ const prdPath = path.join(absPath, "project-context", "PRD.md");
1003
+ const changelogPath = path.join(absPath, "project-context", "CHANGELOG.md");
1004
+ const featuresPath = path.join(absPath, "project-context", "FEATURES.md");
1005
+ const designPath = path.join(absPath, "project-context", "design.md");
1006
+ const researchPath = path.join(absPath, "project-context", "research.md");
1007
+ const ideasPath = path.join(absPath, "project-context", "ideas.md");
1008
+ const architecturePath = path.join(absPath, "project-context", "ARCHITECTURE.md");
1009
+ const readme = readmePath ? readTextIfExists(readmePath) : "";
1010
+ const overview = readTextIfExists(overviewPath);
1011
+ const currentState = readTextIfExists(currentStatePath);
1012
+ const todo = readTextIfExists(todoPath);
1013
+ const tasks = readTextIfExists(tasksMdPath) || readTextIfExists(tasksJsonPath);
1014
+ const prd = readTextIfExists(prdPath);
1015
+ if (readmePath)
1016
+ docs.push(path.relative(absPath, readmePath));
1017
+ if (fs.existsSync(overviewPath))
1018
+ docs.push("project-context/overview.md");
1019
+ if (fs.existsSync(currentStatePath))
1020
+ docs.push("project-context/CURRENT_STATE.md");
1021
+ if (fs.existsSync(todoPath))
1022
+ docs.push("project-context/TODO.md");
1023
+ if (fs.existsSync(tasksMdPath))
1024
+ docs.push("project-context/tasks.md");
1025
+ if (fs.existsSync(tasksJsonPath))
1026
+ docs.push("project-context/tasks.json");
1027
+ if (fs.existsSync(prdPath))
1028
+ docs.push("project-context/PRD.md");
1029
+ if (fs.existsSync(changelogPath))
1030
+ docs.push("project-context/CHANGELOG.md");
1031
+ if (fs.existsSync(featuresPath))
1032
+ docs.push("project-context/FEATURES.md");
1033
+ if (fs.existsSync(designPath))
1034
+ docs.push("project-context/design.md");
1035
+ if (fs.existsSync(researchPath))
1036
+ docs.push("project-context/research.md");
1037
+ if (fs.existsSync(ideasPath))
1038
+ docs.push("project-context/ideas.md");
1039
+ if (fs.existsSync(architecturePath))
1040
+ docs.push("project-context/ARCHITECTURE.md");
1041
+ return {
1042
+ summary: cleanDocSnippet(overview) ??
1043
+ cleanDocSnippet(readme) ??
1044
+ cleanDocSnippet(prd) ??
1045
+ cleanDocSnippet(currentState),
1046
+ focus: cleanDocSnippet(currentState, 360) ??
1047
+ cleanDocSnippet(todo, 360) ??
1048
+ cleanDocSnippet(tasks, 360),
1049
+ docs,
1050
+ contents: {
1051
+ readme,
1052
+ overview,
1053
+ currentState,
1054
+ todo,
1055
+ tasks,
1056
+ prd,
1057
+ features: readTextIfExists(featuresPath),
1058
+ design: readTextIfExists(designPath),
1059
+ research: readTextIfExists(researchPath),
1060
+ ideas: readTextIfExists(ideasPath),
1061
+ architecture: readTextIfExists(architecturePath),
1062
+ },
1063
+ };
1064
+ }
1065
+ function shippedCount(events, days) {
1066
+ const cutoff = Date.now() - days * 86400000;
1067
+ return events.filter((event) => event.occurredAt >= cutoff && (event.kind === "commit" || event.kind === "pull-request" || event.tags?.includes("shipped"))).length;
1068
+ }
1069
+ function hydrationProjectsFromAudit(result, options) {
1070
+ return result.projects
1071
+ .filter((project) => shouldHydrateLocalProject(project, options.includeNested))
1072
+ .slice(0, options.limit)
1073
+ .map((project) => {
1074
+ const absPath = path.join(result.root, project.path);
1075
+ const docs = readProjectDocs(absPath);
1076
+ const commitEvents = readRecentCommitEvents(absPath, project.path, options.days);
1077
+ const prEvents = options.includePrs ? readRecentPrEvents(absPath, project.path, options.days) : [];
1078
+ const activityEvents = [...commitEvents, ...prEvents]
1079
+ .sort((a, b) => b.occurredAt - a.occurredAt)
1080
+ .slice(0, 70);
1081
+ const latestSubjects = activityEvents
1082
+ .filter((event) => event.kind === "commit" || event.kind === "pull-request")
1083
+ .slice(0, 4)
1084
+ .map((event) => event.title)
1085
+ .join("; ");
1086
+ const shippedToday = shippedCount(activityEvents, 1);
1087
+ const shipped7d = shippedCount(activityEvents, 7);
1088
+ const shipped30d = shippedCount(activityEvents, 30);
1089
+ const fallbackSummary = `Local portfolio auditor found ${project.name} at ${project.path}${project.providers.length ? ` using ${project.providers.slice(0, 6).join(", ")}` : ""}.`;
1090
+ const stackName = inferStackName(project.name, project.path);
1091
+ const strategy = options.strategy
1092
+ ? (0, project_strategy_1.synthesizeProjectStrategy)({
1093
+ projectName: project.name,
1094
+ stackName,
1095
+ docs: docs.contents,
1096
+ providers: project.providers,
1097
+ recentActivityTitles: activityEvents.slice(0, 8).map((event) => event.title),
1098
+ shippedToday,
1099
+ shipped7d,
1100
+ shipped30d,
1101
+ fallbackSummary,
1102
+ })
1103
+ : {};
1104
+ return {
1105
+ name: project.name,
1106
+ path: project.path,
1107
+ stackName,
1108
+ status: activityEvents.length ? "active" : "local-audited",
1109
+ summary: docs.summary || fallbackSummary,
1110
+ ...strategy,
1111
+ focus: docs.focus || (latestSubjects ? `Recent shipping activity: ${latestSubjects}` : "Audit project role, API/MCP ownership, reusable patterns, and env/service-account boundaries."),
1112
+ docs: docs.docs,
1113
+ envFiles: project.envFiles,
1114
+ providers: project.providers,
1115
+ tags: [
1116
+ ...project.providers.map((provider) => provider.toLowerCase().replace(/[^a-z0-9]+/g, "-")).filter(Boolean),
1117
+ activityEvents.length ? "activity-hydrated" : "local-audit",
1118
+ ],
1119
+ lastActivityAt: activityEvents[0]?.occurredAt ?? Date.now(),
1120
+ activityEvents: [
1121
+ {
1122
+ kind: "summary",
1123
+ title: `Shipped ${shippedToday} today / ${shipped7d} in 7d / ${shipped30d} in 30d`,
1124
+ summary: latestSubjects || docs.focus || docs.summary || fallbackSummary,
1125
+ source: "local-portfolio-audit",
1126
+ evidencePath: project.path,
1127
+ dedupeKey: `summary:${project.path}`,
1128
+ tags: ["activity-summary", "shipped"],
1129
+ metadata: { shippedToday, shipped7d, shipped30d, providerCount: project.providers.length },
1130
+ occurredAt: activityEvents[0]?.occurredAt ?? Date.now(),
1131
+ },
1132
+ ...activityEvents,
1133
+ ],
1134
+ };
1135
+ });
1136
+ }
1137
+ async function hydratePortfolioFromCli(args) {
1138
+ if (!(0, config_1.isAuthenticated)()) {
1139
+ console.log("");
1140
+ console.log(chalk_1.default.yellow(" not authenticated. run: youmd login"));
1141
+ console.log("");
1142
+ return;
1143
+ }
1144
+ const root = readFlagValue(args, "--root");
1145
+ const days = Number(readFlagValue(args, "--days") ?? 90);
1146
+ const limit = Number(readFlagValue(args, "--limit") ?? 80);
1147
+ const includeNested = hasFlag(args, "--include-nested");
1148
+ const includeTracked = !hasFlag(args, "--no-github");
1149
+ const includePrs = !hasFlag(args, "--no-prs");
1150
+ const strategy = !hasFlag(args, "--no-strategy");
1151
+ const reusablePatternsEnabled = !hasFlag(args, "--no-patterns");
1152
+ const dryRun = hasFlag(args, "--dry-run");
1153
+ const audit = (0, portfolio_audit_1.runPortfolioAudit)({ root, includeDotenv: hasFlag(args, "--include-dotenv"), fingerprints: false, activeDays: days });
1154
+ const projects = hydrationProjectsFromAudit(audit, {
1155
+ includeNested,
1156
+ limit: Math.max(1, Math.min(limit, 200)),
1157
+ days: Number.isFinite(days) ? days : 90,
1158
+ includePrs,
1159
+ strategy,
1160
+ });
1161
+ const patternMining = reusablePatternsEnabled
1162
+ ? (0, reusable_patterns_1.mineReusablePatterns)({
1163
+ root: audit.root,
1164
+ projects: audit.projects
1165
+ .filter((project) => shouldHydrateLocalProject(project, includeNested))
1166
+ .slice(0, Math.max(1, Math.min(limit, 200))),
1167
+ maxProjects: Math.max(1, Math.min(limit, 200)),
1168
+ })
1169
+ : null;
1170
+ const reusablePatternsPayload = patternMining?.patterns ?? [];
1171
+ console.log("");
1172
+ console.log(" " + chalk_1.default.bold("portfolio hydrate") + chalk_1.default.dim(" — auditor -> portfolio graph"));
1173
+ console.log("");
1174
+ console.log(chalk_1.default.dim(` root: ${audit.root}`));
1175
+ console.log(chalk_1.default.dim(` auditor projects scanned: ${audit.projectsScanned}`));
1176
+ console.log(chalk_1.default.dim(` local hydration candidates: ${projects.length}`));
1177
+ console.log(chalk_1.default.dim(` github tracked sync: ${includeTracked ? `${Number.isFinite(days) ? days : 90}d` : "skipped"}`));
1178
+ console.log(chalk_1.default.dim(` local git/PR activity: commits${includePrs ? " + pull requests" : ""}`));
1179
+ console.log(chalk_1.default.dim(` strategy fields: ${strategy ? "docs + activity synthesis" : "skipped"}`));
1180
+ console.log(chalk_1.default.dim(` reusable patterns: ${patternMining ? `${patternMining.patterns.length} mined from ${patternMining.projectsScanned} projects / ${patternMining.filesScanned} files` : "skipped"}`));
1181
+ console.log("");
1182
+ for (const project of projects.slice(0, 12)) {
1183
+ console.log(` ${chalk_1.default.cyan(project.name.padEnd(24))} ${chalk_1.default.dim(project.path || "")}`);
1184
+ }
1185
+ if (projects.length > 12)
1186
+ console.log(chalk_1.default.dim(` ... ${projects.length - 12} more`));
1187
+ if (patternMining && patternMining.patterns.length > 0) {
1188
+ console.log("");
1189
+ for (const pattern of patternMining.patterns.slice(0, 8)) {
1190
+ console.log(` ${chalk_1.default.hex("#C46A3A")(pattern.name.padEnd(36))} ${chalk_1.default.dim(`${pattern.usageProjects.length} project${pattern.usageProjects.length === 1 ? "" : "s"}`)}`);
1191
+ }
1192
+ if (patternMining.patterns.length > 8)
1193
+ console.log(chalk_1.default.dim(` ... ${patternMining.patterns.length - 8} more patterns`));
1194
+ }
1195
+ console.log("");
1196
+ if (dryRun) {
1197
+ console.log(chalk_1.default.dim(" dry run only. remove --dry-run to hydrate Convex portfolio records."));
1198
+ console.log("");
1199
+ return;
1200
+ }
1201
+ const res = await (0, api_1.hydratePortfolioProjects)({
1202
+ projects,
1203
+ reusablePatterns: reusablePatternsPayload,
1204
+ includeTracked,
1205
+ days: Number.isFinite(days) ? days : 90,
1206
+ limit: Number.isFinite(limit) ? limit : 80,
1207
+ source: "local-portfolio-audit",
1208
+ agentName: "youmd CLI portfolio-graph-auditor",
1209
+ });
1210
+ if (!res.ok || !res.data?.success) {
1211
+ console.log(chalk_1.default.red(" portfolio hydrate failed") + chalk_1.default.dim(` — ${(0, api_1.apiErrorMessage)(res.data) || `HTTP ${res.status}`}`));
1212
+ console.log("");
1213
+ return;
1214
+ }
1215
+ const tracked = res.data.tracked;
1216
+ console.log(chalk_1.default.green(" portfolio graph hydrated"));
1217
+ if (tracked) {
1218
+ console.log(chalk_1.default.dim(` github tracked: ${tracked.tracked} considered / ${tracked.created} created / ${tracked.updated} updated`));
1219
+ }
1220
+ console.log(chalk_1.default.dim(` local audit: ${res.data.local.upserted} upserted / ${res.data.local.skipped} skipped`));
1221
+ if (res.data.patterns) {
1222
+ console.log(chalk_1.default.dim(` reusable patterns: ${res.data.patterns.upserted} upserted / ${res.data.patterns.created} created / ${res.data.patterns.updated} updated`));
1223
+ }
1224
+ console.log("");
1225
+ }
388
1226
  // ─── Main command router ──────────────────────────────────────────────
389
1227
  async function projectCommand(subcommand, ...args) {
390
1228
  const sub = (subcommand || "").toLowerCase();
1229
+ if (sub === "--help" || sub === "-h" || args.includes("--help") || args.includes("-h")) {
1230
+ printProjectHelp(sub === "--help" || sub === "-h" ? undefined : sub);
1231
+ return;
1232
+ }
391
1233
  switch (sub) {
392
1234
  case "init":
393
1235
  await initProject(args);
@@ -419,6 +1261,25 @@ async function projectCommand(subcommand, ...args) {
419
1261
  (0, project_log_1.projectLogRead)();
420
1262
  }
421
1263
  break;
1264
+ case "task":
1265
+ case "tasks":
1266
+ await saveTaskFromCli(args);
1267
+ break;
1268
+ case "braindump":
1269
+ case "brain-dump":
1270
+ case "dump":
1271
+ await saveBrainDumpFromCli(args);
1272
+ break;
1273
+ case "portfolio-audit":
1274
+ case "env-audit":
1275
+ case "apis":
1276
+ showPortfolioAudit(args);
1277
+ break;
1278
+ case "portfolio-hydrate":
1279
+ case "hydrate-portfolio":
1280
+ case "hydrate":
1281
+ await hydratePortfolioFromCli(args);
1282
+ break;
422
1283
  default: {
423
1284
  // If no subcommand, try auto-detecting the current project
424
1285
  const detected = (0, config_1.detectProjectContext)();
@@ -427,23 +1288,7 @@ async function projectCommand(subcommand, ...args) {
427
1288
  showProject([]);
428
1289
  return;
429
1290
  }
430
- console.log("");
431
- console.log(" " + chalk_1.default.bold("youmd project") + chalk_1.default.dim(" — manage project agent context"));
432
- console.log("");
433
- console.log(" " + chalk_1.default.cyan("subcommands:"));
434
- console.log("");
435
- console.log(` ${chalk_1.default.cyan("init [name]".padEnd(28))} ${chalk_1.default.dim("initialize a new project")}`);
436
- console.log(` ${chalk_1.default.cyan("list".padEnd(28))} ${chalk_1.default.dim("list all projects")}`);
437
- console.log(` ${chalk_1.default.cyan("show [name]".padEnd(28))} ${chalk_1.default.dim("show project details (auto-detects in project dir)")}`);
438
- console.log(` ${chalk_1.default.cyan("memories <name>".padEnd(28))} ${chalk_1.default.dim("list project memories")}`);
439
- console.log(` ${chalk_1.default.cyan("remember <name> <cat> <msg>".padEnd(28))} ${chalk_1.default.dim("add a memory to a project")}`);
440
- console.log(` ${chalk_1.default.cyan("edit <name> <file>".padEnd(28))} ${chalk_1.default.dim("show a project file path for editing")}`);
441
- console.log(` ${chalk_1.default.cyan("log <message...>".padEnd(28))} ${chalk_1.default.dim("append an agent activity entry to project-context/you.md/log.md")}`);
442
- console.log(` ${chalk_1.default.cyan("log".padEnd(28))} ${chalk_1.default.dim("read last 15 entries from the activity log")}`);
443
- console.log("");
444
- console.log(chalk_1.default.dim(" projects are stored in .youmd/projects/<name>/"));
445
- console.log(chalk_1.default.dim(" run from a project directory for auto-detection."));
446
- console.log("");
1291
+ printProjectHelp();
447
1292
  break;
448
1293
  }
449
1294
  }