chiefwiggum 1.3.39 → 1.3.42

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.
Files changed (2) hide show
  1. package/dist/cli.cjs +270 -19
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -95,6 +95,14 @@ async function multilineText(options) {
95
95
  });
96
96
  });
97
97
  }
98
+ async function multiselect2(options) {
99
+ const result = await p.multiselect({
100
+ message: options.message,
101
+ options: options.options,
102
+ required: options.required ?? false
103
+ });
104
+ return handleCancel(result);
105
+ }
98
106
  async function searchSelect(options) {
99
107
  const { message, items, placeholder = "Type to filter...", maxVisible = 10 } = options;
100
108
  const readline = await import("readline");
@@ -1394,12 +1402,46 @@ Create 5-15 issues.${projectName ? ` Add each to project "${projectName}".` : ""
1394
1402
  }
1395
1403
  if (!usedFallback) {
1396
1404
  const total = issues.length;
1397
- console.log(import_picocolors7.default.dim(` Creating ${total} issues...`));
1405
+ const owner = repoName.split("/")[0];
1406
+ console.log(import_picocolors7.default.dim(" Creating epic from PRD..."));
1407
+ const prdTitleMatch = prdGenerated.match(/^#\s+(.+)$/m);
1408
+ const epicTitle = prdTitleMatch ? `Epic: ${prdTitleMatch[1]}` : "Epic: Project Implementation";
1409
+ const epicBodyInitial = `${prdGenerated}
1410
+
1411
+ ---
1412
+
1413
+ ## Tasks
1414
+
1415
+ _Creating issues..._
1416
+ `;
1417
+ let epicNumber = null;
1418
+ let epicUrl = null;
1419
+ try {
1420
+ const escapeForShell = (str) => {
1421
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/!/g, "\\!").replace(/\n/g, "\\n");
1422
+ };
1423
+ const epicCmd = `gh issue create --title "${escapeForShell(epicTitle)}" --body "${escapeForShell(epicBodyInitial)}" --label "epic,chiefwiggum"`;
1424
+ epicUrl = (0, import_node_child_process6.execSync)(epicCmd, {
1425
+ encoding: "utf-8",
1426
+ stdio: ["pipe", "pipe", "pipe"]
1427
+ }).trim();
1428
+ const epicNumMatch = epicUrl.match(/\/issues\/(\d+)$/);
1429
+ epicNumber = epicNumMatch ? parseInt(epicNumMatch[1], 10) : null;
1430
+ if (epicNumber) {
1431
+ console.log(import_picocolors7.default.green(` \u2713 Created epic #${epicNumber}`));
1432
+ }
1433
+ } catch {
1434
+ console.log(import_picocolors7.default.yellow(" Could not create epic issue. Continuing without parent tracking..."));
1435
+ }
1436
+ console.log(import_picocolors7.default.dim(` Creating ${total} child issues...`));
1398
1437
  let projectNumber = null;
1438
+ let projectId = null;
1439
+ let statusFieldId = null;
1440
+ let todoOptionId = null;
1399
1441
  if (projectName) {
1400
1442
  try {
1401
1443
  const projectsOutput = (0, import_node_child_process6.execSync)(
1402
- `gh project list --owner ${repoName.split("/")[0]} --format json`,
1444
+ `gh project list --owner ${owner} --format json`,
1403
1445
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1404
1446
  );
1405
1447
  const projects = JSON.parse(projectsOutput);
@@ -1408,15 +1450,48 @@ Create 5-15 issues.${projectName ? ` Add each to project "${projectName}".` : ""
1408
1450
  if (projectNumber) {
1409
1451
  try {
1410
1452
  (0, import_node_child_process6.execSync)(
1411
- `gh project link ${projectNumber} --owner ${repoName.split("/")[0]} --repo ${repoName}`,
1453
+ `gh project link ${projectNumber} --owner ${owner} --repo ${repoName}`,
1412
1454
  { stdio: ["pipe", "pipe", "pipe"] }
1413
1455
  );
1414
1456
  } catch {
1415
1457
  }
1458
+ try {
1459
+ const repoProjectsOutput = (0, import_node_child_process6.execSync)(
1460
+ `gh repo view --json projectsV2 -q '.projectsV2.Nodes[] | select(.number == ${projectNumber}) | .id'`,
1461
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1462
+ );
1463
+ projectId = repoProjectsOutput.trim() || null;
1464
+ } catch {
1465
+ }
1466
+ if (projectId) {
1467
+ try {
1468
+ const fieldsOutput = (0, import_node_child_process6.execSync)(
1469
+ `gh project field-list ${projectNumber} --owner ${owner} --format json`,
1470
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1471
+ );
1472
+ const fields = JSON.parse(fieldsOutput);
1473
+ const statusField = fields.fields?.find((f) => f.name === "Status");
1474
+ if (statusField) {
1475
+ statusFieldId = statusField.id;
1476
+ const todoOption = statusField.options?.find((o) => o.name === "Todo");
1477
+ todoOptionId = todoOption?.id || null;
1478
+ }
1479
+ } catch {
1480
+ }
1481
+ }
1416
1482
  }
1417
1483
  } catch {
1418
1484
  }
1419
1485
  }
1486
+ if (epicUrl && projectNumber) {
1487
+ try {
1488
+ (0, import_node_child_process6.execSync)(
1489
+ `gh project item-add ${projectNumber} --owner ${owner} --url "${epicUrl}"`,
1490
+ { stdio: ["pipe", "pipe", "pipe"] }
1491
+ );
1492
+ } catch {
1493
+ }
1494
+ }
1420
1495
  let availableLabels = /* @__PURE__ */ new Set();
1421
1496
  try {
1422
1497
  const labelsOutput = (0, import_node_child_process6.execSync)("gh label list --json name --jq '.[].name'", {
@@ -1426,9 +1501,21 @@ Create 5-15 issues.${projectName ? ` Add each to project "${projectName}".` : ""
1426
1501
  availableLabels = new Set(labelsOutput.trim().split("\n").filter(Boolean));
1427
1502
  } catch {
1428
1503
  }
1504
+ try {
1505
+ (0, import_node_child_process6.execSync)(
1506
+ `gh label create chiefwiggum --description "Created by Chief Wiggum CLI" --color "FFA500" 2>/dev/null || true`,
1507
+ { stdio: ["pipe", "pipe", "pipe"] }
1508
+ );
1509
+ (0, import_node_child_process6.execSync)(
1510
+ `gh label create epic --description "Parent tracking issue" --color "6366F1" 2>/dev/null || true`,
1511
+ { stdio: ["pipe", "pipe", "pipe"] }
1512
+ );
1513
+ } catch {
1514
+ }
1429
1515
  let created = 0;
1430
1516
  let failed = 0;
1431
1517
  const failedIssues = [];
1518
+ const createdIssueNumbers = [];
1432
1519
  for (let i = 0; i < issues.length; i++) {
1433
1520
  const issue = issues[i];
1434
1521
  const progress = Math.round((i + 1) / total * 100);
@@ -1439,23 +1526,49 @@ Create 5-15 issues.${projectName ? ` Add each to project "${projectName}".` : ""
1439
1526
  process.stdout.write(`\r ${bar} ${progress}% (${i + 1}/${total}) Creating: ${issue.title.slice(0, 40)}...`.padEnd(100));
1440
1527
  try {
1441
1528
  const validLabels = issue.labels.filter((label) => availableLabels.size === 0 || availableLabels.has(label));
1442
- const labelsArg = validLabels.length > 0 ? `--label "${validLabels.join(",")}"` : "";
1529
+ const allLabels = ["chiefwiggum", ...validLabels];
1530
+ const labelsArg = `--label "${allLabels.join(",")}"`;
1443
1531
  const escapeForShell = (str) => {
1444
1532
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/!/g, "\\!").replace(/\n/g, "\\n");
1445
1533
  };
1446
- const body = escapeForShell(issue.body);
1534
+ let bodyWithEpic = issue.body;
1535
+ if (epicNumber) {
1536
+ bodyWithEpic = `Part of #${epicNumber}
1537
+
1538
+ ---
1539
+
1540
+ ${issue.body}`;
1541
+ }
1542
+ const body = escapeForShell(bodyWithEpic);
1447
1543
  const title = escapeForShell(issue.title);
1448
1544
  const createCmd = `gh issue create --title "${title}" --body "${body}" ${labelsArg}`;
1449
1545
  const issueUrl = (0, import_node_child_process6.execSync)(createCmd, {
1450
1546
  encoding: "utf-8",
1451
1547
  stdio: ["pipe", "pipe", "pipe"]
1452
1548
  }).trim();
1549
+ const issueNumMatch = issueUrl.match(/\/issues\/(\d+)$/);
1550
+ if (issueNumMatch) {
1551
+ createdIssueNumbers.push(parseInt(issueNumMatch[1], 10));
1552
+ }
1453
1553
  if (projectNumber && issueUrl) {
1454
1554
  try {
1455
- (0, import_node_child_process6.execSync)(
1456
- `gh project item-add ${projectNumber} --owner ${repoName.split("/")[0]} --url "${issueUrl}"`,
1457
- { stdio: ["pipe", "pipe", "pipe"] }
1555
+ const addOutput = (0, import_node_child_process6.execSync)(
1556
+ `gh project item-add ${projectNumber} --owner ${owner} --url "${issueUrl}"`,
1557
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1458
1558
  );
1559
+ if (projectId && statusFieldId && todoOptionId) {
1560
+ const itemIdMatch = addOutput.match(/Added item\s+(\S+)/i);
1561
+ const itemId = itemIdMatch?.[1];
1562
+ if (itemId) {
1563
+ try {
1564
+ (0, import_node_child_process6.execSync)(
1565
+ `gh project item-edit --project-id ${projectId} --id ${itemId} --field-id ${statusFieldId} --single-select-option-id ${todoOptionId}`,
1566
+ { stdio: ["pipe", "pipe", "pipe"] }
1567
+ );
1568
+ } catch {
1569
+ }
1570
+ }
1571
+ }
1459
1572
  } catch {
1460
1573
  }
1461
1574
  }
@@ -1479,7 +1592,33 @@ Create 5-15 issues.${projectName ? ` Add each to project "${projectName}".` : ""
1479
1592
  }
1480
1593
  }
1481
1594
  if (projectName && projectNumber) {
1482
- console.log(import_picocolors7.default.green(` \u2713 Added to board "${projectName}"`));
1595
+ if (todoOptionId) {
1596
+ console.log(import_picocolors7.default.green(` \u2713 Added to board "${projectName}" (Todo column)`));
1597
+ } else {
1598
+ console.log(import_picocolors7.default.green(` \u2713 Added to board "${projectName}"`));
1599
+ }
1600
+ }
1601
+ if (epicNumber && createdIssueNumbers.length > 0) {
1602
+ const escapeForShell = (str) => {
1603
+ return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/`/g, "\\`").replace(/\$/g, "\\$").replace(/!/g, "\\!").replace(/\n/g, "\\n");
1604
+ };
1605
+ const tasklist = createdIssueNumbers.map((num) => `- [ ] #${num}`).join("\n");
1606
+ const updatedEpicBody = `${prdGenerated}
1607
+
1608
+ ---
1609
+
1610
+ ## Tasks
1611
+
1612
+ ${tasklist}`;
1613
+ try {
1614
+ (0, import_node_child_process6.execSync)(
1615
+ `gh issue edit ${epicNumber} --body "${escapeForShell(updatedEpicBody)}"`,
1616
+ { stdio: ["pipe", "pipe", "pipe"] }
1617
+ );
1618
+ console.log(import_picocolors7.default.green(` \u2713 Updated epic #${epicNumber} with tasklist`));
1619
+ } catch {
1620
+ console.log(import_picocolors7.default.yellow(` Could not update epic with tasklist`));
1621
+ }
1483
1622
  }
1484
1623
  } else {
1485
1624
  console.log(import_picocolors7.default.green(" \u2713 GitHub Issues created"));
@@ -1712,11 +1851,123 @@ init_loop();
1712
1851
  // src/commands/clean.ts
1713
1852
  init_cjs_shims();
1714
1853
  var import_node_fs4 = require("fs");
1854
+ var import_node_child_process7 = require("child_process");
1715
1855
  var import_picocolors8 = __toESM(require("picocolors"), 1);
1716
1856
  init_prompts();
1857
+ init_new();
1858
+ async function removeIssues(numbers, action) {
1859
+ let success = 0;
1860
+ let failed = 0;
1861
+ for (const num of numbers) {
1862
+ try {
1863
+ if (action === "delete") {
1864
+ (0, import_node_child_process7.execSync)(`gh issue delete ${num} --yes`, { stdio: ["pipe", "pipe", "pipe"] });
1865
+ } else {
1866
+ (0, import_node_child_process7.execSync)(`gh issue close ${num}`, { stdio: ["pipe", "pipe", "pipe"] });
1867
+ }
1868
+ console.log(import_picocolors8.default.green(`\u2713 #${num} ${action === "delete" ? "deleted" : "closed"}`));
1869
+ success++;
1870
+ } catch {
1871
+ console.log(import_picocolors8.default.red(`\u2717 #${num} failed`));
1872
+ failed++;
1873
+ }
1874
+ }
1875
+ console.log();
1876
+ console.log(`${action === "delete" ? "Deleted" : "Closed"}: ${success}, Failed: ${failed}`);
1877
+ }
1878
+ async function cleanGitHubIssues() {
1879
+ let issues = [];
1880
+ try {
1881
+ const output = (0, import_node_child_process7.execSync)(
1882
+ `gh issue list --label chiefwiggum --state open --json number,title,createdAt,labels --limit 100`,
1883
+ { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1884
+ );
1885
+ issues = JSON.parse(output);
1886
+ } catch {
1887
+ console.log(import_picocolors8.default.yellow("Could not fetch GitHub issues."));
1888
+ return;
1889
+ }
1890
+ if (issues.length === 0) {
1891
+ console.log(import_picocolors8.default.dim("No open issues with 'chiefwiggum' label found."));
1892
+ return;
1893
+ }
1894
+ const isEpic = (i) => i.labels.some((l) => l.name === "epic");
1895
+ const epics = issues.filter(isEpic);
1896
+ const childIssues = issues.filter((i) => !isEpic(i));
1897
+ console.log();
1898
+ console.log(`Found ${issues.length} issues with 'chiefwiggum' label:`);
1899
+ if (epics.length > 0) {
1900
+ console.log(import_picocolors8.default.dim(" Epics:"));
1901
+ for (const issue of epics) {
1902
+ console.log(` ${import_picocolors8.default.magenta(`#${issue.number}`)} ${import_picocolors8.default.bold("\u{1F4CB}")} ${issue.title}`);
1903
+ }
1904
+ }
1905
+ if (childIssues.length > 0) {
1906
+ console.log(import_picocolors8.default.dim(" Tasks:"));
1907
+ for (const issue of childIssues) {
1908
+ console.log(` ${import_picocolors8.default.cyan(`#${issue.number}`)} - ${issue.title}`);
1909
+ }
1910
+ }
1911
+ console.log();
1912
+ const action = await select2({
1913
+ message: "What would you like to do with these issues?",
1914
+ options: [
1915
+ { value: "close", label: "Close all", hint: "reversible" },
1916
+ { value: "delete", label: "Delete all", hint: "permanent" },
1917
+ { value: "pick", label: "Pick which to remove" },
1918
+ { value: "cancel", label: "Cancel" }
1919
+ ]
1920
+ });
1921
+ if (action === "cancel") {
1922
+ return;
1923
+ }
1924
+ if (action === "pick") {
1925
+ const selected = await multiselect2({
1926
+ message: "Select issues to remove (space to toggle, enter to confirm)",
1927
+ options: issues.map((i) => ({
1928
+ value: String(i.number),
1929
+ label: isEpic(i) ? `\u{1F4CB} #${i.number} - ${i.title}` : `#${i.number} - ${i.title}`,
1930
+ hint: isEpic(i) ? "epic" : void 0
1931
+ }))
1932
+ });
1933
+ if (selected.length === 0) {
1934
+ console.log(import_picocolors8.default.yellow("No issues selected."));
1935
+ return;
1936
+ }
1937
+ const pickAction = await select2({
1938
+ message: `${selected.length} issues selected. Close or delete?`,
1939
+ options: [
1940
+ { value: "close", label: "Close", hint: "reversible" },
1941
+ { value: "delete", label: "Delete", hint: "permanent" }
1942
+ ]
1943
+ });
1944
+ await removeIssues(selected, pickAction);
1945
+ } else {
1946
+ const issueNumbers = issues.map((i) => String(i.number));
1947
+ const confirmMsg = action === "delete" ? `DELETE ${issues.length} issues? This cannot be undone!` : `Close ${issues.length} issues?`;
1948
+ const confirmed = await confirm2({
1949
+ message: confirmMsg,
1950
+ initialValue: false
1951
+ });
1952
+ if (confirmed) {
1953
+ await removeIssues(issueNumbers, action);
1954
+ }
1955
+ }
1956
+ }
1717
1957
  async function cmdClean() {
1718
1958
  console.log(import_picocolors8.default.bold("Clean Project"));
1719
1959
  console.log();
1960
+ const tracker = getProjectTracker();
1961
+ if (tracker === "github") {
1962
+ const cleanIssues = await confirm2({
1963
+ message: "Clean up GitHub Issues created by Chief Wiggum?",
1964
+ initialValue: false
1965
+ });
1966
+ if (cleanIssues) {
1967
+ await cleanGitHubIssues();
1968
+ }
1969
+ console.log();
1970
+ }
1720
1971
  const filesToRemove = [];
1721
1972
  if ((0, import_node_fs4.existsSync)(".chiefwiggum")) filesToRemove.push(".chiefwiggum/");
1722
1973
  if ((0, import_node_fs4.existsSync)("specs")) filesToRemove.push("specs/");
@@ -1750,7 +2001,7 @@ async function cmdClean() {
1750
2001
  // src/commands/config.ts
1751
2002
  init_cjs_shims();
1752
2003
  var import_node_fs5 = require("fs");
1753
- var import_node_child_process7 = require("child_process");
2004
+ var import_node_child_process8 = require("child_process");
1754
2005
  var import_picocolors9 = __toESM(require("picocolors"), 1);
1755
2006
  init_prompts();
1756
2007
  var CONFIG_FILE2 = ".chiefwiggum/CLAUDE.md";
@@ -1783,17 +2034,17 @@ function saveConfig2(config2) {
1783
2034
  }
1784
2035
  function checkGitHubCLI2() {
1785
2036
  try {
1786
- (0, import_node_child_process7.execSync)("which gh", { stdio: ["pipe", "pipe", "pipe"] });
2037
+ (0, import_node_child_process8.execSync)("which gh", { stdio: ["pipe", "pipe", "pipe"] });
1787
2038
  } catch {
1788
2039
  return { ok: false, reason: "not_installed" };
1789
2040
  }
1790
2041
  try {
1791
- (0, import_node_child_process7.execSync)("gh auth status", { stdio: ["pipe", "pipe", "pipe"] });
2042
+ (0, import_node_child_process8.execSync)("gh auth status", { stdio: ["pipe", "pipe", "pipe"] });
1792
2043
  } catch {
1793
2044
  return { ok: false, reason: "not_authenticated" };
1794
2045
  }
1795
2046
  try {
1796
- const repo = (0, import_node_child_process7.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
2047
+ const repo = (0, import_node_child_process8.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
1797
2048
  encoding: "utf-8",
1798
2049
  stdio: ["pipe", "pipe", "pipe"]
1799
2050
  }).trim();
@@ -1883,7 +2134,7 @@ async function selectOrCreateProjectBoard2(knownRepo) {
1883
2134
  let repoName = knownRepo || "";
1884
2135
  if (!repoName) {
1885
2136
  try {
1886
- repoName = (0, import_node_child_process7.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
2137
+ repoName = (0, import_node_child_process8.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
1887
2138
  encoding: "utf-8",
1888
2139
  stdio: ["pipe", "pipe", "pipe"]
1889
2140
  }).trim();
@@ -1894,7 +2145,7 @@ async function selectOrCreateProjectBoard2(knownRepo) {
1894
2145
  }
1895
2146
  let existingProjects = [];
1896
2147
  try {
1897
- const projectsOutput = (0, import_node_child_process7.execSync)(`gh project list --owner ${repoName.split("/")[0]} --format json`, {
2148
+ const projectsOutput = (0, import_node_child_process8.execSync)(`gh project list --owner ${repoName.split("/")[0]} --format json`, {
1898
2149
  encoding: "utf-8",
1899
2150
  stdio: ["pipe", "pipe", "pipe"]
1900
2151
  });
@@ -1922,7 +2173,7 @@ async function selectOrCreateProjectBoard2(knownRepo) {
1922
2173
  });
1923
2174
  console.log(import_picocolors9.default.cyan(`Creating board "${projectName}"...`));
1924
2175
  try {
1925
- const createOutput = (0, import_node_child_process7.execSync)(
2176
+ const createOutput = (0, import_node_child_process8.execSync)(
1926
2177
  `gh project create --owner ${repoName.split("/")[0]} --title "${projectName}"`,
1927
2178
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1928
2179
  );
@@ -1931,7 +2182,7 @@ async function selectOrCreateProjectBoard2(knownRepo) {
1931
2182
  console.log(import_picocolors9.default.green(`\u2713 Board "${projectName}" created`));
1932
2183
  if (projectNumber) {
1933
2184
  try {
1934
- (0, import_node_child_process7.execSync)(`gh project link ${projectNumber} --owner ${repoName.split("/")[0]} --repo ${repoName}`, {
2185
+ (0, import_node_child_process8.execSync)(`gh project link ${projectNumber} --owner ${repoName.split("/")[0]} --repo ${repoName}`, {
1935
2186
  stdio: ["pipe", "pipe", "pipe"]
1936
2187
  });
1937
2188
  console.log(import_picocolors9.default.green(`\u2713 Linked to ${repoName}`));
@@ -1946,14 +2197,14 @@ async function selectOrCreateProjectBoard2(knownRepo) {
1946
2197
  }
1947
2198
  } else if (selectedProject !== "__none__") {
1948
2199
  try {
1949
- const projectsOutput = (0, import_node_child_process7.execSync)(
2200
+ const projectsOutput = (0, import_node_child_process8.execSync)(
1950
2201
  `gh project list --owner ${repoName.split("/")[0]} --format json`,
1951
2202
  { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
1952
2203
  );
1953
2204
  const projects = JSON.parse(projectsOutput);
1954
2205
  const project = projects.projects?.find((p2) => p2.title === selectedProject);
1955
2206
  if (project?.number) {
1956
- (0, import_node_child_process7.execSync)(
2207
+ (0, import_node_child_process8.execSync)(
1957
2208
  `gh project link ${project.number} --owner ${repoName.split("/")[0]} --repo ${repoName}`,
1958
2209
  { stdio: ["pipe", "pipe", "pipe"] }
1959
2210
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chiefwiggum",
3
- "version": "1.3.39",
3
+ "version": "1.3.42",
4
4
  "description": "Autonomous coding agent CLI. Point it at a plan, watch it build.",
5
5
  "type": "module",
6
6
  "bin": {