@jonit-dev/night-watch-cli 1.8.4-beta.0 → 1.8.4-beta.2

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/cli.js CHANGED
@@ -3792,7 +3792,7 @@ function getLockFilePaths(projectDir) {
3792
3792
  };
3793
3793
  }
3794
3794
  function sleep(ms) {
3795
- return new Promise((resolve10) => setTimeout(resolve10, ms));
3795
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
3796
3796
  }
3797
3797
  async function cancelProcess(processType, lockPath, force = false) {
3798
3798
  const lockStatus = checkLockFile(lockPath);
@@ -5390,6 +5390,7 @@ var init_roadmap_state = __esm({
5390
5390
  // ../core/dist/templates/slicer-prompt.js
5391
5391
  import * as fs15 from "fs";
5392
5392
  import * as path14 from "path";
5393
+ import { fileURLToPath as fileURLToPath2 } from "url";
5393
5394
  function loadSlicerTemplate(templateDir) {
5394
5395
  if (cachedTemplate) {
5395
5396
  return cachedTemplate;
@@ -5425,10 +5426,12 @@ function createSlicerPromptVars(title, section, description, prdDir, prdFilename
5425
5426
  prdDir
5426
5427
  };
5427
5428
  }
5428
- var DEFAULT_SLICER_TEMPLATE, cachedTemplate;
5429
+ var __filename, __dirname, DEFAULT_SLICER_TEMPLATE, cachedTemplate;
5429
5430
  var init_slicer_prompt = __esm({
5430
5431
  "../core/dist/templates/slicer-prompt.js"() {
5431
5432
  "use strict";
5433
+ __filename = fileURLToPath2(import.meta.url);
5434
+ __dirname = path14.dirname(__filename);
5432
5435
  DEFAULT_SLICER_TEMPLATE = `You are a **PRD Creator Agent**. Your job: analyze the codebase and write a complete Product Requirements Document (PRD) for a feature.
5433
5436
 
5434
5437
  When this activates: \`PRD Creator: Initializing\`
@@ -5702,11 +5705,11 @@ function scanExistingPrdSlugs(prdDir) {
5702
5705
  }
5703
5706
  return slugs;
5704
5707
  }
5705
- function buildProviderArgs(provider, prompt2, workingDir) {
5708
+ function buildProviderArgs(provider, prompt, workingDir) {
5706
5709
  if (provider === "codex") {
5707
- return ["exec", "-C", workingDir, "--yolo", prompt2];
5710
+ return ["exec", "-C", workingDir, "--yolo", prompt];
5708
5711
  }
5709
- return ["-p", prompt2, "--dangerously-skip-permissions"];
5712
+ return ["-p", prompt, "--dangerously-skip-permissions"];
5710
5713
  }
5711
5714
  async function sliceRoadmapItem(projectDir, prdDir, item, config) {
5712
5715
  const itemSlug = slugify(item.title);
@@ -5726,9 +5729,9 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
5726
5729
  fs16.mkdirSync(prdDir, { recursive: true });
5727
5730
  }
5728
5731
  const promptVars = createSlicerPromptVars(item.title, item.section, item.description, prdDir, filename);
5729
- const prompt2 = renderSlicerPrompt(promptVars);
5732
+ const prompt = renderSlicerPrompt(promptVars);
5730
5733
  const provider = resolveJobProvider(config, "slicer");
5731
- const providerArgs = buildProviderArgs(provider, prompt2, projectDir);
5734
+ const providerArgs = buildProviderArgs(provider, prompt, projectDir);
5732
5735
  const logDir = path15.join(projectDir, "logs");
5733
5736
  if (!fs16.existsSync(logDir)) {
5734
5737
  fs16.mkdirSync(logDir, { recursive: true });
@@ -5737,7 +5740,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
5737
5740
  const logStream = fs16.createWriteStream(logFile, { flags: "w" });
5738
5741
  logStream.on("error", () => {
5739
5742
  });
5740
- return new Promise((resolve10) => {
5743
+ return new Promise((resolve9) => {
5741
5744
  const childEnv = {
5742
5745
  ...process.env,
5743
5746
  ...config.providerEnv
@@ -5755,7 +5758,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
5755
5758
  });
5756
5759
  child.on("error", (error2) => {
5757
5760
  logStream.end();
5758
- resolve10({
5761
+ resolve9({
5759
5762
  sliced: false,
5760
5763
  error: `Failed to spawn provider: ${error2.message}`,
5761
5764
  item
@@ -5764,7 +5767,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
5764
5767
  child.on("close", (code) => {
5765
5768
  logStream.end();
5766
5769
  if (code !== 0) {
5767
- resolve10({
5770
+ resolve9({
5768
5771
  sliced: false,
5769
5772
  error: `Provider exited with code ${code ?? 1}`,
5770
5773
  item
@@ -5772,14 +5775,14 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
5772
5775
  return;
5773
5776
  }
5774
5777
  if (!fs16.existsSync(filePath)) {
5775
- resolve10({
5778
+ resolve9({
5776
5779
  sliced: false,
5777
5780
  error: `Provider did not create expected file: ${filePath}`,
5778
5781
  item
5779
5782
  });
5780
5783
  return;
5781
5784
  }
5782
- resolve10({
5785
+ resolve9({
5783
5786
  sliced: true,
5784
5787
  file: filename,
5785
5788
  item
@@ -5940,7 +5943,7 @@ async function executeScript(scriptPath, args = [], env = {}, options = {}) {
5940
5943
  return result.exitCode;
5941
5944
  }
5942
5945
  async function executeScriptWithOutput(scriptPath, args = [], env = {}, options = {}) {
5943
- return new Promise((resolve10, reject) => {
5946
+ return new Promise((resolve9, reject) => {
5944
5947
  const childEnv = {
5945
5948
  ...process.env,
5946
5949
  ...env
@@ -5966,7 +5969,7 @@ async function executeScriptWithOutput(scriptPath, args = [], env = {}, options
5966
5969
  reject(error2);
5967
5970
  });
5968
5971
  child.on("close", (code) => {
5969
- resolve10({
5972
+ resolve9({
5970
5973
  exitCode: code ?? 1,
5971
5974
  stdout: stdoutChunks.join(""),
5972
5975
  stderr: stderrChunks.join("")
@@ -6361,6 +6364,7 @@ function getLockPathForJob(projectPath, jobType) {
6361
6364
  case "audit":
6362
6365
  return auditLockPath(projectPath);
6363
6366
  case "slicer":
6367
+ case "planner":
6364
6368
  return plannerLockPath(projectPath);
6365
6369
  case "analytics":
6366
6370
  return analyticsLockPath(projectPath);
@@ -6917,13 +6921,13 @@ async function runAnalytics(config, projectDir) {
6917
6921
  logger3.info("Fetching Amplitude data", { lookbackDays: config.analytics.lookbackDays });
6918
6922
  const data = await fetchAmplitudeData(apiKey, secretKey, config.analytics.lookbackDays);
6919
6923
  const systemPrompt = config.analytics.analysisPrompt?.trim() || DEFAULT_ANALYTICS_PROMPT;
6920
- const prompt2 = `${systemPrompt}
6924
+ const prompt = `${systemPrompt}
6921
6925
 
6922
6926
  --- AMPLITUDE DATA ---
6923
6927
  ${JSON.stringify(data, null, 2)}`;
6924
6928
  const tmpDir = fs19.mkdtempSync(path19.join(os8.tmpdir(), "nw-analytics-"));
6925
6929
  const promptFile = path19.join(tmpDir, "analytics-prompt.md");
6926
- fs19.writeFileSync(promptFile, prompt2, "utf-8");
6930
+ fs19.writeFileSync(promptFile, prompt, "utf-8");
6927
6931
  try {
6928
6932
  const provider = resolveJobProvider(config, "analytics");
6929
6933
  const providerCmd = PROVIDER_COMMANDS[provider];
@@ -7553,19 +7557,19 @@ var init_dist = __esm({
7553
7557
  import "reflect-metadata";
7554
7558
  import { Command as Command3 } from "commander";
7555
7559
  import { existsSync as existsSync30, readFileSync as readFileSync19 } from "fs";
7556
- import { fileURLToPath as fileURLToPath4 } from "url";
7557
- import { dirname as dirname9, join as join36 } from "path";
7560
+ import { fileURLToPath as fileURLToPath6 } from "url";
7561
+ import { dirname as dirname12, join as join36 } from "path";
7558
7562
 
7559
7563
  // src/commands/init.ts
7560
7564
  init_dist();
7561
7565
  import fs20 from "fs";
7562
7566
  import path20 from "path";
7563
7567
  import { execSync as execSync3 } from "child_process";
7564
- import { fileURLToPath as fileURLToPath2 } from "url";
7565
- import { dirname as dirname5, join as join18 } from "path";
7568
+ import { fileURLToPath as fileURLToPath3 } from "url";
7569
+ import { dirname as dirname6, join as join18 } from "path";
7566
7570
  import * as readline from "readline";
7567
- var __filename = fileURLToPath2(import.meta.url);
7568
- var __dirname2 = dirname5(__filename);
7571
+ var __filename2 = fileURLToPath3(import.meta.url);
7572
+ var __dirname2 = dirname6(__filename2);
7569
7573
  function findTemplatesDir(startDir) {
7570
7574
  let d = startDir;
7571
7575
  for (let i = 0; i < 8; i++) {
@@ -7573,7 +7577,7 @@ function findTemplatesDir(startDir) {
7573
7577
  if (fs20.existsSync(candidate) && fs20.statSync(candidate).isDirectory()) {
7574
7578
  return candidate;
7575
7579
  }
7576
- d = dirname5(d);
7580
+ d = dirname6(d);
7577
7581
  }
7578
7582
  return join18(startDir, "templates");
7579
7583
  }
@@ -7632,7 +7636,7 @@ function promptYesNo(question, defaultNo = true) {
7632
7636
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
7633
7637
  return Promise.resolve(false);
7634
7638
  }
7635
- return new Promise((resolve10) => {
7639
+ return new Promise((resolve9) => {
7636
7640
  const rl = readline.createInterface({
7637
7641
  input: process.stdin,
7638
7642
  output: process.stdout
@@ -7642,10 +7646,10 @@ function promptYesNo(question, defaultNo = true) {
7642
7646
  rl.close();
7643
7647
  const normalized = answer.trim().toLowerCase();
7644
7648
  if (normalized === "") {
7645
- resolve10(!defaultNo);
7649
+ resolve9(!defaultNo);
7646
7650
  return;
7647
7651
  }
7648
- resolve10(normalized === "y" || normalized === "yes");
7652
+ resolve9(normalized === "y" || normalized === "yes");
7649
7653
  });
7650
7654
  });
7651
7655
  }
@@ -7748,7 +7752,7 @@ function getDefaultBranch(cwd) {
7748
7752
  }
7749
7753
  }
7750
7754
  function promptProviderSelection(providers) {
7751
- return new Promise((resolve10, reject) => {
7755
+ return new Promise((resolve9, reject) => {
7752
7756
  const rl = readline.createInterface({
7753
7757
  input: process.stdin,
7754
7758
  output: process.stdout
@@ -7764,7 +7768,7 @@ function promptProviderSelection(providers) {
7764
7768
  reject(new Error("Invalid selection. Please run init again and select a valid number."));
7765
7769
  return;
7766
7770
  }
7767
- resolve10(providers[selection - 1]);
7771
+ resolve9(providers[selection - 1]);
7768
7772
  });
7769
7773
  });
7770
7774
  }
@@ -8126,6 +8130,20 @@ function initCommand(program2) {
8126
8130
  auditResolution.source
8127
8131
  );
8128
8132
  templateSources.push({ name: "audit.md", source: auditResult.source });
8133
+ const plannerResolution = resolveTemplatePath(
8134
+ "prd-creator.md",
8135
+ customTemplatesDir,
8136
+ TEMPLATES_DIR
8137
+ );
8138
+ const plannerResult = processTemplate(
8139
+ "prd-creator.md",
8140
+ path20.join(instructionsDir, "prd-creator.md"),
8141
+ replacements,
8142
+ force,
8143
+ plannerResolution.path,
8144
+ plannerResolution.source
8145
+ );
8146
+ templateSources.push({ name: "prd-creator.md", source: plannerResult.source });
8129
8147
  step(9, totalSteps, "Creating configuration file...");
8130
8148
  const configPath = path20.join(cwd, CONFIG_FILE_NAME);
8131
8149
  if (fs20.existsSync(configPath) && !force) {
@@ -8214,6 +8232,7 @@ function initCommand(program2) {
8214
8232
  filesTable.push(["", `instructions/pr-reviewer.md (${templateSources[2].source})`]);
8215
8233
  filesTable.push(["", `instructions/qa.md (${templateSources[3].source})`]);
8216
8234
  filesTable.push(["", `instructions/audit.md (${templateSources[4].source})`]);
8235
+ filesTable.push(["", `instructions/prd-creator.md (${templateSources[5].source})`]);
8217
8236
  filesTable.push(["Config File", CONFIG_FILE_NAME]);
8218
8237
  filesTable.push(["Board Setup", boardSetupStatus]);
8219
8238
  filesTable.push(["Global Registry", "~/.night-watch/projects.json"]);
@@ -8295,7 +8314,7 @@ async function maybeApplyCronSchedulingDelay(config, jobType, projectDir) {
8295
8314
  return plan;
8296
8315
  }
8297
8316
  if (plan.totalDelayMinutes > 0) {
8298
- await new Promise((resolve10) => setTimeout(resolve10, plan.totalDelayMinutes * 6e4));
8317
+ await new Promise((resolve9) => setTimeout(resolve9, plan.totalDelayMinutes * 6e4));
8299
8318
  }
8300
8319
  return getSchedulingPlan(projectDir, config, jobType);
8301
8320
  }
@@ -10038,9 +10057,25 @@ function logsCommand(program2) {
10038
10057
 
10039
10058
  // src/commands/prd.ts
10040
10059
  init_dist();
10060
+ import { execSync as execSync5, spawn as spawn4, spawnSync } from "child_process";
10041
10061
  import * as fs26 from "fs";
10042
10062
  import * as path28 from "path";
10043
- import * as readline2 from "readline";
10063
+ import { fileURLToPath as fileURLToPath4 } from "url";
10064
+ import { dirname as dirname9 } from "path";
10065
+ var __filename3 = fileURLToPath4(import.meta.url);
10066
+ var __dirname3 = dirname9(__filename3);
10067
+ function findTemplatesDir2(startDir) {
10068
+ let current = startDir;
10069
+ for (let i = 0; i < 8; i++) {
10070
+ const candidate = path28.join(current, "templates");
10071
+ if (fs26.existsSync(candidate) && fs26.statSync(candidate).isDirectory()) {
10072
+ return candidate;
10073
+ }
10074
+ current = path28.dirname(current);
10075
+ }
10076
+ return path28.join(startDir, "templates");
10077
+ }
10078
+ var TEMPLATES_DIR2 = findTemplatesDir2(__dirname3);
10044
10079
  function slugify2(name) {
10045
10080
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
10046
10081
  }
@@ -10053,13 +10088,186 @@ function getNextPrdNumber2(prdDir) {
10053
10088
  });
10054
10089
  return Math.max(0, ...numbers) + 1;
10055
10090
  }
10056
- function prompt(rl, question) {
10057
- return new Promise((resolve10) => {
10058
- rl.question(question, (answer) => {
10059
- resolve10(answer.trim());
10091
+ function extractPrdMarkdown(response) {
10092
+ const match = response.match(/(^#\s+[\s\S]*)/m);
10093
+ return match ? match[1].trim() : response.trim();
10094
+ }
10095
+ function extractPrdTitle(markdown) {
10096
+ const match = markdown.match(/^#\s+PRD:\s*(.+)/m);
10097
+ return match ? match[1].trim() : null;
10098
+ }
10099
+ function buildPrdPrompt(description, projectDir, planningPrinciples) {
10100
+ return `You are generating a PRD markdown file for Night Watch.
10101
+
10102
+ Return only the final PRD markdown.
10103
+
10104
+ Hard requirements:
10105
+ - Start with: # PRD: <title>
10106
+ - Do not ask follow-up questions
10107
+ - Do not add any preamble, commentary, or code fences
10108
+ - Do not describe what you are going to do
10109
+ - Do not mention these instructions
10110
+ - Treat the planning guide below as mandatory instructions, not background context
10111
+
10112
+ Project directory: ${projectDir}
10113
+
10114
+ Planning guide:
10115
+ ${planningPrinciples}
10116
+
10117
+ User request:
10118
+ ${description}
10119
+
10120
+ Now write the complete PRD markdown file.`;
10121
+ }
10122
+ function buildNativeClaudeEnv(baseEnv = process.env) {
10123
+ const env = { ...baseEnv };
10124
+ delete env.ANTHROPIC_BASE_URL;
10125
+ delete env.ANTHROPIC_API_KEY;
10126
+ delete env.ANTHROPIC_AUTH_TOKEN;
10127
+ delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;
10128
+ delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;
10129
+ delete env.API_TIMEOUT_MS;
10130
+ delete env.CLAUDE_CODE_SSE_PORT;
10131
+ delete env.CLAUDE_NIGHTS_WATCH_DIR;
10132
+ delete env.NW_CLAUDE_MODEL_ID;
10133
+ delete env.NW_CLAUDE_PRIMARY_MODEL_ID;
10134
+ delete env.NW_CLAUDE_SECONDARY_MODEL_ID;
10135
+ delete env.NW_PROVIDER_CMD;
10136
+ delete env.NW_PROVIDER_SUBCOMMAND;
10137
+ delete env.NW_PROVIDER_PROMPT_FLAG;
10138
+ delete env.NW_PROVIDER_APPROVE_FLAG;
10139
+ delete env.NW_PROVIDER_WORKDIR_FLAG;
10140
+ delete env.NW_PROVIDER_MODEL_FLAG;
10141
+ delete env.NW_PROVIDER_MODEL;
10142
+ delete env.NW_PROVIDER_LABEL;
10143
+ return env;
10144
+ }
10145
+ function resolvePrdCreateDir() {
10146
+ return "docs/PRDs";
10147
+ }
10148
+ function resolveGitHubBlobUrl(projectDir, relPath) {
10149
+ try {
10150
+ const remoteUrl = execSync5("git remote get-url origin", {
10151
+ cwd: projectDir,
10152
+ encoding: "utf-8",
10153
+ stdio: ["ignore", "pipe", "pipe"]
10154
+ }).trim();
10155
+ const branch = execSync5("git rev-parse --abbrev-ref HEAD", {
10156
+ cwd: projectDir,
10157
+ encoding: "utf-8",
10158
+ stdio: ["ignore", "pipe", "pipe"]
10159
+ }).trim();
10160
+ const httpsBase = remoteUrl.replace(/^git@github\.com:/, "https://github.com/").replace(/^ssh:\/\/git@github\.com\//, "https://github.com/").replace(/\.git$/, "");
10161
+ if (!httpsBase.startsWith("https://github.com/")) {
10162
+ return null;
10163
+ }
10164
+ const ref = branch && branch !== "HEAD" ? branch : "main";
10165
+ return `${httpsBase}/blob/${encodeURIComponent(ref).replace(/%2F/g, "/")}/${relPath.split(path28.sep).map((segment) => encodeURIComponent(segment)).join("/")}`;
10166
+ } catch {
10167
+ return null;
10168
+ }
10169
+ }
10170
+ function buildGithubIssueBody(prdPath, projectDir, prdContent) {
10171
+ const relPath = path28.relative(projectDir, prdPath);
10172
+ const blobUrl = resolveGitHubBlobUrl(projectDir, relPath);
10173
+ const fileLine = blobUrl ? `PRD file: [\`${relPath}\`](${blobUrl})` : `PRD file: \`${relPath}\``;
10174
+ return `${fileLine}
10175
+
10176
+ ${prdContent}
10177
+
10178
+ ---
10179
+ Created via \`night-watch prd create\`.`;
10180
+ }
10181
+ async function generatePrdWithClaude(description, projectDir, model) {
10182
+ const bundledTemplatePath = path28.join(TEMPLATES_DIR2, "prd-creator.md");
10183
+ const installedTemplatePath = path28.join(projectDir, "instructions", "prd-creator.md");
10184
+ const templatePath = fs26.existsSync(installedTemplatePath) ? installedTemplatePath : bundledTemplatePath;
10185
+ if (!fs26.existsSync(templatePath)) {
10186
+ return null;
10187
+ }
10188
+ const planningPrinciples = fs26.readFileSync(templatePath, "utf-8");
10189
+ const prompt = buildPrdPrompt(description, projectDir, planningPrinciples);
10190
+ const modelId = model ?? CLAUDE_MODEL_IDS.opus;
10191
+ const env = buildNativeClaudeEnv(process.env);
10192
+ return await new Promise((resolve9) => {
10193
+ const child = spawn4(
10194
+ "claude",
10195
+ [
10196
+ "-p",
10197
+ "--verbose",
10198
+ "--output-format",
10199
+ "stream-json",
10200
+ "--include-partial-messages",
10201
+ "--model",
10202
+ modelId,
10203
+ prompt
10204
+ ],
10205
+ { env, stdio: ["ignore", "pipe", "pipe"] }
10206
+ );
10207
+ let stdoutBuffer = "";
10208
+ let finalResult = "";
10209
+ child.stdout.on("data", (chunk) => {
10210
+ stdoutBuffer += chunk.toString("utf-8");
10211
+ while (stdoutBuffer.includes("\n")) {
10212
+ const newlineIndex = stdoutBuffer.indexOf("\n");
10213
+ const line = stdoutBuffer.slice(0, newlineIndex).trim();
10214
+ stdoutBuffer = stdoutBuffer.slice(newlineIndex + 1);
10215
+ if (!line) continue;
10216
+ try {
10217
+ const payload = JSON.parse(line);
10218
+ if (payload.type === "stream_event") {
10219
+ const event = payload.event;
10220
+ const delta = event?.delta;
10221
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
10222
+ process.stdout.write(delta.text);
10223
+ }
10224
+ continue;
10225
+ }
10226
+ if (payload.type === "result" && typeof payload.result === "string") {
10227
+ finalResult = payload.result;
10228
+ }
10229
+ } catch {
10230
+ }
10231
+ }
10232
+ });
10233
+ child.stderr.on("data", (chunk) => process.stderr.write(chunk));
10234
+ child.on("close", (code) => {
10235
+ if (stdoutBuffer.trim().length > 0) {
10236
+ try {
10237
+ const payload = JSON.parse(stdoutBuffer.trim());
10238
+ if (payload.type === "result" && typeof payload.result === "string") {
10239
+ finalResult = payload.result;
10240
+ }
10241
+ } catch {
10242
+ }
10243
+ }
10244
+ process.stdout.write("\n");
10245
+ resolve9(code === 0 && finalResult ? extractPrdMarkdown(finalResult) : null);
10060
10246
  });
10247
+ child.on("error", () => resolve9(null));
10061
10248
  });
10062
10249
  }
10250
+ function runGh(args, cwd) {
10251
+ const result = spawnSync("gh", args, { cwd, encoding: "utf-8" });
10252
+ if (result.status === 0) return (result.stdout ?? "").trim();
10253
+ return null;
10254
+ }
10255
+ function createGithubIssue(title, prdPath, projectDir, prdContent) {
10256
+ const tmpFile = path28.join(projectDir, `.prd-issue-body-${Date.now()}.tmp`);
10257
+ try {
10258
+ const body = buildGithubIssueBody(prdPath, projectDir, prdContent);
10259
+ fs26.writeFileSync(tmpFile, body, "utf-8");
10260
+ const baseArgs = ["issue", "create", "--title", `PRD: ${title}`, "--body-file", tmpFile];
10261
+ return runGh([...baseArgs, "--label", "prd"], projectDir) ?? runGh(baseArgs, projectDir);
10262
+ } catch {
10263
+ return null;
10264
+ } finally {
10265
+ try {
10266
+ fs26.unlinkSync(tmpFile);
10267
+ } catch {
10268
+ }
10269
+ }
10270
+ }
10063
10271
  function parseDependencies(content) {
10064
10272
  const match = content.match(/\*\*Depends on:\*\*\s*(.+)/i) || content.match(/Depends on:\s*(.+)/i);
10065
10273
  if (!match) return [];
@@ -10083,101 +10291,38 @@ function isClaimActive(claimPath, maxRuntime) {
10083
10291
  }
10084
10292
  function prdCommand(program2) {
10085
10293
  const prd = program2.command("prd").description("Manage PRD files");
10086
- prd.command("create").description("Generate a new PRD markdown file from template").argument("<name>", "PRD name (used for title and filename)").option("-i, --interactive", "Prompt for complexity, dependencies, and phase count", false).option("-t, --template <path>", "Path to a custom template file").option("--deps <files>", "Comma-separated dependency filenames").option("--phases <count>", "Number of execution phases", "3").option("--no-number", "Skip auto-numbering prefix").action(async (name, options) => {
10294
+ prd.command("create").description("Generate a new PRD markdown file using Claude").argument("<name>", "PRD description").option("--number", "Add auto-numbering prefix to the filename", false).option("--model <model>", "Claude model to use (e.g. sonnet, opus, or a full model ID)").action(async (name, options) => {
10087
10295
  const projectDir = process.cwd();
10088
- const config = loadConfig(projectDir);
10089
- const prdDir = path28.join(projectDir, config.prdDir);
10296
+ const prdDir = path28.join(projectDir, resolvePrdCreateDir());
10090
10297
  if (!fs26.existsSync(prdDir)) {
10091
10298
  fs26.mkdirSync(prdDir, { recursive: true });
10092
10299
  }
10093
- let complexityScore = 5;
10094
- let dependsOn = [];
10095
- let phaseCount = parseInt(options.phases ?? "3", 10);
10096
- if (isNaN(phaseCount) || phaseCount < 1) {
10097
- phaseCount = 3;
10098
- }
10099
- if (options.deps) {
10100
- dependsOn = options.deps.split(",").map((d) => d.trim()).filter((d) => d.length > 0);
10101
- }
10102
- if (options.interactive) {
10103
- const rl = readline2.createInterface({
10104
- input: process.stdin,
10105
- output: process.stdout
10106
- });
10107
- try {
10108
- const complexityInput = await prompt(rl, "Complexity score (1-10, default 5): ");
10109
- if (complexityInput) {
10110
- const parsed = parseInt(complexityInput, 10);
10111
- if (!isNaN(parsed) && parsed >= 1 && parsed <= 10) {
10112
- complexityScore = parsed;
10113
- }
10114
- }
10115
- const depsInput = await prompt(
10116
- rl,
10117
- "Dependencies (comma-separated filenames, or empty): "
10118
- );
10119
- if (depsInput) {
10120
- dependsOn = depsInput.split(",").map((d) => d.trim()).filter((d) => d.length > 0);
10121
- }
10122
- const phasesInput = await prompt(rl, `Number of phases (default ${phaseCount}): `);
10123
- if (phasesInput) {
10124
- const parsed = parseInt(phasesInput, 10);
10125
- if (!isNaN(parsed) && parsed >= 1) {
10126
- phaseCount = parsed;
10127
- }
10128
- }
10129
- } finally {
10130
- rl.close();
10131
- }
10132
- }
10133
- let complexityLevel;
10134
- if (complexityScore <= 3) {
10135
- complexityLevel = "LOW";
10136
- } else if (complexityScore <= 7) {
10137
- complexityLevel = "MEDIUM";
10138
- } else {
10139
- complexityLevel = "HIGH";
10140
- }
10141
- const slug = slugify2(name);
10142
- let filename;
10143
- if (options.number) {
10144
- const nextNum = getNextPrdNumber2(prdDir);
10145
- const padded = String(nextNum).padStart(2, "0");
10146
- filename = `${padded}-${slug}.md`;
10147
- } else {
10148
- filename = `${slug}.md`;
10300
+ const resolvedModel = options.model ? CLAUDE_MODEL_IDS[options.model] ?? options.model : void 0;
10301
+ const modelLabel = resolvedModel ?? CLAUDE_MODEL_IDS.opus;
10302
+ dim(`Calling Claude (${modelLabel}) to generate the PRD. It can take several minutes, please hang on!
10303
+ `);
10304
+ const generated = await generatePrdWithClaude(name, projectDir, resolvedModel);
10305
+ if (!generated) {
10306
+ error("Claude generation failed. Is the provider configured and available?");
10307
+ process.exit(1);
10149
10308
  }
10309
+ const prdTitle = extractPrdTitle(generated) ?? name;
10310
+ const slug = slugify2(prdTitle);
10311
+ const filename = options.number ? `${String(getNextPrdNumber2(prdDir)).padStart(2, "0")}-${slug}.md` : `${slug}.md`;
10150
10312
  const filePath = path28.join(prdDir, filename);
10151
10313
  if (fs26.existsSync(filePath)) {
10152
10314
  error(`File already exists: ${filePath}`);
10153
10315
  dim("Use a different name or remove the existing file.");
10154
10316
  process.exit(1);
10155
10317
  }
10156
- let customTemplate;
10157
- if (options.template) {
10158
- const templatePath = path28.resolve(options.template);
10159
- if (!fs26.existsSync(templatePath)) {
10160
- error(`Template file not found: ${templatePath}`);
10161
- process.exit(1);
10162
- }
10163
- customTemplate = fs26.readFileSync(templatePath, "utf-8");
10164
- }
10165
- const vars = {
10166
- title: name,
10167
- dependsOn,
10168
- complexityScore,
10169
- complexityLevel,
10170
- complexityBreakdown: [],
10171
- phaseCount
10172
- };
10173
- const content = renderPrdTemplate(vars, customTemplate);
10174
- fs26.writeFileSync(filePath, content, "utf-8");
10318
+ fs26.writeFileSync(filePath, generated, "utf-8");
10175
10319
  header("PRD Created");
10176
10320
  success(`Created: ${filePath}`);
10177
- info(`Title: ${name}`);
10178
- dim(`Phases: ${phaseCount}`);
10179
- if (dependsOn.length > 0) {
10180
- dim(`Dependencies: ${dependsOn.join(", ")}`);
10321
+ const issueUrl = createGithubIssue(prdTitle, filePath, projectDir, generated);
10322
+ if (issueUrl) {
10323
+ info(`Issue: ${issueUrl}`);
10324
+ } else {
10325
+ dim("GitHub issue creation skipped (gh not available or not in a GitHub repo).");
10181
10326
  }
10182
10327
  });
10183
10328
  prd.command("list").description("List all PRDs with status").option("--json", "Output as JSON").action(async (options) => {
@@ -10219,16 +10364,14 @@ function prdCommand(program2) {
10219
10364
  dim(" No PRDs found.");
10220
10365
  return;
10221
10366
  }
10222
- const table = createTable({
10223
- head: ["Name", "Status", "Dependencies"]
10224
- });
10225
- for (const prd2 of pending) {
10226
- const status = prd2.claimed ? "claimed" : "pending";
10227
- const statusDisplay = prd2.claimed && prd2.claimInfo ? `claimed (${prd2.claimInfo.hostname}:${prd2.claimInfo.pid})` : status;
10228
- table.push([prd2.name, statusDisplay, prd2.dependencies.join(", ") || "-"]);
10367
+ const table = createTable({ head: ["Name", "Status", "Dependencies"] });
10368
+ for (const prdEntry of pending) {
10369
+ const status = prdEntry.claimed ? "claimed" : "pending";
10370
+ const statusDisplay = prdEntry.claimed && prdEntry.claimInfo ? `claimed (${prdEntry.claimInfo.hostname}:${prdEntry.claimInfo.pid})` : status;
10371
+ table.push([prdEntry.name, statusDisplay, prdEntry.dependencies.join(", ") || "-"]);
10229
10372
  }
10230
- for (const prd2 of done) {
10231
- table.push([prd2.name, "done", prd2.dependencies.join(", ") || "-"]);
10373
+ for (const prdEntry of done) {
10374
+ table.push([prdEntry.name, "done", prdEntry.dependencies.join(", ") || "-"]);
10232
10375
  }
10233
10376
  console.log(table.toString());
10234
10377
  const claimedCount = pending.filter((p) => p.claimed).length;
@@ -11748,11 +11891,11 @@ function createSchedulesTab() {
11748
11891
 
11749
11892
  // src/commands/dashboard/tab-actions.ts
11750
11893
  import blessed4 from "blessed";
11751
- import { spawn as spawn4 } from "child_process";
11894
+ import { spawn as spawn5 } from "child_process";
11752
11895
  function spawnAction(args, ctx, outputBox, onDone) {
11753
11896
  outputBox.setContent("{cyan-fg}Starting...{/cyan-fg}\n");
11754
11897
  ctx.screen.render();
11755
- const child = spawn4("npx", ["tsx", "src/cli.ts", ...args], {
11898
+ const child = spawn5("npx", ["tsx", "src/cli.ts", ...args], {
11756
11899
  cwd: ctx.projectDir,
11757
11900
  stdio: ["ignore", "pipe", "pipe"],
11758
11901
  env: { ...process.env }
@@ -12565,8 +12708,8 @@ import * as fs33 from "fs";
12565
12708
  init_dist();
12566
12709
  import * as fs32 from "fs";
12567
12710
  import * as path35 from "path";
12568
- import { dirname as dirname8 } from "path";
12569
- import { fileURLToPath as fileURLToPath3 } from "url";
12711
+ import { dirname as dirname11 } from "path";
12712
+ import { fileURLToPath as fileURLToPath5 } from "url";
12570
12713
  import cors from "cors";
12571
12714
  import express from "express";
12572
12715
 
@@ -12707,7 +12850,7 @@ function startSseStatusWatcher(clients, projectDir, getConfig) {
12707
12850
  init_dist();
12708
12851
  import * as fs30 from "fs";
12709
12852
  import * as path31 from "path";
12710
- import { execSync as execSync5, spawn as spawn5 } from "child_process";
12853
+ import { execSync as execSync6, spawn as spawn6 } from "child_process";
12711
12854
  import { Router } from "express";
12712
12855
 
12713
12856
  // ../server/dist/helpers.js
@@ -12794,7 +12937,7 @@ function spawnAction2(projectDir, command, req, res, onSpawned) {
12794
12937
  if (prdName) {
12795
12938
  extraEnv.NW_PRD_PRIORITY = prdName;
12796
12939
  }
12797
- const child = spawn5("night-watch", command, {
12940
+ const child = spawn6("night-watch", command, {
12798
12941
  detached: true,
12799
12942
  stdio: "ignore",
12800
12943
  cwd: projectDir,
@@ -12826,7 +12969,7 @@ function formatCommandError(error2) {
12826
12969
  return output || error2.message;
12827
12970
  }
12828
12971
  function runCliCommand(projectDir, args) {
12829
- execSync5(`night-watch ${args.join(" ")}`, {
12972
+ execSync6(`night-watch ${args.join(" ")}`, {
12830
12973
  cwd: projectDir,
12831
12974
  encoding: "utf-8",
12832
12975
  stdio: "pipe",
@@ -13657,12 +13800,12 @@ function createProjectConfigRoutes() {
13657
13800
  init_dist();
13658
13801
  import * as fs31 from "fs";
13659
13802
  import * as path32 from "path";
13660
- import { execSync as execSync6 } from "child_process";
13803
+ import { execSync as execSync7 } from "child_process";
13661
13804
  import { Router as Router4 } from "express";
13662
13805
  function runDoctorChecks(projectDir, config) {
13663
13806
  const checks = [];
13664
13807
  try {
13665
- execSync6("git rev-parse --is-inside-work-tree", {
13808
+ execSync7("git rev-parse --is-inside-work-tree", {
13666
13809
  cwd: projectDir,
13667
13810
  stdio: "pipe"
13668
13811
  });
@@ -13673,7 +13816,7 @@ function runDoctorChecks(projectDir, config) {
13673
13816
  try {
13674
13817
  const preset = resolvePreset(config, config.provider);
13675
13818
  const command = preset?.command ?? config.provider;
13676
- execSync6(`which ${command}`, { stdio: "pipe" });
13819
+ execSync7(`which ${command}`, { stdio: "pipe" });
13677
13820
  checks.push({
13678
13821
  name: "provider",
13679
13822
  status: "pass",
@@ -14188,13 +14331,13 @@ function createQueueRoutes(deps) {
14188
14331
  }
14189
14332
 
14190
14333
  // ../server/dist/index.js
14191
- var __filename2 = fileURLToPath3(import.meta.url);
14192
- var __dirname3 = dirname8(__filename2);
14334
+ var __filename4 = fileURLToPath5(import.meta.url);
14335
+ var __dirname4 = dirname11(__filename4);
14193
14336
  function resolveWebDistPath() {
14194
- const bundled = path35.join(__dirname3, "web");
14337
+ const bundled = path35.join(__dirname4, "web");
14195
14338
  if (fs32.existsSync(path35.join(bundled, "index.html")))
14196
14339
  return bundled;
14197
- let d = __dirname3;
14340
+ let d = __dirname4;
14198
14341
  for (let i = 0; i < 8; i++) {
14199
14342
  if (fs32.existsSync(path35.join(d, "turbo.json"))) {
14200
14343
  const dev = path35.join(d, "web/dist");
@@ -14202,7 +14345,7 @@ function resolveWebDistPath() {
14202
14345
  return dev;
14203
14346
  break;
14204
14347
  }
14205
- d = dirname8(d);
14348
+ d = dirname11(d);
14206
14349
  }
14207
14350
  return bundled;
14208
14351
  }
@@ -14561,7 +14704,7 @@ function historyCommand(program2) {
14561
14704
 
14562
14705
  // src/commands/update.ts
14563
14706
  init_dist();
14564
- import { spawnSync } from "child_process";
14707
+ import { spawnSync as spawnSync2 } from "child_process";
14565
14708
  import * as fs34 from "fs";
14566
14709
  import * as path36 from "path";
14567
14710
  var DEFAULT_GLOBAL_SPEC = "@jonit-dev/night-watch-cli@latest";
@@ -14576,7 +14719,7 @@ function shouldInstallGlobal(options) {
14576
14719
  return options.global !== false;
14577
14720
  }
14578
14721
  function runCommand2(command, args, cwd) {
14579
- const result = spawnSync(command, args, {
14722
+ const result = spawnSync2(command, args, {
14580
14723
  cwd,
14581
14724
  env: process.env,
14582
14725
  stdio: "inherit"
@@ -14590,7 +14733,7 @@ function runCommand2(command, args, cwd) {
14590
14733
  }
14591
14734
  }
14592
14735
  function resolveNightWatchBin() {
14593
- const result = spawnSync("which", ["night-watch"], {
14736
+ const result = spawnSync2("which", ["night-watch"], {
14594
14737
  encoding: "utf-8",
14595
14738
  env: process.env
14596
14739
  });
@@ -14788,10 +14931,10 @@ function prsCommand(program2) {
14788
14931
  // src/commands/prds.ts
14789
14932
  init_dist();
14790
14933
  import chalk4 from "chalk";
14791
- import { execSync as execSync7 } from "child_process";
14934
+ import { execSync as execSync8 } from "child_process";
14792
14935
  function getOpenPrBranches(projectDir) {
14793
14936
  try {
14794
- execSync7("git rev-parse --git-dir", {
14937
+ execSync8("git rev-parse --git-dir", {
14795
14938
  cwd: projectDir,
14796
14939
  encoding: "utf-8",
14797
14940
  stdio: ["pipe", "pipe", "pipe"]
@@ -14800,12 +14943,12 @@ function getOpenPrBranches(projectDir) {
14800
14943
  return /* @__PURE__ */ new Set();
14801
14944
  }
14802
14945
  try {
14803
- execSync7("which gh", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
14946
+ execSync8("which gh", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
14804
14947
  } catch {
14805
14948
  return /* @__PURE__ */ new Set();
14806
14949
  }
14807
14950
  try {
14808
- const output = execSync7("gh pr list --state open --json headRefName", {
14951
+ const output = execSync8("gh pr list --state open --json headRefName", {
14809
14952
  cwd: projectDir,
14810
14953
  encoding: "utf-8",
14811
14954
  stdio: ["pipe", "pipe", "pipe"]
@@ -14936,7 +15079,7 @@ function prdsCommand(program2) {
14936
15079
  // src/commands/cancel.ts
14937
15080
  init_dist();
14938
15081
  import * as fs36 from "fs";
14939
- import * as readline3 from "readline";
15082
+ import * as readline2 from "readline";
14940
15083
  function getLockFilePaths2(projectDir) {
14941
15084
  const runtimeKey = projectRuntimeKey(projectDir);
14942
15085
  return {
@@ -14944,24 +15087,24 @@ function getLockFilePaths2(projectDir) {
14944
15087
  reviewer: `${LOCK_FILE_PREFIX}pr-reviewer-${runtimeKey}.lock`
14945
15088
  };
14946
15089
  }
14947
- async function promptConfirmation(prompt2) {
15090
+ async function promptConfirmation(prompt) {
14948
15091
  if (!process.stdin.isTTY) {
14949
15092
  return false;
14950
15093
  }
14951
- const rl = readline3.createInterface({
15094
+ const rl = readline2.createInterface({
14952
15095
  input: process.stdin,
14953
15096
  output: process.stdout
14954
15097
  });
14955
- return new Promise((resolve10) => {
14956
- rl.question(`${prompt2} `, (answer) => {
15098
+ return new Promise((resolve9) => {
15099
+ rl.question(`${prompt} `, (answer) => {
14957
15100
  rl.close();
14958
15101
  const normalized = answer.toLowerCase().trim();
14959
- resolve10(normalized === "y" || normalized === "yes");
15102
+ resolve9(normalized === "y" || normalized === "yes");
14960
15103
  });
14961
15104
  });
14962
15105
  }
14963
15106
  function sleep2(ms) {
14964
- return new Promise((resolve10) => setTimeout(resolve10, ms));
15107
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
14965
15108
  }
14966
15109
  function isProcessRunning3(pid) {
14967
15110
  try {
@@ -15443,7 +15586,7 @@ init_dist();
15443
15586
  import { execFileSync as execFileSync6 } from "child_process";
15444
15587
  import * as fs38 from "fs";
15445
15588
  import * as path40 from "path";
15446
- import * as readline4 from "readline";
15589
+ import * as readline3 from "readline";
15447
15590
  import chalk6 from "chalk";
15448
15591
  async function run(fn) {
15449
15592
  try {
@@ -15491,14 +15634,14 @@ async function ensureBoardConfigured(config, cwd, provider, options) {
15491
15634
  }
15492
15635
  }
15493
15636
  async function confirmPrompt(question) {
15494
- const rl = readline4.createInterface({
15637
+ const rl = readline3.createInterface({
15495
15638
  input: process.stdin,
15496
15639
  output: process.stdout
15497
15640
  });
15498
- return new Promise((resolve10) => {
15641
+ return new Promise((resolve9) => {
15499
15642
  rl.question(question, (answer) => {
15500
15643
  rl.close();
15501
- resolve10(answer.trim().toLowerCase() === "y");
15644
+ resolve9(answer.trim().toLowerCase() === "y");
15502
15645
  });
15503
15646
  });
15504
15647
  }
@@ -16038,7 +16181,7 @@ function boardCommand(program2) {
16038
16181
  init_dist();
16039
16182
  init_dist();
16040
16183
  import * as path41 from "path";
16041
- import { spawn as spawn6 } from "child_process";
16184
+ import { spawn as spawn7 } from "child_process";
16042
16185
  import chalk7 from "chalk";
16043
16186
  import { Command as Command2 } from "commander";
16044
16187
  var logger4 = createLogger("queue");
@@ -16192,7 +16335,7 @@ function createQueueCommand() {
16192
16335
  const scriptPath = getScriptPath(scriptName);
16193
16336
  logger4.info(`Spawning: ${scriptPath} ${entry.projectPath}`);
16194
16337
  try {
16195
- const child = spawn6("bash", [scriptPath, entry.projectPath], {
16338
+ const child = spawn7("bash", [scriptPath, entry.projectPath], {
16196
16339
  detached: true,
16197
16340
  stdio: "ignore",
16198
16341
  env,
@@ -16312,17 +16455,17 @@ function notifyCommand(program2) {
16312
16455
  }
16313
16456
 
16314
16457
  // src/cli.ts
16315
- var __filename3 = fileURLToPath4(import.meta.url);
16316
- var __dirname4 = dirname9(__filename3);
16458
+ var __filename5 = fileURLToPath6(import.meta.url);
16459
+ var __dirname5 = dirname12(__filename5);
16317
16460
  function findPackageRoot(dir) {
16318
16461
  let d = dir;
16319
16462
  for (let i = 0; i < 5; i++) {
16320
16463
  if (existsSync30(join36(d, "package.json"))) return d;
16321
- d = dirname9(d);
16464
+ d = dirname12(d);
16322
16465
  }
16323
16466
  return dir;
16324
16467
  }
16325
- var packageRoot = findPackageRoot(__dirname4);
16468
+ var packageRoot = findPackageRoot(__dirname5);
16326
16469
  var packageJson = JSON.parse(readFileSync19(join36(packageRoot, "package.json"), "utf-8"));
16327
16470
  var program = new Command3();
16328
16471
  program.name("night-watch").description("Autonomous PRD execution using Claude CLI + cron").version(packageJson.version);