@dunnewold-labs/mr-manager 0.4.16 → 0.4.20

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 (2) hide show
  1. package/dist/index.mjs +198 -29
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // cli/index.ts
4
- import { Command as Command27 } from "commander";
4
+ import { Command as Command28 } from "commander";
5
5
  import { existsSync as existsSync17 } from "fs";
6
6
  import { homedir as homedir3 } from "os";
7
7
  import { join as join12 } from "path";
@@ -185,7 +185,7 @@ import { fileURLToPath } from "url";
185
185
  // cli/package.json
186
186
  var package_default = {
187
187
  name: "@dunnewold-labs/mr-manager",
188
- version: "0.4.16",
188
+ version: "0.4.20",
189
189
  description: "Mr. Manager - Task and project management CLI",
190
190
  bin: {
191
191
  mr: "./dist/index.mjs"
@@ -2729,11 +2729,12 @@ var watchCommand = new Command8("watch").description(
2729
2729
  return;
2730
2730
  }
2731
2731
  let attemptIndex = 0;
2732
+ let resumeAlreadyRetried = false;
2732
2733
  const launchAttempt = async (attemptAgent) => {
2733
2734
  let spawnFailureReason = null;
2734
2735
  const pausedForNetwork = networkPaused.get(task.id);
2735
- const shouldResumeClaudeSession = attemptAgent === "claude" && !!task.claudeSessionId && (hasFeedback || pausedForNetwork?.resumeSession === true);
2736
- const sessionId = attemptAgent === "claude" ? task.claudeSessionId ?? randomUUID() : void 0;
2736
+ const shouldResumeClaudeSession = attemptAgent === "claude" && !!task.claudeSessionId && !resumeAlreadyRetried && (hasFeedback || pausedForNetwork?.resumeSession === true);
2737
+ const sessionId = attemptAgent === "claude" ? shouldResumeClaudeSession ? task.claudeSessionId : randomUUID() : void 0;
2737
2738
  const executionSystemPrompt = composeSystemPrompt(EXECUTION_SYSTEM_SECTIONS);
2738
2739
  const child = spawnAgent(
2739
2740
  attemptAgent,
@@ -2777,6 +2778,17 @@ var watchCommand = new Command8("watch").description(
2777
2778
  logWarn(prefix, `${attemptAgent} paused after network loss (${failureDetail})`);
2778
2779
  return;
2779
2780
  }
2781
+ if (shouldResumeClaudeSession && !resumeAlreadyRetried) {
2782
+ resumeAlreadyRetried = true;
2783
+ logWarn(prefix, `Claude session resume failed (${failureDetail}) \u2014 retrying with fresh session`);
2784
+ await postTaskUpdate(
2785
+ task.id,
2786
+ `Claude session resume failed \u2014 retrying with fresh session`,
2787
+ "system"
2788
+ );
2789
+ await launchAttempt("claude");
2790
+ return;
2791
+ }
2780
2792
  const nextAgent = attemptOrder[attemptIndex + 1];
2781
2793
  if (nextAgent) {
2782
2794
  logWarn(prefix, `${attemptAgent} failed (${failureDetail}) \u2014 retrying with ${nextAgent}`);
@@ -4557,7 +4569,7 @@ async function checkApiConnectivity() {
4557
4569
  }
4558
4570
  }
4559
4571
  function printResults(checks) {
4560
- const maxNameLen = Math.max(...checks.map((c10) => c10.name.length));
4572
+ const maxNameLen = Math.max(...checks.map((c11) => c11.name.length));
4561
4573
  let allOk = true;
4562
4574
  for (const check of checks) {
4563
4575
  const isOptional = check.optional ?? false;
@@ -4571,10 +4583,10 @@ function printResults(checks) {
4571
4583
  }
4572
4584
  async function autoFix(checks, agent) {
4573
4585
  const { spawn: spawn8 } = await import("child_process");
4574
- const ghInstalled = checks.find((c10) => c10.name === "GitHub CLI (gh)").ok;
4575
- const ghAuthed = checks.find((c10) => c10.name === "GitHub CLI auth").ok;
4576
- const mrAuthed = checks.find((c10) => c10.name === "Mr. Manager CLI auth").ok;
4577
- const claudeCheck = checks.find((c10) => c10.name === "Claude Code (claude)");
4586
+ const ghInstalled = checks.find((c11) => c11.name === "GitHub CLI (gh)").ok;
4587
+ const ghAuthed = checks.find((c11) => c11.name === "GitHub CLI auth").ok;
4588
+ const mrAuthed = checks.find((c11) => c11.name === "Mr. Manager CLI auth").ok;
4589
+ const claudeCheck = checks.find((c11) => c11.name === "Claude Code (claude)");
4578
4590
  if (claudeCheck && !claudeCheck.ok && agent === "claude") {
4579
4591
  console.log(paint5("cyan", " Installing Claude Code..."));
4580
4592
  console.log(paint5("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
@@ -4637,7 +4649,7 @@ var setupCommand = new Command14("setup").description("Check that all dependenci
4637
4649
  console.log("");
4638
4650
  return;
4639
4651
  }
4640
- const fixes = checks.filter((c10) => !c10.ok && c10.fix && !c10.optional);
4652
+ const fixes = checks.filter((c11) => !c11.ok && c11.fix && !c11.optional);
4641
4653
  if (fixes.length > 0) {
4642
4654
  console.log(paint5("yellow", " To fix:"));
4643
4655
  for (const fix of fixes) {
@@ -4909,7 +4921,7 @@ async function ensureDevServer() {
4909
4921
  }
4910
4922
  var browseCommand = new Command18("browse").description("Control a headless browser for QA and testing").argument("[command]", "Browse command (goto, click, fill, screenshot, etc.)").argument("[args...]", "Command arguments").option(
4911
4923
  "--task-id <id>",
4912
- "Attach screenshot to a task update (only for screenshot command)"
4924
+ "Attach output to a task update (for screenshot and recording-stop commands)"
4913
4925
  ).option("--dev", "Auto-start local dev server before browsing").allowUnknownOption(true).action(
4914
4926
  async (command, args, opts) => {
4915
4927
  if (!command) {
@@ -4948,6 +4960,62 @@ var browseCommand = new Command18("browse").description("Control a headless brow
4948
4960
  if (exitCode !== 0) {
4949
4961
  process.exit(exitCode);
4950
4962
  }
4963
+ if (command === "recording-stop" && opts.taskId) {
4964
+ const recordingPath = stdout.match(/Recording saved: (.+)/)?.[1]?.trim();
4965
+ if (!recordingPath || !existsSync9(recordingPath)) {
4966
+ console.error("[browse] Could not find recording file");
4967
+ process.exit(1);
4968
+ }
4969
+ try {
4970
+ const config = loadConfig();
4971
+ const videoBuffer = readFileSync7(recordingPath);
4972
+ const fileName = recordingPath.split("/").pop() || "browse-recording.webm";
4973
+ const isMp4 = fileName.endsWith(".mp4");
4974
+ const formData = new FormData();
4975
+ const blob = new Blob([videoBuffer], {
4976
+ type: isMp4 ? "video/mp4" : "video/webm"
4977
+ });
4978
+ formData.append("file", blob, fileName);
4979
+ formData.append("prefix", "browse-recordings");
4980
+ const uploadRes = await fetch(`${config.apiUrl}/api/upload`, {
4981
+ method: "POST",
4982
+ headers: { Authorization: `Bearer ${config.apiKey}` },
4983
+ body: formData
4984
+ });
4985
+ if (!uploadRes.ok) {
4986
+ const errText = await uploadRes.text();
4987
+ console.error(
4988
+ `[browse] Upload failed: ${uploadRes.status} ${errText}`
4989
+ );
4990
+ process.exit(1);
4991
+ }
4992
+ const uploadData = await uploadRes.json();
4993
+ await api.post(`/api/tasks/${opts.taskId}/updates`, {
4994
+ message: "Browser recording",
4995
+ source: "agent",
4996
+ media: [
4997
+ {
4998
+ kind: "recording",
4999
+ url: uploadData.url,
5000
+ mimeType: isMp4 ? "video/mp4" : "video/webm",
5001
+ label: "Browse recording",
5002
+ captureContext: "browse-recording",
5003
+ uploadState: "uploaded",
5004
+ isCanonical: true
5005
+ }
5006
+ ]
5007
+ });
5008
+ console.log(
5009
+ `[browse] Recording uploaded and attached to task ${opts.taskId}`
5010
+ );
5011
+ } catch (err) {
5012
+ console.error(
5013
+ `[browse] Failed to attach recording: ${err.message}`
5014
+ );
5015
+ process.exit(1);
5016
+ }
5017
+ return;
5018
+ }
4951
5019
  if (command === "screenshot" && opts.taskId) {
4952
5020
  const screenshotPath = stdout.match(/Screenshot saved: (.+)/)?.[1];
4953
5021
  if (!screenshotPath || !existsSync9(screenshotPath)) {
@@ -5719,10 +5787,10 @@ ${codebaseAnalysis.routes.map((r) => `- ${r}`).join("\n")}
5719
5787
  ${codebaseAnalysis.prismaModels.map((m) => `- ${m}`).join("\n")}
5720
5788
 
5721
5789
  **Components:**
5722
- ${codebaseAnalysis.components.slice(0, 15).map((c10) => `- ${c10}`).join("\n")}
5790
+ ${codebaseAnalysis.components.slice(0, 15).map((c11) => `- ${c11}`).join("\n")}
5723
5791
 
5724
5792
  **Recent Git Commits:**
5725
- ${codebaseAnalysis.recentCommits.slice(0, 8).map((c10) => `- ${c10}`).join("\n")}
5793
+ ${codebaseAnalysis.recentCommits.slice(0, 8).map((c11) => `- ${c11}`).join("\n")}
5726
5794
 
5727
5795
  **Completed Tasks:**
5728
5796
  ${context.completedTasks.slice(0, 10).map((t) => `- ${t.title}`).join("\n") || "None"}
@@ -6369,7 +6437,7 @@ var doctorCommand = new Command25("doctor").description("Diagnose Mr. Manager CL
6369
6437
  console.log("");
6370
6438
  return;
6371
6439
  }
6372
- const fixes = checks.filter((c10) => !c10.ok && c10.fix && !c10.optional);
6440
+ const fixes = checks.filter((c11) => !c11.ok && c11.fix && !c11.optional);
6373
6441
  if (fixes.length > 0) {
6374
6442
  console.log(paint5("yellow", " To fix:"));
6375
6443
  for (const fix of fixes) {
@@ -6600,6 +6668,106 @@ ${r.jobType} [${r.identifier}]`);
6600
6668
  }
6601
6669
  });
6602
6670
 
6671
+ // cli/commands/skill.ts
6672
+ import { Command as Command27 } from "commander";
6673
+ var c10 = {
6674
+ reset: "\x1B[0m",
6675
+ bold: "\x1B[1m",
6676
+ dim: "\x1B[2m",
6677
+ cyan: "\x1B[36m",
6678
+ green: "\x1B[32m",
6679
+ yellow: "\x1B[33m"
6680
+ };
6681
+ var skillCommand = new Command27("skill").description("Manage skills \u2014 reusable playbooks for AI agents");
6682
+ skillCommand.command("list").alias("ls").description("List all skills").option("--category <category>", "Filter by category").action(async (opts) => {
6683
+ const params = new URLSearchParams();
6684
+ if (opts.category) params.set("category", opts.category);
6685
+ const skills = await api.get(
6686
+ `/api/skills${params.toString() ? `?${params}` : ""}`
6687
+ );
6688
+ if (skills.length === 0) {
6689
+ console.log(`${c10.dim}No skills found.${c10.reset}`);
6690
+ return;
6691
+ }
6692
+ for (const skill of skills) {
6693
+ const cat = skill.category ? ` ${c10.dim}[${skill.category}]${c10.reset}` : "";
6694
+ const scope = skill.projectId ? ` ${c10.dim}(project)${c10.reset}` : ` ${c10.dim}(global)${c10.reset}`;
6695
+ console.log(` ${c10.cyan}${skill.name}${c10.reset}${cat}${scope}`);
6696
+ if (skill.description) {
6697
+ console.log(` ${c10.dim}${skill.description}${c10.reset}`);
6698
+ }
6699
+ console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
6700
+ }
6701
+ });
6702
+ skillCommand.command("create").description("Create a new skill from a markdown file or inline content").argument("<name>", "Skill name").option("-d, --description <desc>", "Short description").option("-c, --category <cat>", "Category (e.g. Deployment, Testing)").option("-f, --file <path>", "Read content from a markdown file").option("--content <text>", "Inline markdown content").option("-p, --project", "Scope to the linked project").action(async (name, opts) => {
6703
+ let content = opts.content ?? "";
6704
+ if (opts.file) {
6705
+ const { readFileSync: readFileSync13 } = await import("fs");
6706
+ try {
6707
+ content = readFileSync13(opts.file, "utf-8");
6708
+ } catch (err) {
6709
+ console.error(`Failed to read file: ${err.message}`);
6710
+ process.exit(1);
6711
+ }
6712
+ }
6713
+ if (!content.trim()) {
6714
+ console.error("Content is required. Use --file <path> or --content <text>.");
6715
+ process.exit(1);
6716
+ }
6717
+ let projectId = null;
6718
+ if (opts.project) {
6719
+ projectId = getLinkedProjectId();
6720
+ if (!projectId) {
6721
+ console.error(
6722
+ 'No project linked to this directory. Run "mr link <project-id>" first.'
6723
+ );
6724
+ process.exit(1);
6725
+ }
6726
+ }
6727
+ const skill = await api.post("/api/skills", {
6728
+ name,
6729
+ description: opts.description ?? null,
6730
+ content: content.trim(),
6731
+ category: opts.category ?? null,
6732
+ projectId
6733
+ });
6734
+ console.log(
6735
+ `${c10.green}Created skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset} ${c10.dim}(${skill.id})${c10.reset}`
6736
+ );
6737
+ });
6738
+ skillCommand.command("generate").alias("gen").description("Generate a new skill using AI from a text prompt").argument("<prompt>", "Describe the skill to generate").option("-p, --project", "Scope to the linked project").action(async (prompt2, opts) => {
6739
+ let projectId = null;
6740
+ if (opts.project) {
6741
+ projectId = getLinkedProjectId();
6742
+ if (!projectId) {
6743
+ console.error(
6744
+ 'No project linked to this directory. Run "mr link <project-id>" first.'
6745
+ );
6746
+ process.exit(1);
6747
+ }
6748
+ }
6749
+ console.log(`${c10.dim}Generating skill...${c10.reset}`);
6750
+ try {
6751
+ const skill = await api.post("/api/skills/generate", {
6752
+ prompt: prompt2,
6753
+ projectId
6754
+ });
6755
+ console.log(
6756
+ `${c10.green}Generated skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset}`
6757
+ );
6758
+ if (skill.description) {
6759
+ console.log(` ${c10.dim}${skill.description}${c10.reset}`);
6760
+ }
6761
+ if (skill.category) {
6762
+ console.log(` ${c10.dim}Category: ${skill.category}${c10.reset}`);
6763
+ }
6764
+ console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
6765
+ } catch (err) {
6766
+ console.error(`Failed to generate skill: ${err.message}`);
6767
+ process.exit(1);
6768
+ }
6769
+ });
6770
+
6603
6771
  // cli/index.ts
6604
6772
  var configPath = join12(homedir3(), ".mr-manager", "config.json");
6605
6773
  var isFirstRun = !existsSync17(configPath);
@@ -6607,7 +6775,7 @@ var userArgs = process.argv.slice(2);
6607
6775
  var bypassCommands = /* @__PURE__ */ new Set(["login", "init", "auth", "help", "--help", "-h", "--version", "-V", "doctor", "setup"]);
6608
6776
  var shouldBypass = userArgs.length > 0 && bypassCommands.has(userArgs[0]);
6609
6777
  if (isFirstRun && !shouldBypass) {
6610
- const c10 = {
6778
+ const c11 = {
6611
6779
  reset: "\x1B[0m",
6612
6780
  bold: "\x1B[1m",
6613
6781
  dim: "\x1B[2m",
@@ -6617,28 +6785,28 @@ if (isFirstRun && !shouldBypass) {
6617
6785
  magenta: "\x1B[35m"
6618
6786
  };
6619
6787
  console.log("");
6620
- console.log(`${c10.cyan} \u2554\u2566\u2557\u2566\u2550\u2557 \u2554\u2566\u2557\u2554\u2550\u2557\u2554\u2557\u2554\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2566\u2550\u2557${c10.reset}`);
6621
- console.log(`${c10.magenta} \u2551\u2551\u2551\u2560\u2566\u255D \u2551\u2551\u2551\u2560\u2550\u2563\u2551\u2551\u2551\u2560\u2550\u2563\u2551 \u2566\u2551\u2563 \u2560\u2566\u255D${c10.reset}`);
6622
- console.log(`${c10.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c10.reset}`);
6623
- console.log(`${c10.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c10.reset}`);
6788
+ console.log(`${c11.cyan} \u2554\u2566\u2557\u2566\u2550\u2557 \u2554\u2566\u2557\u2554\u2550\u2557\u2554\u2557\u2554\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2566\u2550\u2557${c11.reset}`);
6789
+ console.log(`${c11.magenta} \u2551\u2551\u2551\u2560\u2566\u255D \u2551\u2551\u2551\u2560\u2550\u2563\u2551\u2551\u2551\u2560\u2550\u2563\u2551 \u2566\u2551\u2563 \u2560\u2566\u255D${c11.reset}`);
6790
+ console.log(`${c11.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c11.reset}`);
6791
+ console.log(`${c11.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c11.reset}`);
6624
6792
  console.log("");
6625
- console.log(`${c10.bold} Welcome to Mr. Manager!${c10.reset}`);
6626
- console.log(`${c10.dim} Let's get you set up in a few quick steps.${c10.reset}`);
6793
+ console.log(`${c11.bold} Welcome to Mr. Manager!${c11.reset}`);
6794
+ console.log(`${c11.dim} Let's get you set up in a few quick steps.${c11.reset}`);
6627
6795
  console.log("");
6628
- console.log(` ${c10.yellow}Step 1:${c10.reset} Authenticate via Google OAuth`);
6629
- console.log(` ${c10.dim}Run:${c10.reset} ${c10.cyan}mr login${c10.reset}`);
6796
+ console.log(` ${c11.yellow}Step 1:${c11.reset} Authenticate via Google OAuth`);
6797
+ console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr login${c11.reset}`);
6630
6798
  console.log("");
6631
- console.log(` ${c10.yellow}Step 2:${c10.reset} Verify your environment`);
6632
- console.log(` ${c10.dim}Run:${c10.reset} ${c10.cyan}mr setup${c10.reset}`);
6799
+ console.log(` ${c11.yellow}Step 2:${c11.reset} Verify your environment`);
6800
+ console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr setup${c11.reset}`);
6633
6801
  console.log("");
6634
- console.log(` ${c10.yellow}Step 3:${c10.reset} Link a repo and start watching`);
6635
- console.log(` ${c10.dim}Run:${c10.reset} ${c10.cyan}mr link${c10.reset} ${c10.dim}&&${c10.reset} ${c10.cyan}mr watch${c10.reset}`);
6802
+ console.log(` ${c11.yellow}Step 3:${c11.reset} Link a repo and start watching`);
6803
+ console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr link${c11.reset} ${c11.dim}&&${c11.reset} ${c11.cyan}mr watch${c11.reset}`);
6636
6804
  console.log("");
6637
- console.log(`${c10.dim} Or run ${c10.reset}${c10.cyan}mr login${c10.reset}${c10.dim} to get started now.${c10.reset}`);
6805
+ console.log(`${c11.dim} Or run ${c11.reset}${c11.cyan}mr login${c11.reset}${c11.dim} to get started now.${c11.reset}`);
6638
6806
  console.log("");
6639
6807
  process.exit(0);
6640
6808
  }
6641
- var program = new Command27();
6809
+ var program = new Command28();
6642
6810
  program.name("mr").description("Mr. Manager - Task and project management CLI").version(CLI_VERSION);
6643
6811
  program.addCommand(initCommand);
6644
6812
  program.addCommand(authCommand);
@@ -6669,4 +6837,5 @@ program.addCommand(scanCommand);
6669
6837
  program.addCommand(ideaCommand);
6670
6838
  program.addCommand(doctorCommand);
6671
6839
  program.addCommand(promptAuditCommand);
6840
+ program.addCommand(skillCommand);
6672
6841
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dunnewold-labs/mr-manager",
3
- "version": "0.4.16",
3
+ "version": "0.4.20",
4
4
  "description": "Mr. Manager - Task and project management CLI",
5
5
  "bin": {
6
6
  "mr": "./dist/index.mjs"