@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.
- package/dist/index.mjs +198 -29
- 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
|
|
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.
|
|
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
|
|
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((
|
|
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((
|
|
4575
|
-
const ghAuthed = checks.find((
|
|
4576
|
-
const mrAuthed = checks.find((
|
|
4577
|
-
const claudeCheck = checks.find((
|
|
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((
|
|
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
|
|
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((
|
|
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((
|
|
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((
|
|
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
|
|
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(`${
|
|
6621
|
-
console.log(`${
|
|
6622
|
-
console.log(`${
|
|
6623
|
-
console.log(`${
|
|
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(`${
|
|
6626
|
-
console.log(`${
|
|
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(` ${
|
|
6629
|
-
console.log(` ${
|
|
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(` ${
|
|
6632
|
-
console.log(` ${
|
|
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(` ${
|
|
6635
|
-
console.log(` ${
|
|
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(`${
|
|
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
|
|
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();
|