@dunnewold-labs/mr-manager 0.4.35 → 0.4.37
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 +134 -77
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// cli/index.ts
|
|
4
|
-
import { Command as
|
|
5
|
-
import { existsSync as
|
|
4
|
+
import { Command as Command31 } from "commander";
|
|
5
|
+
import { existsSync as existsSync18 } from "fs";
|
|
6
6
|
import { homedir as homedir3 } from "os";
|
|
7
7
|
import { join as join12 } from "path";
|
|
8
8
|
|
|
@@ -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.37",
|
|
189
189
|
description: "Mr. Manager - Task and project management CLI",
|
|
190
190
|
bin: {
|
|
191
191
|
mr: "./dist/index.mjs"
|
|
@@ -354,8 +354,28 @@ var tasksCommand = new Command5("tasks").description("List tasks for the linked
|
|
|
354
354
|
console.log(JSON.stringify(tasks, null, 2));
|
|
355
355
|
});
|
|
356
356
|
|
|
357
|
-
// cli/commands/
|
|
357
|
+
// cli/commands/task-group.ts
|
|
358
358
|
import { Command as Command6 } from "commander";
|
|
359
|
+
var taskGroupCommand = new Command6("task-group").description("Group existing tasks under a new parent task with linked subtasks").requiredOption("--title <title>", "Parent task title").argument("<taskIds...>", "IDs of tasks to link as subtasks").action(async (taskIds, opts) => {
|
|
360
|
+
if (!Array.isArray(taskIds) || taskIds.length === 0) {
|
|
361
|
+
console.error("At least one task ID is required");
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
const parent = await api.post("/api/tasks/group", {
|
|
366
|
+
title: opts.title,
|
|
367
|
+
taskIds
|
|
368
|
+
});
|
|
369
|
+
console.log(parent.id);
|
|
370
|
+
} catch (err) {
|
|
371
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
372
|
+
console.error(`Failed to create parent task: ${message}`);
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// cli/commands/link.ts
|
|
378
|
+
import { Command as Command7 } from "commander";
|
|
359
379
|
import { createInterface } from "readline";
|
|
360
380
|
|
|
361
381
|
// cli/vcs.ts
|
|
@@ -402,7 +422,7 @@ function prompt(question) {
|
|
|
402
422
|
});
|
|
403
423
|
});
|
|
404
424
|
}
|
|
405
|
-
var linkCommand = new
|
|
425
|
+
var linkCommand = new Command7("link").description("Associate current directory with a project").argument("[project-id]", "Project ID to link").action(async (projectId) => {
|
|
406
426
|
if (!projectId) {
|
|
407
427
|
const current = getLinkedProjectId();
|
|
408
428
|
if (current) {
|
|
@@ -441,7 +461,7 @@ Created project: ${project.name} (${project.id})`);
|
|
|
441
461
|
);
|
|
442
462
|
}
|
|
443
463
|
});
|
|
444
|
-
var unlinkCommand = new
|
|
464
|
+
var unlinkCommand = new Command7("unlink").description("Remove current directory's project association").action(async () => {
|
|
445
465
|
const config = loadConfig();
|
|
446
466
|
const cwd = process.cwd();
|
|
447
467
|
if (!config.directories[cwd]) {
|
|
@@ -454,7 +474,7 @@ var unlinkCommand = new Command6("unlink").description("Remove current directory
|
|
|
454
474
|
});
|
|
455
475
|
|
|
456
476
|
// cli/commands/context.ts
|
|
457
|
-
import { Command as
|
|
477
|
+
import { Command as Command8 } from "commander";
|
|
458
478
|
import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
|
|
459
479
|
import { join as join3, sep } from "path";
|
|
460
480
|
var FEATURES_FILE = ".mr-features.md";
|
|
@@ -472,7 +492,7 @@ function readFeatures() {
|
|
|
472
492
|
if (!existsSync3(path)) return null;
|
|
473
493
|
return readFileSync3(path, "utf-8");
|
|
474
494
|
}
|
|
475
|
-
var contextCommand = new
|
|
495
|
+
var contextCommand = new Command8("context").description("Output project context JSON for Claude Code").option("--install", "Install Claude Code command to .claude/commands/").action(async (opts) => {
|
|
476
496
|
if (opts.install) {
|
|
477
497
|
const dir = join3(process.cwd(), ".claude", "commands");
|
|
478
498
|
if (!existsSync3(dir)) {
|
|
@@ -527,7 +547,7 @@ var contextCommand = new Command7("context").description("Output project context
|
|
|
527
547
|
});
|
|
528
548
|
|
|
529
549
|
// cli/commands/watch.ts
|
|
530
|
-
import { Command as
|
|
550
|
+
import { Command as Command9 } from "commander";
|
|
531
551
|
import { spawn as spawn4, exec } from "child_process";
|
|
532
552
|
import { randomUUID } from "crypto";
|
|
533
553
|
import { resolve as resolve2 } from "path";
|
|
@@ -1950,11 +1970,18 @@ function buildExecutionPrompt(task, repoDir, subtasks, vcs = "github", protoRefs
|
|
|
1950
1970
|
const workingDir = executionDir ?? repoDir;
|
|
1951
1971
|
const prBodyPath = "/tmp/mr-pr-body.md";
|
|
1952
1972
|
const hasAttachedBranch = !!task.attachedBranch?.trim();
|
|
1953
|
-
const
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1973
|
+
const hasPrd = !!task.prdContent?.trim();
|
|
1974
|
+
const notes = hasPrd ? [
|
|
1975
|
+
``,
|
|
1976
|
+
``,
|
|
1977
|
+
`## Approved PRD (Product Requirements Document)`,
|
|
1978
|
+
``,
|
|
1979
|
+
`A PRD for this task has been generated and approved by the user. Your job now is to IMPLEMENT the PRD below in code. The planning/proposal phase is already complete \u2014 do not produce another plan or proposal.`,
|
|
1980
|
+
``,
|
|
1981
|
+
`Even if the task title includes words like "proposal", "plan", "design", "spec", or "PRD", treat the PRD below as the finalized spec and deliver working code that fulfills it. Only signal "no MR/PR needed" if the PRD itself explicitly calls out that no code changes are required.`,
|
|
1982
|
+
``,
|
|
1983
|
+
task.prdContent
|
|
1984
|
+
].join("\n") : task.notes ? `
|
|
1958
1985
|
|
|
1959
1986
|
Task notes:
|
|
1960
1987
|
${task.notes}` : "";
|
|
@@ -2041,6 +2068,10 @@ ${task.notes}` : "";
|
|
|
2041
2068
|
`\`mr no-mr ${task.id} "Brief description of what was done instead"\``,
|
|
2042
2069
|
``,
|
|
2043
2070
|
`This tells the watch system to skip looking for a ${vcs === "gitlab" ? "MR" : "PR"} and records what action was taken. You should still clean up any worktrees and exit normally.`,
|
|
2071
|
+
...hasPrd ? [
|
|
2072
|
+
``,
|
|
2073
|
+
`**Important:** This task has an approved PRD, which means the user has already committed to implementation. Do NOT use \`mr no-mr\` just because the task title mentions "proposal", "plan", or because a PRD already exists \u2014 the PRD is the starting point for your implementation, not the deliverable. Only use \`mr no-mr\` if the PRD itself explicitly states that no code changes are required.`
|
|
2074
|
+
] : [],
|
|
2044
2075
|
``,
|
|
2045
2076
|
...hasFeedback || hasAttachedBranch && task.attachedBranchLink && isPrOrMrUrl(task.attachedBranchLink) ? [] : [
|
|
2046
2077
|
`## PR Description Template`,
|
|
@@ -2560,7 +2591,7 @@ function spawnAgent(agent, repoDir, prompt2, prefix, onActivity, sessionId, name
|
|
|
2560
2591
|
});
|
|
2561
2592
|
return child;
|
|
2562
2593
|
}
|
|
2563
|
-
var watchCommand = new
|
|
2594
|
+
var watchCommand = new Command9("watch").description(
|
|
2564
2595
|
"Watch for in-progress tasks and autonomously dispatch an AI coding agent to work on them"
|
|
2565
2596
|
).option("--interval <seconds>", "Polling interval in seconds", "15").option("--dry-run", "Show what would be dispatched without spawning the agent", false).option("--plan-approval", "Show the agent's plan and ask for approval before executing", false).option("--root <dir>", "Root directory filter for linked repos (default: cwd)").option("--agent <agent>", "AI agent to use: claude, codex, or gemini", "claude").option("--scan-at <HH:MM>", "Run a product scan daily at this time (e.g., 02:00)").action(async (opts) => {
|
|
2566
2597
|
const intervalMs = parseInt(opts.interval, 10) * 1e3;
|
|
@@ -4066,7 +4097,7 @@ ${timestamp()} ${watchTag()} Shutting down\u2026`);
|
|
|
4066
4097
|
});
|
|
4067
4098
|
|
|
4068
4099
|
// cli/commands/start.ts
|
|
4069
|
-
import { Command as
|
|
4100
|
+
import { Command as Command10 } from "commander";
|
|
4070
4101
|
var c2 = {
|
|
4071
4102
|
reset: "\x1B[0m",
|
|
4072
4103
|
bold: "\x1B[1m",
|
|
@@ -4092,19 +4123,19 @@ function printTaskBanner(action, title, id) {
|
|
|
4092
4123
|
``
|
|
4093
4124
|
].join("\n"));
|
|
4094
4125
|
}
|
|
4095
|
-
var startCommand = new
|
|
4126
|
+
var startCommand = new Command10("start").description("Mark a task as in-progress (you are working on it)").argument("<task-id>", "Task ID to start").action(async (taskId) => {
|
|
4096
4127
|
const task = await api.patch(`/api/tasks/${taskId}`, {
|
|
4097
4128
|
status: "in_progress"
|
|
4098
4129
|
});
|
|
4099
4130
|
printTaskBanner(paint2("green", "start"), task.title, task.id);
|
|
4100
4131
|
});
|
|
4101
|
-
var delegateCommand = new
|
|
4132
|
+
var delegateCommand = new Command10("delegate").description("Queue a task for the watch agent to pick up").argument("<task-id>", "Task ID to delegate").action(async (taskId) => {
|
|
4102
4133
|
const task = await api.patch(`/api/tasks/${taskId}`, {
|
|
4103
4134
|
status: "queued"
|
|
4104
4135
|
});
|
|
4105
4136
|
printTaskBanner(paint2("cyan", "delegate"), task.title, task.id);
|
|
4106
4137
|
});
|
|
4107
|
-
var undelegateCommand = new
|
|
4138
|
+
var undelegateCommand = new Command10("undelegate").description("Remove delegation from a task and return to todo").argument("<task-id>", "Task ID to undelegate").action(async (taskId) => {
|
|
4108
4139
|
const task = await api.patch(`/api/tasks/${taskId}`, {
|
|
4109
4140
|
status: "todo",
|
|
4110
4141
|
mode: "development"
|
|
@@ -4113,8 +4144,8 @@ var undelegateCommand = new Command9("undelegate").description("Remove delegatio
|
|
|
4113
4144
|
});
|
|
4114
4145
|
|
|
4115
4146
|
// cli/commands/create.ts
|
|
4116
|
-
import { Command as
|
|
4117
|
-
var createCommand = new
|
|
4147
|
+
import { Command as Command11 } from "commander";
|
|
4148
|
+
var createCommand = new Command11("create").description("Create a new task in the linked project").argument("<title>", "Task title").option("--notes <notes>", "Task notes").option("--in-progress", "Mark the task as in-progress immediately").action(async (title, opts) => {
|
|
4118
4149
|
const projectId = getLinkedProjectId();
|
|
4119
4150
|
if (!projectId) {
|
|
4120
4151
|
console.error(
|
|
@@ -4133,7 +4164,7 @@ var createCommand = new Command10("create").description("Create a new task in th
|
|
|
4133
4164
|
});
|
|
4134
4165
|
|
|
4135
4166
|
// cli/commands/complete.ts
|
|
4136
|
-
import { Command as
|
|
4167
|
+
import { Command as Command12 } from "commander";
|
|
4137
4168
|
var c3 = {
|
|
4138
4169
|
reset: "\x1B[0m",
|
|
4139
4170
|
bold: "\x1B[1m",
|
|
@@ -4159,7 +4190,7 @@ function printTaskBanner2(action, title, id) {
|
|
|
4159
4190
|
``
|
|
4160
4191
|
].join("\n"));
|
|
4161
4192
|
}
|
|
4162
|
-
var completeCommand = new
|
|
4193
|
+
var completeCommand = new Command12("complete").description("Mark a task as completed").argument("<task-id>", "Task ID to complete").action(async (taskId) => {
|
|
4163
4194
|
const task = await api.patch(`/api/tasks/${taskId}`, {
|
|
4164
4195
|
status: "completed"
|
|
4165
4196
|
});
|
|
@@ -4167,8 +4198,8 @@ var completeCommand = new Command11("complete").description("Mark a task as comp
|
|
|
4167
4198
|
});
|
|
4168
4199
|
|
|
4169
4200
|
// cli/commands/subtask-complete.ts
|
|
4170
|
-
import { Command as
|
|
4171
|
-
var subtaskCompleteCommand = new
|
|
4201
|
+
import { Command as Command13 } from "commander";
|
|
4202
|
+
var subtaskCompleteCommand = new Command13("subtask-complete").description("Mark a subtask as completed").argument("<task-id>", "Parent task ID").argument("<subtask-id>", "Subtask ID to complete").action(async (taskId, subtaskId) => {
|
|
4172
4203
|
const subtask = await api.patch(
|
|
4173
4204
|
`/api/tasks/${taskId}/subtasks/${subtaskId}`,
|
|
4174
4205
|
{ completed: true }
|
|
@@ -4177,7 +4208,7 @@ var subtaskCompleteCommand = new Command12("subtask-complete").description("Mark
|
|
|
4177
4208
|
});
|
|
4178
4209
|
|
|
4179
4210
|
// cli/commands/prototype.ts
|
|
4180
|
-
import { Command as
|
|
4211
|
+
import { Command as Command14 } from "commander";
|
|
4181
4212
|
var c4 = {
|
|
4182
4213
|
reset: "\x1B[0m",
|
|
4183
4214
|
bold: "\x1B[1m",
|
|
@@ -4206,8 +4237,8 @@ function statusBadge(status) {
|
|
|
4206
4237
|
return paint4("gray", status);
|
|
4207
4238
|
}
|
|
4208
4239
|
}
|
|
4209
|
-
var prototypeCommand = new
|
|
4210
|
-
new
|
|
4240
|
+
var prototypeCommand = new Command14("prototype").description("Manage prototypes").addCommand(
|
|
4241
|
+
new Command14("list").description("List prototypes for the linked project").option("--all", "Show prototypes for all projects").action(async (opts) => {
|
|
4211
4242
|
const params = new URLSearchParams();
|
|
4212
4243
|
if (!opts.all) {
|
|
4213
4244
|
const projectId = getLinkedProjectId();
|
|
@@ -4240,7 +4271,7 @@ var prototypeCommand = new Command13("prototype").description("Manage prototypes
|
|
|
4240
4271
|
}
|
|
4241
4272
|
})
|
|
4242
4273
|
).addCommand(
|
|
4243
|
-
new
|
|
4274
|
+
new Command14("create").description("Create a new prototype").argument("<title>", "Title of the prototype").requiredOption("--prompt <prompt>", "Design description / prompt").option("--project <projectId>", "Project ID (defaults to linked project, when available)").option("--variants <count>", "Number of variants to generate (1-50)", "5").option("--type <type>", "Prototype type: web_app, mobile_app, desktop_app, logo (default: web_app)", "web_app").action(async (title, opts) => {
|
|
4244
4275
|
const projectId = opts.project ?? getLinkedProjectId();
|
|
4245
4276
|
const variantCount = Math.max(1, Math.min(50, parseInt(opts.variants, 10) || 5));
|
|
4246
4277
|
const validTypes = ["web_app", "mobile_app", "desktop_app", "logo"];
|
|
@@ -4269,7 +4300,7 @@ var prototypeCommand = new Command13("prototype").description("Manage prototypes
|
|
|
4269
4300
|
console.log();
|
|
4270
4301
|
})
|
|
4271
4302
|
).addCommand(
|
|
4272
|
-
new
|
|
4303
|
+
new Command14("start").description("Start prototype generation (sets status to in_progress)").argument("<id>", "Prototype ID").action(async (id) => {
|
|
4273
4304
|
const prototype = await api.patch(`/api/prototypes/${id}`, {
|
|
4274
4305
|
status: "in_progress"
|
|
4275
4306
|
});
|
|
@@ -4279,7 +4310,7 @@ var prototypeCommand = new Command13("prototype").description("Manage prototypes
|
|
|
4279
4310
|
console.log();
|
|
4280
4311
|
})
|
|
4281
4312
|
).addCommand(
|
|
4282
|
-
new
|
|
4313
|
+
new Command14("retry").description("Retry a failed prototype").argument("<id>", "Prototype ID").action(async (id) => {
|
|
4283
4314
|
const prototype = await api.patch(`/api/prototypes/${id}`, {
|
|
4284
4315
|
status: "in_progress",
|
|
4285
4316
|
files: null
|
|
@@ -4291,7 +4322,7 @@ var prototypeCommand = new Command13("prototype").description("Manage prototypes
|
|
|
4291
4322
|
);
|
|
4292
4323
|
|
|
4293
4324
|
// cli/commands/setup.ts
|
|
4294
|
-
import { Command as
|
|
4325
|
+
import { Command as Command15 } from "commander";
|
|
4295
4326
|
import { exec as exec2 } from "child_process";
|
|
4296
4327
|
var c5 = {
|
|
4297
4328
|
reset: "\x1B[0m",
|
|
@@ -4594,7 +4625,7 @@ async function autoFix(checks, agent) {
|
|
|
4594
4625
|
console.log("");
|
|
4595
4626
|
}
|
|
4596
4627
|
}
|
|
4597
|
-
var setupCommand = new
|
|
4628
|
+
var setupCommand = new Command15("setup").description("Check that all dependencies for mr watch are installed and configured").option("--fix", "Attempt to auto-fix issues where possible", false).option("--agent <agent>", "AI agent to check: claude, codex, or gemini (default: claude)", "claude").action(async (opts) => {
|
|
4598
4629
|
const agent = opts.agent === "codex" ? "codex" : opts.agent === "gemini" ? "gemini" : "claude";
|
|
4599
4630
|
const banner = [
|
|
4600
4631
|
``,
|
|
@@ -4644,8 +4675,8 @@ var setupCommand = new Command14("setup").description("Check that all dependenci
|
|
|
4644
4675
|
});
|
|
4645
4676
|
|
|
4646
4677
|
// cli/commands/update.ts
|
|
4647
|
-
import { Command as
|
|
4648
|
-
var updateCommand = new
|
|
4678
|
+
import { Command as Command16 } from "commander";
|
|
4679
|
+
var updateCommand = new Command16("update").description("Post a status update to a task, or attach a resource").argument("<task-id>", "Task ID").argument("[message-or-title]", "Status update message, or resource title when using --resource").argument("[content]", "Resource content (only used with --resource)").option("--source <source>", "Update source: agent, system, or user", "agent").option("--resource <type>", "Create a task resource (e.g. test-plan, note, plan, research)").action(async (taskId, messageOrTitle, content, opts) => {
|
|
4649
4680
|
if (opts.resource) {
|
|
4650
4681
|
if (!messageOrTitle || !content) {
|
|
4651
4682
|
console.error(`Usage: mr update <task-id> --resource <type> "<title>" '<content>'`);
|
|
@@ -4671,11 +4702,11 @@ var updateCommand = new Command15("update").description("Post a status update to
|
|
|
4671
4702
|
});
|
|
4672
4703
|
|
|
4673
4704
|
// cli/commands/screenshot.ts
|
|
4674
|
-
import { Command as
|
|
4705
|
+
import { Command as Command17 } from "commander";
|
|
4675
4706
|
import { readFileSync as readFileSync6, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
|
|
4676
4707
|
import { join as join7 } from "path";
|
|
4677
4708
|
import { tmpdir } from "os";
|
|
4678
|
-
var screenshotCommand = new
|
|
4709
|
+
var screenshotCommand = new Command17("screenshot").description(
|
|
4679
4710
|
"Take or attach a screenshot to a task update (agents use this to show their work)"
|
|
4680
4711
|
).argument("<task-id>", "Task ID").argument("[file]", "Path to an image file (if omitted, uses headless browser to screenshot the app)").option("-m, --message <message>", "Optional message to include with the screenshot").option("-u, --url <url>", "Custom URL to screenshot (defaults to the task's project page)").action(async (taskId, file, opts) => {
|
|
4681
4712
|
let filePath = file;
|
|
@@ -4766,7 +4797,7 @@ var screenshotCommand = new Command16("screenshot").description(
|
|
|
4766
4797
|
});
|
|
4767
4798
|
|
|
4768
4799
|
// cli/commands/resume.ts
|
|
4769
|
-
import { Command as
|
|
4800
|
+
import { Command as Command18 } from "commander";
|
|
4770
4801
|
import { spawn as spawn5 } from "child_process";
|
|
4771
4802
|
import { resolve as resolve3 } from "path";
|
|
4772
4803
|
var c6 = {
|
|
@@ -4783,7 +4814,7 @@ var c6 = {
|
|
|
4783
4814
|
function paint6(color, text) {
|
|
4784
4815
|
return `${c6[color]}${text}${c6.reset}`;
|
|
4785
4816
|
}
|
|
4786
|
-
var resumeCommand = new
|
|
4817
|
+
var resumeCommand = new Command18("resume").description("Resume an interactive Claude session for a task (non-headless)").argument("<task-id>", "Task ID whose Claude session to resume").option("--dir <directory>", "Override the working directory for the session").action(async (taskId, opts) => {
|
|
4787
4818
|
const task = await api.get(`/api/tasks/${taskId}`);
|
|
4788
4819
|
if (!task.claudeSessionId) {
|
|
4789
4820
|
console.error(
|
|
@@ -4852,7 +4883,7 @@ var resumeCommand = new Command17("resume").description("Resume an interactive C
|
|
|
4852
4883
|
});
|
|
4853
4884
|
|
|
4854
4885
|
// cli/commands/browse.ts
|
|
4855
|
-
import { Command as
|
|
4886
|
+
import { Command as Command19 } from "commander";
|
|
4856
4887
|
import { execSync as execSync4, spawn as spawn6 } from "child_process";
|
|
4857
4888
|
import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
4858
4889
|
import { createHash } from "crypto";
|
|
@@ -4967,7 +4998,7 @@ async function ensureDevServer(options = {}) {
|
|
|
4967
4998
|
}
|
|
4968
4999
|
throw new Error(`Dev server failed to start within 60s. Command: ${devCmd} in ${projectCwd}`);
|
|
4969
5000
|
}
|
|
4970
|
-
var browseCommand = new
|
|
5001
|
+
var browseCommand = new Command19("browse").description("Control a headless browser for QA and testing").argument("[command]", "Browse command (goto, click, fill, screenshot, etc.)").argument("[args...]", "Command arguments").option(
|
|
4971
5002
|
"--task-id <id>",
|
|
4972
5003
|
"Attach output to a task update (for screenshot and recording-stop commands)"
|
|
4973
5004
|
).option("--dev", "Auto-start local dev server before browsing").option("--dev-cwd <path>", "Working directory for the dev server (defaults to mr-manager root)").option("--dev-cmd <command>", "Dev server command to run (auto-detected from package.json if omitted)").option("--dev-port-flag <flag>", "CLI flag name used to set port (e.g. --port). Omit to use PORT env var.").allowUnknownOption(true).action(
|
|
@@ -5110,10 +5141,10 @@ var browseCommand = new Command18("browse").description("Control a headless brow
|
|
|
5110
5141
|
);
|
|
5111
5142
|
|
|
5112
5143
|
// cli/commands/set-path.ts
|
|
5113
|
-
import { Command as
|
|
5144
|
+
import { Command as Command20 } from "commander";
|
|
5114
5145
|
import { resolve as resolve5 } from "path";
|
|
5115
5146
|
import { existsSync as existsSync10 } from "fs";
|
|
5116
|
-
var setPathCommand = new
|
|
5147
|
+
var setPathCommand = new Command20("set-path").description("Set or update the local repo path for a project").argument("<project-id>", "Project ID").argument("<path>", "Absolute or relative path to the local repo").action(async (projectId, pathArg) => {
|
|
5117
5148
|
const absolutePath = resolve5(pathArg);
|
|
5118
5149
|
if (!existsSync10(absolutePath)) {
|
|
5119
5150
|
console.error(`Error: Path does not exist: ${absolutePath}`);
|
|
@@ -5131,9 +5162,9 @@ var setPathCommand = new Command19("set-path").description("Set or update the lo
|
|
|
5131
5162
|
});
|
|
5132
5163
|
|
|
5133
5164
|
// cli/commands/test.ts
|
|
5134
|
-
import { Command as
|
|
5165
|
+
import { Command as Command21 } from "commander";
|
|
5135
5166
|
import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
|
|
5136
|
-
var testCommand = new
|
|
5167
|
+
var testCommand = new Command21("test").description("Run automated browser test for a task's MR/PR").argument("<task-id>", "Task ID to test").option("--plan <file>", "Path to a custom test plan JSON file").option("--no-recording", "Disable proof recording for this run").action(async (taskId, opts) => {
|
|
5137
5168
|
const config = loadConfig();
|
|
5138
5169
|
console.log("[test] Fetching task...");
|
|
5139
5170
|
let task;
|
|
@@ -5295,7 +5326,7 @@ var testCommand = new Command20("test").description("Run automated browser test
|
|
|
5295
5326
|
});
|
|
5296
5327
|
|
|
5297
5328
|
// cli/commands/features.ts
|
|
5298
|
-
import { Command as
|
|
5329
|
+
import { Command as Command22 } from "commander";
|
|
5299
5330
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync12 } from "fs";
|
|
5300
5331
|
import { resolve as resolve6, sep as sep2 } from "path";
|
|
5301
5332
|
var FEATURES_FILE3 = ".mr-features.md";
|
|
@@ -5329,7 +5360,7 @@ function readFeatures2() {
|
|
|
5329
5360
|
if (!existsSync12(path)) return null;
|
|
5330
5361
|
return readFileSync9(path, "utf-8");
|
|
5331
5362
|
}
|
|
5332
|
-
var featuresCommand = new
|
|
5363
|
+
var featuresCommand = new Command22("features").description("View or update the project features & goals document (.mr-features.md)").option("--update <content>", "Replace the features document with the given content").option("--file <path>", "Read content from a file and use it to update the features document").option("--path", "Print the path to the features file").action(async (opts) => {
|
|
5333
5364
|
if (opts.path) {
|
|
5334
5365
|
console.log(getFeaturesPath());
|
|
5335
5366
|
return;
|
|
@@ -5357,11 +5388,11 @@ var featuresCommand = new Command21("features").description("View or update the
|
|
|
5357
5388
|
});
|
|
5358
5389
|
|
|
5359
5390
|
// cli/commands/no-mr.ts
|
|
5360
|
-
import { Command as
|
|
5391
|
+
import { Command as Command23 } from "commander";
|
|
5361
5392
|
import { writeFileSync as writeFileSync6 } from "fs";
|
|
5362
5393
|
import { resolve as resolve7 } from "path";
|
|
5363
5394
|
var NO_MR_FILE = ".mr-no-mr";
|
|
5364
|
-
var noMrCommand = new
|
|
5395
|
+
var noMrCommand = new Command23("no-mr").description("Signal that a task does not require a merge/pull request and describe what was done instead").argument("<task-id>", "Task ID").argument("<description>", "Description of what was done instead of creating an MR/PR").action(async (taskId, description) => {
|
|
5365
5396
|
const filePath = resolve7(process.cwd(), NO_MR_FILE);
|
|
5366
5397
|
writeFileSync6(filePath, description, "utf-8");
|
|
5367
5398
|
await api.post(`/api/tasks/${taskId}/updates`, {
|
|
@@ -5373,8 +5404,9 @@ var noMrCommand = new Command22("no-mr").description("Signal that a task does no
|
|
|
5373
5404
|
});
|
|
5374
5405
|
|
|
5375
5406
|
// cli/commands/review.ts
|
|
5376
|
-
import { Command as
|
|
5407
|
+
import { Command as Command24 } from "commander";
|
|
5377
5408
|
import { spawn as spawn7, execSync as execSync5 } from "child_process";
|
|
5409
|
+
import { existsSync as existsSync13, statSync as statSync2 } from "fs";
|
|
5378
5410
|
var c8 = {
|
|
5379
5411
|
reset: "\x1B[0m",
|
|
5380
5412
|
bold: "\x1B[1m",
|
|
@@ -5405,7 +5437,7 @@ function logOk(msg) {
|
|
|
5405
5437
|
function logErr(msg) {
|
|
5406
5438
|
console.error(`${timestamp2()} ${tag()} ${paint8("red", "\u2717")} ${msg}`);
|
|
5407
5439
|
}
|
|
5408
|
-
var reviewCommand = new
|
|
5440
|
+
var reviewCommand = new Command24("review").description("Run an automated code review on a branch").option("--project <id>", "Project ID (defaults to linked project)").option("--report <id>", "Use an existing review report ID (created by UI trigger)").option("--branch <name>", "Branch to review (defaults to current branch)").option("--base <name>", "Base branch to diff against (defaults to main)").action(async (opts) => {
|
|
5409
5441
|
const config = loadConfig();
|
|
5410
5442
|
if (!config.apiKey) {
|
|
5411
5443
|
logErr('Not authenticated. Run "mr login" first.');
|
|
@@ -5445,6 +5477,26 @@ var reviewCommand = new Command23("review").description("Run an automated code r
|
|
|
5445
5477
|
if (!projectPath) {
|
|
5446
5478
|
projectPath = process.cwd();
|
|
5447
5479
|
}
|
|
5480
|
+
if (!existsSync13(projectPath)) {
|
|
5481
|
+
logErr(`Project path does not exist: ${projectPath}`);
|
|
5482
|
+
logErr(`Update the project's localPath or run "mr link" from the correct directory.`);
|
|
5483
|
+
process.exit(1);
|
|
5484
|
+
}
|
|
5485
|
+
try {
|
|
5486
|
+
if (!statSync2(projectPath).isDirectory()) {
|
|
5487
|
+
logErr(`Project path is not a directory: ${projectPath}`);
|
|
5488
|
+
process.exit(1);
|
|
5489
|
+
}
|
|
5490
|
+
} catch (err) {
|
|
5491
|
+
logErr(`Cannot stat project path ${projectPath}: ${err.message}`);
|
|
5492
|
+
process.exit(1);
|
|
5493
|
+
}
|
|
5494
|
+
if (!existsSync13(`${projectPath}/.git`)) {
|
|
5495
|
+
logErr(`Project path is not a git repository: ${projectPath}`);
|
|
5496
|
+
logErr(`Update the project's localPath to point to the local checkout.`);
|
|
5497
|
+
process.exit(1);
|
|
5498
|
+
}
|
|
5499
|
+
log(`Using project path: ${paint8("dim", projectPath)}`);
|
|
5448
5500
|
let branch = opts.branch;
|
|
5449
5501
|
if (!branch) {
|
|
5450
5502
|
try {
|
|
@@ -5691,13 +5743,13 @@ function parseReviewOutput(output) {
|
|
|
5691
5743
|
}
|
|
5692
5744
|
|
|
5693
5745
|
// cli/commands/scan.ts
|
|
5694
|
-
import { Command as
|
|
5746
|
+
import { Command as Command25 } from "commander";
|
|
5695
5747
|
|
|
5696
5748
|
// lib/scanner/index.ts
|
|
5697
5749
|
import { spawn as spawn8 } from "child_process";
|
|
5698
5750
|
|
|
5699
5751
|
// lib/scanner/config.ts
|
|
5700
|
-
import { readFileSync as readFileSync10, existsSync as
|
|
5752
|
+
import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
|
|
5701
5753
|
import { join as join9 } from "path";
|
|
5702
5754
|
var ALL_FINDING_TYPES = [
|
|
5703
5755
|
"idea",
|
|
@@ -5715,7 +5767,7 @@ var DEFAULTS = {
|
|
|
5715
5767
|
};
|
|
5716
5768
|
function loadScanConfig(projectPath) {
|
|
5717
5769
|
const configPath2 = join9(projectPath, ".mr-scan.json");
|
|
5718
|
-
if (!
|
|
5770
|
+
if (!existsSync14(configPath2)) {
|
|
5719
5771
|
return { ...DEFAULTS };
|
|
5720
5772
|
}
|
|
5721
5773
|
try {
|
|
@@ -5761,13 +5813,13 @@ async function authenticateBrowseSession(magicUrl, runBrowse) {
|
|
|
5761
5813
|
}
|
|
5762
5814
|
|
|
5763
5815
|
// lib/scanner/codebase-analysis.ts
|
|
5764
|
-
import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as
|
|
5816
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as existsSync15 } from "fs";
|
|
5765
5817
|
import { join as join10, relative } from "path";
|
|
5766
5818
|
import { execSync as execSync6 } from "child_process";
|
|
5767
5819
|
function resolveDir(projectPath, candidates) {
|
|
5768
5820
|
for (const candidate of candidates) {
|
|
5769
5821
|
const dir = join10(projectPath, candidate);
|
|
5770
|
-
if (
|
|
5822
|
+
if (existsSync15(dir)) return dir;
|
|
5771
5823
|
}
|
|
5772
5824
|
return null;
|
|
5773
5825
|
}
|
|
@@ -5801,7 +5853,7 @@ function discoverRoutes(projectPath) {
|
|
|
5801
5853
|
}
|
|
5802
5854
|
function extractModels(projectPath) {
|
|
5803
5855
|
const schemaPath = join10(projectPath, "prisma", "schema.prisma");
|
|
5804
|
-
if (
|
|
5856
|
+
if (existsSync15(schemaPath)) {
|
|
5805
5857
|
const content = readFileSync11(schemaPath, "utf-8");
|
|
5806
5858
|
const models2 = [];
|
|
5807
5859
|
const modelRegex = /^model\s+(\w+)\s*\{/gm;
|
|
@@ -5815,7 +5867,7 @@ function extractModels(projectPath) {
|
|
|
5815
5867
|
const drizzleDirs = ["src/db", "src/schema", "db", "drizzle"];
|
|
5816
5868
|
for (const dir of drizzleDirs) {
|
|
5817
5869
|
const fullDir = join10(projectPath, dir);
|
|
5818
|
-
if (!
|
|
5870
|
+
if (!existsSync15(fullDir)) continue;
|
|
5819
5871
|
try {
|
|
5820
5872
|
const entries = readdirSync2(fullDir, { withFileTypes: true });
|
|
5821
5873
|
for (const entry of entries) {
|
|
@@ -5856,7 +5908,7 @@ function discoverComponents(projectPath) {
|
|
|
5856
5908
|
function extractInternalLinks(projectPath) {
|
|
5857
5909
|
const links = /* @__PURE__ */ new Set();
|
|
5858
5910
|
function searchDir(dir) {
|
|
5859
|
-
if (!
|
|
5911
|
+
if (!existsSync15(dir)) return;
|
|
5860
5912
|
const entries = readdirSync2(dir, { withFileTypes: true });
|
|
5861
5913
|
for (const entry of entries) {
|
|
5862
5914
|
if (entry.isDirectory()) {
|
|
@@ -6453,7 +6505,7 @@ function logOk2(msg) {
|
|
|
6453
6505
|
function logErr2(msg) {
|
|
6454
6506
|
console.error(`${timestamp3()} ${scanTag()} ${paint9("red", "\u2717")} ${msg}`);
|
|
6455
6507
|
}
|
|
6456
|
-
var scanCommand = new
|
|
6508
|
+
var scanCommand = new Command25("scan").description("Run a product scan on the current project \u2014 analyzes codebase, crawls the app, and surfaces findings").option("--project <id>", "Project ID (defaults to linked project)").option("--report <id>", "Use an existing scan report ID (created by UI trigger)").option("--no-crawl", "Skip live crawl (codebase analysis only)").action(async (opts) => {
|
|
6457
6509
|
const config = loadConfig();
|
|
6458
6510
|
if (!config.apiKey) {
|
|
6459
6511
|
logErr2('Not authenticated. Run "mr login" first.');
|
|
@@ -6608,13 +6660,13 @@ var scanCommand = new Command24("scan").description("Run a product scan on the c
|
|
|
6608
6660
|
});
|
|
6609
6661
|
|
|
6610
6662
|
// cli/commands/doctor.ts
|
|
6611
|
-
import { Command as
|
|
6612
|
-
import { existsSync as
|
|
6663
|
+
import { Command as Command26 } from "commander";
|
|
6664
|
+
import { existsSync as existsSync16 } from "fs";
|
|
6613
6665
|
import { homedir as homedir2 } from "os";
|
|
6614
6666
|
import { join as join11 } from "path";
|
|
6615
6667
|
async function checkConfigExists() {
|
|
6616
6668
|
const configPath2 = join11(homedir2(), ".mr-manager", "config.json");
|
|
6617
|
-
const exists =
|
|
6669
|
+
const exists = existsSync16(configPath2);
|
|
6618
6670
|
if (!exists) {
|
|
6619
6671
|
return {
|
|
6620
6672
|
name: "Config file",
|
|
@@ -6656,7 +6708,7 @@ async function checkProjectLink() {
|
|
|
6656
6708
|
optional: true
|
|
6657
6709
|
};
|
|
6658
6710
|
}
|
|
6659
|
-
var doctorCommand = new
|
|
6711
|
+
var doctorCommand = new Command26("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
|
|
6660
6712
|
const banner = [
|
|
6661
6713
|
``,
|
|
6662
6714
|
paint5("cyan", ` MR DOCTOR`),
|
|
@@ -6699,14 +6751,14 @@ var doctorCommand = new Command25("doctor").description("Diagnose Mr. Manager CL
|
|
|
6699
6751
|
});
|
|
6700
6752
|
|
|
6701
6753
|
// cli/commands/prompt-audit.ts
|
|
6702
|
-
import { Command as
|
|
6754
|
+
import { Command as Command27 } from "commander";
|
|
6703
6755
|
import { resolve as resolve8 } from "path";
|
|
6704
|
-
import { existsSync as
|
|
6756
|
+
import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
|
|
6705
6757
|
function auditLine(label, tokens) {
|
|
6706
6758
|
const bar = "\u2588".repeat(Math.min(60, Math.round(tokens / 200)));
|
|
6707
6759
|
return ` ${label.padEnd(30)} ${formatTokenCount(tokens).padStart(8)} ${bar}`;
|
|
6708
6760
|
}
|
|
6709
|
-
var promptAuditCommand = new
|
|
6761
|
+
var promptAuditCommand = new Command27("prompt-audit").description("Dry-run prompt construction and report estimated token counts by job type").option("--task <id>", "Audit prompts for a specific task ID").option("--all", "Audit all supported job types with representative data", false).option("--json", "Output as JSON instead of plain text", false).action(async (opts) => {
|
|
6710
6762
|
const results = [];
|
|
6711
6763
|
if (opts.task) {
|
|
6712
6764
|
try {
|
|
@@ -6722,7 +6774,11 @@ var promptAuditCommand = new Command26("prompt-audit").description("Dry-run prom
|
|
|
6722
6774
|
const sections = [];
|
|
6723
6775
|
const notesContent = task.prdContent ? `
|
|
6724
6776
|
|
|
6725
|
-
## PRD (Product Requirements Document)
|
|
6777
|
+
## Approved PRD (Product Requirements Document)
|
|
6778
|
+
|
|
6779
|
+
A PRD for this task has been generated and approved by the user. Your job now is to IMPLEMENT the PRD below in code. The planning/proposal phase is already complete \u2014 do not produce another plan or proposal.
|
|
6780
|
+
|
|
6781
|
+
Even if the task title includes words like "proposal", "plan", "design", "spec", or "PRD", treat the PRD below as the finalized spec and deliver working code that fulfills it. Only signal "no MR/PR needed" if the PRD itself explicitly calls out that no code changes are required.
|
|
6726
6782
|
|
|
6727
6783
|
${task.prdContent}` : task.notes ? `
|
|
6728
6784
|
|
|
@@ -6756,7 +6812,7 @@ ${task.notes}` : "";
|
|
|
6756
6812
|
const repoDir = Object.entries(config.directories).find(([, pid]) => pid === task.projectId)?.[0];
|
|
6757
6813
|
if (repoDir) {
|
|
6758
6814
|
const featuresPath = resolve8(repoDir, ".mr-features.md");
|
|
6759
|
-
if (
|
|
6815
|
+
if (existsSync17(featuresPath)) {
|
|
6760
6816
|
const featuresContent = readFileSync12(featuresPath, "utf-8");
|
|
6761
6817
|
sections.push({ name: "features-doc", tokens: estimateTokens(featuresContent) });
|
|
6762
6818
|
}
|
|
@@ -6911,7 +6967,7 @@ ${r.jobType} [${r.identifier}]`);
|
|
|
6911
6967
|
});
|
|
6912
6968
|
|
|
6913
6969
|
// cli/commands/skill.ts
|
|
6914
|
-
import { Command as
|
|
6970
|
+
import { Command as Command28 } from "commander";
|
|
6915
6971
|
var c10 = {
|
|
6916
6972
|
reset: "\x1B[0m",
|
|
6917
6973
|
bold: "\x1B[1m",
|
|
@@ -6920,7 +6976,7 @@ var c10 = {
|
|
|
6920
6976
|
green: "\x1B[32m",
|
|
6921
6977
|
yellow: "\x1B[33m"
|
|
6922
6978
|
};
|
|
6923
|
-
var skillCommand = new
|
|
6979
|
+
var skillCommand = new Command28("skill").description("Manage skills \u2014 reusable playbooks for AI agents");
|
|
6924
6980
|
skillCommand.command("list").alias("ls").description("List all skills").option("--category <category>", "Filter by category").action(async (opts) => {
|
|
6925
6981
|
const params = new URLSearchParams();
|
|
6926
6982
|
if (opts.category) params.set("category", opts.category);
|
|
@@ -7011,7 +7067,7 @@ skillCommand.command("generate").alias("gen").description("Generate a new skill
|
|
|
7011
7067
|
});
|
|
7012
7068
|
|
|
7013
7069
|
// cli/commands/resource.ts
|
|
7014
|
-
import { Command as
|
|
7070
|
+
import { Command as Command29 } from "commander";
|
|
7015
7071
|
var c11 = {
|
|
7016
7072
|
reset: "\x1B[0m",
|
|
7017
7073
|
bold: "\x1B[1m",
|
|
@@ -7031,7 +7087,7 @@ function typeLabel(type) {
|
|
|
7031
7087
|
const color = TYPE_COLORS[type] ?? c11.dim;
|
|
7032
7088
|
return `${color}${type}${c11.reset}`;
|
|
7033
7089
|
}
|
|
7034
|
-
var resourceCommand = new
|
|
7090
|
+
var resourceCommand = new Command29("resource").description("Manage resources \u2014 documents, plans, research, and notes");
|
|
7035
7091
|
resourceCommand.command("list").alias("ls").description("List resources for the linked project (or all)").option("--all", "List all resources across projects").action(async (opts) => {
|
|
7036
7092
|
const params = new URLSearchParams();
|
|
7037
7093
|
if (opts.all) {
|
|
@@ -7129,13 +7185,13 @@ resourceCommand.command("generate").alias("gen").description("Generate a resourc
|
|
|
7129
7185
|
});
|
|
7130
7186
|
|
|
7131
7187
|
// cli/commands/tests.ts
|
|
7132
|
-
import { Command as
|
|
7188
|
+
import { Command as Command30 } from "commander";
|
|
7133
7189
|
var c12 = {
|
|
7134
7190
|
reset: "\x1B[0m",
|
|
7135
7191
|
dim: "\x1B[2m",
|
|
7136
7192
|
yellow: "\x1B[33m"
|
|
7137
7193
|
};
|
|
7138
|
-
var testsCommand = new
|
|
7194
|
+
var testsCommand = new Command30("tests").description("List MR Test scenarios for the linked project").action(async () => {
|
|
7139
7195
|
const projectId = getLinkedProjectId();
|
|
7140
7196
|
if (!projectId) {
|
|
7141
7197
|
console.error(
|
|
@@ -7168,7 +7224,7 @@ var testsCommand = new Command29("tests").description("List MR Test scenarios fo
|
|
|
7168
7224
|
|
|
7169
7225
|
// cli/index.ts
|
|
7170
7226
|
var configPath = join12(homedir3(), ".mr-manager", "config.json");
|
|
7171
|
-
var isFirstRun = !
|
|
7227
|
+
var isFirstRun = !existsSync18(configPath);
|
|
7172
7228
|
var userArgs = process.argv.slice(2);
|
|
7173
7229
|
var bypassCommands = /* @__PURE__ */ new Set(["login", "init", "auth", "help", "--help", "-h", "--version", "-V", "doctor", "setup"]);
|
|
7174
7230
|
var shouldBypass = userArgs.length > 0 && bypassCommands.has(userArgs[0]);
|
|
@@ -7204,13 +7260,14 @@ if (isFirstRun && !shouldBypass) {
|
|
|
7204
7260
|
console.log("");
|
|
7205
7261
|
process.exit(0);
|
|
7206
7262
|
}
|
|
7207
|
-
var program = new
|
|
7263
|
+
var program = new Command31();
|
|
7208
7264
|
program.name("mr").description("Mr. Manager - Task and project management CLI").version(CLI_VERSION);
|
|
7209
7265
|
program.addCommand(initCommand);
|
|
7210
7266
|
program.addCommand(authCommand);
|
|
7211
7267
|
program.addCommand(loginCommand);
|
|
7212
7268
|
program.addCommand(projectsCommand);
|
|
7213
7269
|
program.addCommand(tasksCommand);
|
|
7270
|
+
program.addCommand(taskGroupCommand);
|
|
7214
7271
|
program.addCommand(linkCommand);
|
|
7215
7272
|
program.addCommand(unlinkCommand);
|
|
7216
7273
|
program.addCommand(contextCommand);
|