chiefwiggum 1.3.47 → 1.3.51

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.cjs CHANGED
@@ -865,6 +865,22 @@ function countOpenGitHubIssues() {
865
865
  return 0;
866
866
  }
867
867
  }
868
+ function parsePRDFrontmatter(content) {
869
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
870
+ if (!frontmatterMatch) return null;
871
+ const frontmatter = {};
872
+ const lines = frontmatterMatch[1].split("\n");
873
+ for (const line of lines) {
874
+ const match = line.match(/^(\w+):\s*"?([^"]+)"?$/);
875
+ if (match) {
876
+ const [, key, value] = match;
877
+ if (key === "project" || key === "branch" || key === "priority" || key === "source_plan") {
878
+ frontmatter[key] = value.trim();
879
+ }
880
+ }
881
+ }
882
+ return Object.keys(frontmatter).length > 0 ? frontmatter : null;
883
+ }
868
884
  function findMarkdownFiles(dir, basePath = "") {
869
885
  const results = [];
870
886
  try {
@@ -1285,6 +1301,7 @@ async function generateFromPlan(planFile) {
1285
1301
  const claudeTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/CLAUDE-template.md`, "utf-8");
1286
1302
  console.log();
1287
1303
  console.log(import_picocolors7.default.yellow(`[1/4] Generating ${prdPath}...`));
1304
+ const planFilename = planFile.split("/").pop() || planFile;
1288
1305
  const prdPrompt = `You are filling in a PRD template based on a project plan.
1289
1306
 
1290
1307
  Here is the plan:
@@ -1299,6 +1316,18 @@ ${prdTemplate}
1299
1316
 
1300
1317
  Fill in the template with specific details from the plan.
1301
1318
  Replace all placeholder text in [brackets] with real content.
1319
+
1320
+ IMPORTANT for YAML frontmatter at the top:
1321
+ - project: Use a short, descriptive project name (e.g., "User Authentication System")
1322
+ - branch: Suggest a git branch name (e.g., "feat/user-auth")
1323
+ - priority: Set to "high", "medium", or "low" based on the plan
1324
+ - source_plan: Set to "${planFilename}"
1325
+
1326
+ For User Stories section:
1327
+ - Each "### Story:" becomes a GitHub Issue
1328
+ - Use P0 for critical, P1 for important, P2 for nice-to-have
1329
+ - Include specific acceptance criteria as checkboxes
1330
+
1302
1331
  Write the completed PRD directly to ${prdPath}.
1303
1332
  Do NOT ask questions \u2014 infer everything from the plan.`;
1304
1333
  await runClaude({ prompt: prdPrompt });
@@ -1435,8 +1464,14 @@ Create 5-15 issues.${projectName ? ` Add each to project "${projectName}".` : ""
1435
1464
  } catch {
1436
1465
  }
1437
1466
  console.log(import_picocolors7.default.dim(" Creating epic from PRD..."));
1438
- const prdTitleMatch = prdGenerated.match(/^#\s+(.+)$/m);
1439
- const epicTitle = prdTitleMatch ? `Epic: ${prdTitleMatch[1]}` : "Epic: Project Implementation";
1467
+ const prdFrontmatter = parsePRDFrontmatter(prdGenerated);
1468
+ let epicTitle;
1469
+ if (prdFrontmatter?.project) {
1470
+ epicTitle = `Epic: ${prdFrontmatter.project}`;
1471
+ } else {
1472
+ const planTitleMatch = planContent.match(/^#\s+(.+)$/m);
1473
+ epicTitle = planTitleMatch ? `Epic: ${planTitleMatch[1]}` : "Epic: Project Implementation";
1474
+ }
1440
1475
  const epicBodyInitial = `${prdGenerated}
1441
1476
 
1442
1477
  ---
@@ -1549,21 +1584,27 @@ _Creating issues..._
1549
1584
  let failed = 0;
1550
1585
  const failedIssues = [];
1551
1586
  const createdIssueNumbers = [];
1587
+ const supportsInPlace = process.stdout.isTTY && !process.env.SSH_TTY && !process.env.CI;
1588
+ const loggedMilestones = /* @__PURE__ */ new Set();
1552
1589
  for (let i = 0; i < issues.length; i++) {
1553
1590
  const issue = issues[i];
1554
1591
  const progress = Math.round((i + 1) / total * 100);
1555
- const barWidth = 20;
1556
- const filled = Math.round(progress / 100 * barWidth);
1557
- const empty = barWidth - filled;
1558
- const bar = import_picocolors7.default.green("\u2588".repeat(filled)) + import_picocolors7.default.dim("\u2591".repeat(empty));
1559
- process.stdout.write(`\x1B[2K\r ${bar} ${progress}% (${i + 1}/${total}) Creating: ${issue.title.slice(0, 40)}...`);
1592
+ if (supportsInPlace) {
1593
+ const barWidth = 20;
1594
+ const filled = Math.round(progress / 100 * barWidth);
1595
+ const empty = barWidth - filled;
1596
+ const bar = import_picocolors7.default.green("\u2588".repeat(filled)) + import_picocolors7.default.dim("\u2591".repeat(empty));
1597
+ process.stdout.write(`\r ${bar} ${progress}% (${i + 1}/${total}) Creating: ${issue.title.slice(0, 40)}...`);
1598
+ } else {
1599
+ const milestone = Math.floor(progress / 25) * 25;
1600
+ if (milestone > 0 && !loggedMilestones.has(milestone) && milestone < 100) {
1601
+ console.log(import_picocolors7.default.dim(` ${milestone}% complete (${i + 1}/${total})...`));
1602
+ loggedMilestones.add(milestone);
1603
+ }
1604
+ }
1560
1605
  try {
1561
1606
  const validLabels = issue.labels.filter((label) => availableLabels.size === 0 || availableLabels.has(label));
1562
1607
  const allLabels = ["chiefwiggum", ...validLabels];
1563
- const labelsArg = `--label "${allLabels.join(",")}"`;
1564
- const escapeForShell = (str) => {
1565
- return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/!/g, "\\!").replace(/\n/g, "\\n");
1566
- };
1567
1608
  let bodyWithEpic = issue.body;
1568
1609
  if (epicNumber) {
1569
1610
  bodyWithEpic = `Part of #${epicNumber}
@@ -1572,13 +1613,23 @@ _Creating issues..._
1572
1613
 
1573
1614
  ${issue.body}`;
1574
1615
  }
1575
- const body = escapeForShell(bodyWithEpic);
1576
- const title = escapeForShell(issue.title);
1577
- const createCmd = `gh issue create --title "${title}" --body "${body}" ${labelsArg}`;
1578
- const issueUrl = (0, import_node_child_process6.execSync)(createCmd, {
1616
+ const createResult = (0, import_node_child_process6.spawnSync)("gh", [
1617
+ "issue",
1618
+ "create",
1619
+ "--title",
1620
+ issue.title,
1621
+ "--body",
1622
+ bodyWithEpic,
1623
+ "--label",
1624
+ allLabels.join(",")
1625
+ ], {
1579
1626
  encoding: "utf-8",
1580
1627
  stdio: ["pipe", "pipe", "pipe"]
1581
- }).trim();
1628
+ });
1629
+ if (createResult.status !== 0) {
1630
+ throw new Error(createResult.stderr || "Failed to create issue");
1631
+ }
1632
+ const issueUrl = createResult.stdout.trim();
1582
1633
  const issueNumMatch = issueUrl.match(/\/issues\/(\d+)$/);
1583
1634
  if (issueNumMatch) {
1584
1635
  createdIssueNumbers.push(parseInt(issueNumMatch[1], 10));
@@ -1586,20 +1637,20 @@ ${issue.body}`;
1586
1637
  if (projectNumber && issueUrl) {
1587
1638
  try {
1588
1639
  const addOutput = (0, import_node_child_process6.execSync)(
1589
- `gh project item-add ${projectNumber} --owner ${owner} --url "${issueUrl}"`,
1640
+ `gh project item-add ${projectNumber} --owner ${owner} --url "${issueUrl}" --format json`,
1590
1641
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1591
1642
  );
1592
1643
  if (projectId && statusFieldId && todoOptionId) {
1593
- const itemIdMatch = addOutput.match(/Added item\s+(\S+)/i);
1594
- const itemId = itemIdMatch?.[1];
1595
- if (itemId) {
1596
- try {
1644
+ try {
1645
+ const addResult = JSON.parse(addOutput);
1646
+ const itemId = addResult.id;
1647
+ if (itemId) {
1597
1648
  (0, import_node_child_process6.execSync)(
1598
1649
  `gh project item-edit --project-id ${projectId} --id ${itemId} --field-id ${statusFieldId} --single-select-option-id ${todoOptionId}`,
1599
1650
  { stdio: ["pipe", "pipe", "pipe"] }
1600
1651
  );
1601
- } catch {
1602
1652
  }
1653
+ } catch {
1603
1654
  }
1604
1655
  }
1605
1656
  } catch {
@@ -1612,7 +1663,9 @@ ${issue.body}`;
1612
1663
  failedIssues.push({ title: issue.title, error: errorMsg });
1613
1664
  }
1614
1665
  }
1615
- process.stdout.write("\r" + " ".repeat(100) + "\r");
1666
+ if (supportsInPlace) {
1667
+ process.stdout.write("\r" + " ".repeat(80) + "\r");
1668
+ }
1616
1669
  console.log(import_picocolors7.default.green(` \u2713 Created ${created} GitHub Issues`));
1617
1670
  if (failed > 0) {
1618
1671
  console.log(import_picocolors7.default.yellow(` \u26A0 ${failed} issues failed to create:`));
@@ -1632,9 +1685,6 @@ ${issue.body}`;
1632
1685
  }
1633
1686
  }
1634
1687
  if (epicNumber && createdIssueNumbers.length > 0) {
1635
- const escapeForShell = (str) => {
1636
- return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/!/g, "\\!").replace(/\n/g, "\\n");
1637
- };
1638
1688
  const tasklist = createdIssueNumbers.map((num) => `- [ ] #${num}`).join("\n");
1639
1689
  const updatedEpicBody = `${prdGenerated}
1640
1690
 
@@ -1644,11 +1694,21 @@ ${issue.body}`;
1644
1694
 
1645
1695
  ${tasklist}`;
1646
1696
  try {
1647
- (0, import_node_child_process6.execSync)(
1648
- `gh issue edit ${epicNumber} --body "${escapeForShell(updatedEpicBody)}"`,
1649
- { stdio: ["pipe", "pipe", "pipe"] }
1650
- );
1651
- console.log(import_picocolors7.default.green(` \u2713 Updated epic #${epicNumber} with tasklist`));
1697
+ const editResult = (0, import_node_child_process6.spawnSync)("gh", [
1698
+ "issue",
1699
+ "edit",
1700
+ String(epicNumber),
1701
+ "--body",
1702
+ updatedEpicBody
1703
+ ], {
1704
+ encoding: "utf-8",
1705
+ stdio: ["pipe", "pipe", "pipe"]
1706
+ });
1707
+ if (editResult.status === 0) {
1708
+ console.log(import_picocolors7.default.green(` \u2713 Updated epic #${epicNumber} with tasklist`));
1709
+ } else {
1710
+ throw new Error(editResult.stderr || "Failed");
1711
+ }
1652
1712
  } catch {
1653
1713
  console.log(import_picocolors7.default.yellow(` Could not update epic with tasklist`));
1654
1714
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chiefwiggum",
3
- "version": "1.3.47",
3
+ "version": "1.3.51",
4
4
  "description": "Autonomous coding agent CLI. Point it at a plan, watch it build.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,3 +1,10 @@
1
+ ---
2
+ project: "[Project Name - short, descriptive title]"
3
+ branch: "[feat/feature-name or fix/bug-name]"
4
+ priority: "[high|medium|low]"
5
+ source_plan: "[filename of the input plan]"
6
+ ---
7
+
1
8
  # Product Requirements Document
2
9
 
3
10
  ## Overview
@@ -15,9 +22,25 @@
15
22
 
16
23
  ## User Stories
17
24
 
18
- ### As a [user type]
19
- - I want to [action]
20
- - So that [benefit]
25
+ <!-- Each story becomes a GitHub Issue. Use this exact format: -->
26
+
27
+ ### Story: [Short descriptive title]
28
+ - **Priority**: P0
29
+ - **As a**: [user type]
30
+ - **I want**: [action]
31
+ - **So that**: [benefit]
32
+ - **Acceptance Criteria**:
33
+ - [ ] Criterion 1
34
+ - [ ] Criterion 2
35
+
36
+ ### Story: [Another story title]
37
+ - **Priority**: P1
38
+ - **As a**: [user type]
39
+ - **I want**: [action]
40
+ - **So that**: [benefit]
41
+ - **Acceptance Criteria**:
42
+ - [ ] Criterion 1
43
+ - [ ] Criterion 2
21
44
 
22
45
  ## Functional Requirements
23
46