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 +91 -31
- package/package.json +1 -1
- package/templates/prd-template.md +26 -3
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
|
|
1439
|
-
|
|
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
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
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
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
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
|
-
})
|
|
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
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
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
|
-
|
|
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.
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
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,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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|