@jonit-dev/night-watch-cli 1.7.96 → 1.7.97

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.js +251 -555
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1599,17 +1599,14 @@ var init_github_graphql = __esm({
1599
1599
  }
1600
1600
  });
1601
1601
 
1602
- // ../core/dist/board/providers/github-projects.js
1603
- import { execFile as execFile2 } from "child_process";
1604
- import { promisify as promisify2 } from "util";
1605
- var execFileAsync2, GitHubProjectsProvider;
1606
- var init_github_projects = __esm({
1607
- "../core/dist/board/providers/github-projects.js"() {
1602
+ // ../core/dist/board/providers/github-projects-base.js
1603
+ var GitHubProjectsBase;
1604
+ var init_github_projects_base = __esm({
1605
+ "../core/dist/board/providers/github-projects-base.js"() {
1608
1606
  "use strict";
1609
1607
  init_types2();
1610
1608
  init_github_graphql();
1611
- execFileAsync2 = promisify2(execFile2);
1612
- GitHubProjectsProvider = class {
1609
+ GitHubProjectsBase = class {
1613
1610
  config;
1614
1611
  cwd;
1615
1612
  cachedProjectId = null;
@@ -1617,15 +1614,19 @@ var init_github_projects = __esm({
1617
1614
  cachedOptionIds = /* @__PURE__ */ new Map();
1618
1615
  cachedOwner = null;
1619
1616
  cachedRepositoryId = null;
1617
+ cachedRepoNameWithOwner = null;
1620
1618
  constructor(config, cwd) {
1621
1619
  this.config = config;
1622
1620
  this.cwd = cwd;
1623
1621
  }
1624
1622
  // -------------------------------------------------------------------------
1625
- // Helpers
1623
+ // Repo resolution
1626
1624
  // -------------------------------------------------------------------------
1627
1625
  async getRepo() {
1628
- return this.config.repo ?? getRepoNwo(this.cwd);
1626
+ if (this.cachedRepoNameWithOwner)
1627
+ return this.cachedRepoNameWithOwner;
1628
+ this.cachedRepoNameWithOwner = this.config.repo ?? await getRepoNwo(this.cwd);
1629
+ return this.cachedRepoNameWithOwner;
1629
1630
  }
1630
1631
  async getRepoParts() {
1631
1632
  const repo = await this.getRepo();
@@ -1635,131 +1636,102 @@ var init_github_projects = __esm({
1635
1636
  }
1636
1637
  return { owner, name };
1637
1638
  }
1639
+ normalizeRepoName(repo) {
1640
+ return repo.trim().toLowerCase();
1641
+ }
1642
+ isCurrentRepoItem(content, repo) {
1643
+ const repoNameWithOwner = content?.repository?.nameWithOwner;
1644
+ if (!repoNameWithOwner)
1645
+ return true;
1646
+ return this.normalizeRepoName(repoNameWithOwner) === this.normalizeRepoName(repo);
1647
+ }
1638
1648
  async getRepoOwnerLogin() {
1639
1649
  return (await this.getRepoParts()).owner;
1640
1650
  }
1641
1651
  async getRepoOwner() {
1642
- if (this.cachedOwner && this.cachedRepositoryId) {
1652
+ if (this.cachedOwner && this.cachedRepositoryId)
1643
1653
  return this.cachedOwner;
1644
- }
1645
1654
  const { owner, name } = await this.getRepoParts();
1646
1655
  const data = await graphql(`query ResolveRepoOwner($owner: String!, $name: String!) {
1647
1656
  repository(owner: $owner, name: $name) {
1648
1657
  id
1649
- owner {
1650
- __typename
1651
- id
1652
- login
1653
- }
1658
+ owner { __typename id login }
1654
1659
  }
1655
1660
  }`, { owner, name }, this.cwd);
1656
- if (!data.repository) {
1661
+ if (!data.repository)
1657
1662
  throw new Error(`Repository ${owner}/${name} not found.`);
1658
- }
1659
1663
  const ownerNode = data.repository.owner;
1660
1664
  if (!ownerNode || ownerNode.__typename !== "User" && ownerNode.__typename !== "Organization") {
1661
1665
  throw new Error(`Failed to resolve repository owner for ${owner}/${name}.`);
1662
1666
  }
1663
1667
  this.cachedRepositoryId = data.repository.id;
1664
- this.cachedOwner = {
1665
- id: ownerNode.id,
1666
- login: ownerNode.login,
1667
- type: ownerNode.__typename
1668
- };
1668
+ this.cachedOwner = { id: ownerNode.id, login: ownerNode.login, type: ownerNode.__typename };
1669
1669
  return this.cachedOwner;
1670
1670
  }
1671
1671
  async getRepositoryNodeId() {
1672
- if (this.cachedRepositoryId) {
1672
+ if (this.cachedRepositoryId)
1673
1673
  return this.cachedRepositoryId;
1674
- }
1675
1674
  await this.getRepoOwner();
1676
1675
  if (!this.cachedRepositoryId) {
1677
1676
  throw new Error(`Failed to resolve repository ID for ${await this.getRepo()}.`);
1678
1677
  }
1679
1678
  return this.cachedRepositoryId;
1680
1679
  }
1681
- async linkProjectToRepository(projectId) {
1682
- const repositoryId = await this.getRepositoryNodeId();
1680
+ // -------------------------------------------------------------------------
1681
+ // Project discovery & caching
1682
+ // -------------------------------------------------------------------------
1683
+ async resolveProjectNode(projectNumber) {
1684
+ const ownerLogins = /* @__PURE__ */ new Set([await this.getRepoOwnerLogin()]);
1683
1685
  try {
1684
- await graphql(`mutation LinkProjectToRepository($projectId: ID!, $repositoryId: ID!) {
1685
- linkProjectV2ToRepository(input: { projectId: $projectId, repositoryId: $repositoryId }) {
1686
- repository {
1687
- id
1688
- }
1689
- }
1690
- }`, { projectId, repositoryId }, this.cwd);
1691
- } catch (err) {
1692
- const message = err instanceof Error ? err.message : String(err);
1693
- const normalized = message.toLowerCase();
1694
- if (normalized.includes("already") && normalized.includes("project")) {
1695
- return;
1696
- }
1697
- throw err;
1686
+ ownerLogins.add(await getViewerLogin(this.cwd));
1687
+ } catch {
1698
1688
  }
1689
+ for (const login of ownerLogins) {
1690
+ const node = await this.fetchProjectNode(login, projectNumber);
1691
+ if (node)
1692
+ return node;
1693
+ }
1694
+ return null;
1699
1695
  }
1700
- async fetchStatusField(projectId) {
1701
- const fieldData = await graphql(`query GetStatusField($projectId: ID!) {
1702
- node(id: $projectId) {
1703
- ... on ProjectV2 {
1704
- field(name: "Status") {
1705
- ... on ProjectV2SingleSelectField {
1706
- id
1707
- options {
1708
- id
1709
- name
1710
- }
1711
- }
1712
- }
1696
+ /** Try user query first, fall back to org query. */
1697
+ async fetchProjectNode(login, projectNumber) {
1698
+ try {
1699
+ const userData = await graphql(`query GetProject($login: String!, $number: Int!) {
1700
+ user(login: $login) {
1701
+ projectV2(number: $number) { id number title url }
1713
1702
  }
1703
+ }`, { login, number: projectNumber }, this.cwd);
1704
+ if (userData.user?.projectV2)
1705
+ return userData.user.projectV2;
1706
+ } catch {
1714
1707
  }
1715
- }`, { projectId }, this.cwd);
1716
- const field = fieldData.node?.field;
1717
- if (!field) {
1718
- throw new Error(`Status field not found on project ${projectId}. Run \`night-watch board setup\` to create it.`);
1708
+ try {
1709
+ const orgData = await graphql(`query GetOrgProject($login: String!, $number: Int!) {
1710
+ organization(login: $login) {
1711
+ projectV2(number: $number) { id number title url }
1712
+ }
1713
+ }`, { login, number: projectNumber }, this.cwd);
1714
+ if (orgData.organization?.projectV2)
1715
+ return orgData.organization.projectV2;
1716
+ } catch {
1719
1717
  }
1720
- return {
1721
- fieldId: field.id,
1722
- optionIds: new Map(field.options.map((o) => [o.name, o.id]))
1723
- };
1718
+ return null;
1724
1719
  }
1725
- /**
1726
- * Fetch and cache the project node ID, Status field ID, and option IDs.
1727
- * Throws if the project cannot be found or has no Status field.
1728
- */
1729
1720
  async ensureProjectCache() {
1730
1721
  if (this.cachedProjectId !== null && this.cachedFieldId !== null && this.cachedOptionIds.size > 0) {
1731
- return {
1732
- projectId: this.cachedProjectId,
1733
- fieldId: this.cachedFieldId,
1734
- optionIds: this.cachedOptionIds
1735
- };
1722
+ return { projectId: this.cachedProjectId, fieldId: this.cachedFieldId, optionIds: this.cachedOptionIds };
1736
1723
  }
1737
1724
  if (this.cachedProjectId !== null) {
1738
1725
  const statusField2 = await this.fetchStatusField(this.cachedProjectId);
1739
1726
  this.cachedFieldId = statusField2.fieldId;
1740
1727
  this.cachedOptionIds = statusField2.optionIds;
1741
- return {
1742
- projectId: this.cachedProjectId,
1743
- fieldId: this.cachedFieldId,
1744
- optionIds: this.cachedOptionIds
1745
- };
1728
+ return { projectId: this.cachedProjectId, fieldId: this.cachedFieldId, optionIds: this.cachedOptionIds };
1746
1729
  }
1747
1730
  const projectNumber = this.config.projectNumber;
1748
1731
  if (!projectNumber) {
1749
1732
  throw new Error("No projectNumber configured. Run `night-watch board setup` first.");
1750
1733
  }
1751
- const ownerLogins = /* @__PURE__ */ new Set([await this.getRepoOwnerLogin()]);
1752
- try {
1753
- ownerLogins.add(await getViewerLogin(this.cwd));
1754
- } catch {
1755
- }
1756
- let projectNode = null;
1757
- for (const login of ownerLogins) {
1758
- projectNode = await this.fetchProjectNode(login, projectNumber);
1759
- if (projectNode) {
1760
- break;
1761
- }
1762
- }
1734
+ const projectNode = await this.resolveProjectNode(projectNumber);
1763
1735
  if (!projectNode) {
1764
1736
  throw new Error(`GitHub Project #${projectNumber} not found for repository owner "${await this.getRepoOwnerLogin()}".`);
1765
1737
  }
@@ -1767,129 +1739,92 @@ var init_github_projects = __esm({
1767
1739
  const statusField = await this.fetchStatusField(projectNode.id);
1768
1740
  this.cachedFieldId = statusField.fieldId;
1769
1741
  this.cachedOptionIds = statusField.optionIds;
1770
- return {
1771
- projectId: this.cachedProjectId,
1772
- fieldId: this.cachedFieldId,
1773
- optionIds: this.cachedOptionIds
1774
- };
1742
+ return { projectId: this.cachedProjectId, fieldId: this.cachedFieldId, optionIds: this.cachedOptionIds };
1775
1743
  }
1776
- /** Try user query first, fall back to org query. */
1777
- async fetchProjectNode(login, projectNumber) {
1778
- try {
1779
- const userData = await graphql(`query GetProject($login: String!, $number: Int!) {
1780
- user(login: $login) {
1781
- projectV2(number: $number) {
1782
- id
1783
- number
1784
- title
1785
- url
1744
+ // -------------------------------------------------------------------------
1745
+ // Status field management
1746
+ // -------------------------------------------------------------------------
1747
+ async fetchStatusField(projectId) {
1748
+ const fieldData = await graphql(`query GetStatusField($projectId: ID!) {
1749
+ node(id: $projectId) {
1750
+ ... on ProjectV2 {
1751
+ field(name: "Status") {
1752
+ ... on ProjectV2SingleSelectField {
1753
+ id
1754
+ options { id name }
1755
+ }
1786
1756
  }
1787
1757
  }
1788
- }`, { login, number: projectNumber }, this.cwd);
1789
- if (userData.user?.projectV2) {
1790
- return userData.user.projectV2;
1791
- }
1792
- } catch {
1793
1758
  }
1794
- try {
1795
- const orgData = await graphql(`query GetOrgProject($login: String!, $number: Int!) {
1796
- organization(login: $login) {
1797
- projectV2(number: $number) {
1798
- id
1799
- number
1800
- title
1801
- url
1759
+ }`, { projectId }, this.cwd);
1760
+ const field = fieldData.node?.field;
1761
+ if (!field) {
1762
+ throw new Error(`Status field not found on project ${projectId}. Run \`night-watch board setup\` to create it.`);
1763
+ }
1764
+ return { fieldId: field.id, optionIds: new Map(field.options.map((o) => [o.name, o.id])) };
1765
+ }
1766
+ async ensureStatusColumns(projectId) {
1767
+ const fieldData = await graphql(`query GetStatusField($projectId: ID!) {
1768
+ node(id: $projectId) {
1769
+ ... on ProjectV2 {
1770
+ field(name: "Status") {
1771
+ ... on ProjectV2SingleSelectField {
1772
+ id
1773
+ options { id name }
1774
+ }
1802
1775
  }
1803
1776
  }
1804
- }`, { login, number: projectNumber }, this.cwd);
1805
- if (orgData.organization?.projectV2) {
1806
- return orgData.organization.projectV2;
1777
+ }
1778
+ }`, { projectId }, this.cwd);
1779
+ const field = fieldData.node?.field;
1780
+ if (!field)
1781
+ return;
1782
+ const existing = new Set(field.options.map((o) => o.name));
1783
+ const required = ["Draft", "Ready", "In Progress", "Review", "Done"];
1784
+ if (required.every((n) => existing.has(n)))
1785
+ return;
1786
+ await graphql(`mutation UpdateField($fieldId: ID!) {
1787
+ updateProjectV2Field(input: {
1788
+ fieldId: $fieldId
1789
+ singleSelectOptions: [
1790
+ { name: "Draft", color: GRAY, description: "" }
1791
+ { name: "Ready", color: BLUE, description: "" }
1792
+ { name: "In Progress", color: YELLOW, description: "" }
1793
+ { name: "Review", color: ORANGE, description: "" }
1794
+ { name: "Done", color: GREEN, description: "" }
1795
+ ]
1796
+ }) {
1797
+ projectV2Field {
1798
+ ... on ProjectV2SingleSelectField { id options { id name } }
1807
1799
  }
1808
- } catch {
1809
1800
  }
1810
- return null;
1801
+ }`, { fieldId: field.id }, this.cwd);
1811
1802
  }
1812
- /**
1813
- * Parse a raw project item node into IBoardIssue, returning null for items
1814
- * that are not issues.
1815
- */
1816
- parseItem(item) {
1817
- const content = item.content;
1818
- if (!content || content.number === void 0) {
1819
- return null;
1820
- }
1821
- let column = null;
1822
- for (const fv of item.fieldValues.nodes) {
1823
- if (fv.field?.name === "Status" && fv.name) {
1824
- const candidate = fv.name;
1825
- if (BOARD_COLUMNS.includes(candidate)) {
1826
- column = candidate;
1827
- }
1803
+ async linkProjectToRepository(projectId) {
1804
+ const repositoryId = await this.getRepositoryNodeId();
1805
+ try {
1806
+ await graphql(`mutation LinkProjectToRepository($projectId: ID!, $repositoryId: ID!) {
1807
+ linkProjectV2ToRepository(input: { projectId: $projectId, repositoryId: $repositoryId }) {
1808
+ repository { id }
1828
1809
  }
1810
+ }`, { projectId, repositoryId }, this.cwd);
1811
+ } catch (err) {
1812
+ const message = err instanceof Error ? err.message : String(err);
1813
+ if (message.toLowerCase().includes("already") && message.toLowerCase().includes("project"))
1814
+ return;
1815
+ throw err;
1829
1816
  }
1830
- return {
1831
- id: content.id ?? item.id,
1832
- number: content.number,
1833
- title: content.title ?? "",
1834
- body: content.body ?? "",
1835
- url: content.url ?? "",
1836
- column,
1837
- labels: content.labels?.nodes.map((l) => l.name) ?? [],
1838
- assignees: content.assignees?.nodes.map((a) => a.login) ?? []
1839
- };
1840
1817
  }
1841
- /**
1842
- * Fetch ALL items from a GitHub ProjectV2 using cursor-based pagination.
1843
- *
1844
- * The API caps each page at 100 items. We loop until `hasNextPage` is false,
1845
- * accumulating every item node so callers never see a truncated board.
1846
- */
1847
- async fetchAllProjectItems(projectId) {
1818
+ // -------------------------------------------------------------------------
1819
+ // Project items
1820
+ // -------------------------------------------------------------------------
1821
+ async paginateProjectItems(query, projectId) {
1848
1822
  const allNodes = [];
1849
1823
  let cursor = null;
1850
- const query = `query GetProjectItems($projectId: ID!, $cursor: String) {
1851
- node(id: $projectId) {
1852
- ... on ProjectV2 {
1853
- items(first: 100, after: $cursor) {
1854
- pageInfo {
1855
- hasNextPage
1856
- endCursor
1857
- }
1858
- nodes {
1859
- id
1860
- content {
1861
- ... on Issue {
1862
- number
1863
- title
1864
- body
1865
- url
1866
- id
1867
- labels(first: 10) { nodes { name } }
1868
- assignees(first: 10) { nodes { login } }
1869
- }
1870
- }
1871
- fieldValues(first: 10) {
1872
- nodes {
1873
- ... on ProjectV2ItemFieldSingleSelectValue {
1874
- name
1875
- field {
1876
- ... on ProjectV2SingleSelectField {
1877
- name
1878
- }
1879
- }
1880
- }
1881
- }
1882
- }
1883
- }
1884
- }
1885
- }
1886
- }
1887
- }`;
1888
1824
  do {
1889
1825
  const variables = { projectId };
1890
- if (cursor !== null) {
1826
+ if (cursor !== null)
1891
1827
  variables.cursor = cursor;
1892
- }
1893
1828
  const data = await graphql(query, variables, this.cwd);
1894
1829
  const page = data.node.items;
1895
1830
  allNodes.push(...page.nodes);
@@ -1897,37 +1832,27 @@ var init_github_projects = __esm({
1897
1832
  } while (cursor !== null);
1898
1833
  return allNodes;
1899
1834
  }
1900
- /**
1901
- * Fetch project items for moveIssue — only needs id, content.number, and
1902
- * fieldValues. Uses the same paginated approach to ensure items beyond
1903
- * position 100 are reachable.
1904
- */
1905
- async fetchAllProjectItemsForMove(projectId) {
1906
- const allNodes = [];
1907
- let cursor = null;
1908
- const query = `query GetProjectItemsForMove($projectId: ID!, $cursor: String) {
1909
- node(id: $projectId) {
1910
- ... on ProjectV2 {
1911
- items(first: 100, after: $cursor) {
1912
- pageInfo {
1913
- hasNextPage
1914
- endCursor
1915
- }
1916
- nodes {
1917
- id
1918
- content {
1919
- ... on Issue {
1920
- number
1835
+ async fetchAllProjectItems(projectId) {
1836
+ return this.paginateProjectItems(`query GetProjectItems($projectId: ID!, $cursor: String) {
1837
+ node(id: $projectId) {
1838
+ ... on ProjectV2 {
1839
+ items(first: 100, after: $cursor) {
1840
+ pageInfo { hasNextPage endCursor }
1841
+ nodes {
1842
+ id
1843
+ content {
1844
+ ... on Issue {
1845
+ number title body url id
1846
+ repository { nameWithOwner }
1847
+ labels(first: 10) { nodes { name } }
1848
+ assignees(first: 10) { nodes { login } }
1849
+ }
1921
1850
  }
1922
- }
1923
- fieldValues(first: 10) {
1924
- nodes {
1925
- ... on ProjectV2ItemFieldSingleSelectValue {
1926
- name
1927
- field {
1928
- ... on ProjectV2SingleSelectField {
1929
- name
1930
- }
1851
+ fieldValues(first: 10) {
1852
+ nodes {
1853
+ ... on ProjectV2ItemFieldSingleSelectValue {
1854
+ name
1855
+ field { ... on ProjectV2SingleSelectField { name } }
1931
1856
  }
1932
1857
  }
1933
1858
  }
@@ -1935,44 +1860,61 @@ var init_github_projects = __esm({
1935
1860
  }
1936
1861
  }
1937
1862
  }
1863
+ }`, projectId);
1938
1864
  }
1939
- }`;
1940
- do {
1941
- const variables = { projectId };
1942
- if (cursor !== null) {
1943
- variables.cursor = cursor;
1865
+ parseItem(item, repo) {
1866
+ const content = item.content;
1867
+ if (!content || content.number === void 0)
1868
+ return null;
1869
+ if (!this.isCurrentRepoItem(content, repo))
1870
+ return null;
1871
+ let column = null;
1872
+ for (const fv of item.fieldValues.nodes) {
1873
+ if (fv.field?.name === "Status" && fv.name) {
1874
+ const candidate = fv.name;
1875
+ if (BOARD_COLUMNS.includes(candidate))
1876
+ column = candidate;
1944
1877
  }
1945
- const data = await graphql(query, variables, this.cwd);
1946
- const page = data.node.items;
1947
- allNodes.push(...page.nodes);
1948
- cursor = page.pageInfo.hasNextPage ? page.pageInfo.endCursor : null;
1949
- } while (cursor !== null);
1950
- return allNodes;
1878
+ }
1879
+ return {
1880
+ id: content.id ?? item.id,
1881
+ number: content.number,
1882
+ title: content.title ?? "",
1883
+ body: content.body ?? "",
1884
+ url: content.url ?? "",
1885
+ column,
1886
+ labels: content.labels?.nodes.map((l) => l.name) ?? [],
1887
+ assignees: content.assignees?.nodes.map((a) => a.login) ?? []
1888
+ };
1889
+ }
1890
+ async setItemStatus(projectId, itemId, fieldId, optionId) {
1891
+ await graphql(`mutation UpdateItemField($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
1892
+ updateProjectV2ItemFieldValue(input: {
1893
+ projectId: $projectId
1894
+ itemId: $itemId
1895
+ fieldId: $fieldId
1896
+ value: { singleSelectOptionId: $optionId }
1897
+ }) {
1898
+ projectV2Item { id }
1899
+ }
1900
+ }`, { projectId, itemId, fieldId, optionId }, this.cwd);
1951
1901
  }
1952
1902
  // -------------------------------------------------------------------------
1953
- // IBoardProvider implementation
1903
+ // Project listing
1954
1904
  // -------------------------------------------------------------------------
1955
- /**
1956
- * Find an existing project by title among the repository owner's first 50 projects.
1957
- * Returns null if not found.
1958
- */
1959
1905
  async findExistingProject(owner, title) {
1960
1906
  try {
1961
1907
  if (owner.type === "User") {
1962
1908
  const data2 = await graphql(`query ListUserProjects($login: String!) {
1963
1909
  user(login: $login) {
1964
- projectsV2(first: 50) {
1965
- nodes { id number title url }
1966
- }
1910
+ projectsV2(first: 50) { nodes { id number title url } }
1967
1911
  }
1968
1912
  }`, { login: owner.login }, this.cwd);
1969
1913
  return data2.user?.projectsV2.nodes.find((p) => p.title === title) ?? null;
1970
1914
  }
1971
1915
  const data = await graphql(`query ListOrgProjects($login: String!) {
1972
1916
  organization(login: $login) {
1973
- projectsV2(first: 50) {
1974
- nodes { id number title url }
1975
- }
1917
+ projectsV2(first: 50) { nodes { id number title url } }
1976
1918
  }
1977
1919
  }`, { login: owner.login }, this.cwd);
1978
1920
  return data.organization?.projectsV2.nodes.find((p) => p.title === title) ?? null;
@@ -1980,63 +1922,22 @@ var init_github_projects = __esm({
1980
1922
  return null;
1981
1923
  }
1982
1924
  }
1983
- /**
1984
- * Ensure the Status field on an existing project has all five Night Watch
1985
- * lifecycle columns, updating it via GraphQL if any are missing.
1986
- */
1987
- async ensureStatusColumns(projectId) {
1988
- const fieldData = await graphql(`query GetStatusField($projectId: ID!) {
1989
- node(id: $projectId) {
1990
- ... on ProjectV2 {
1991
- field(name: "Status") {
1992
- ... on ProjectV2SingleSelectField {
1993
- id
1994
- options { id name }
1995
- }
1996
- }
1997
- }
1998
- }
1999
- }`, { projectId }, this.cwd);
2000
- const field = fieldData.node?.field;
2001
- if (!field)
2002
- return;
2003
- const existing = new Set(field.options.map((o) => o.name));
2004
- const required = ["Draft", "Ready", "In Progress", "Review", "Done"];
2005
- const missing = required.filter((n) => !existing.has(n));
2006
- if (missing.length === 0)
2007
- return;
2008
- const colorMap = {
2009
- Draft: "GRAY",
2010
- Ready: "BLUE",
2011
- "In Progress": "YELLOW",
2012
- Review: "ORANGE",
2013
- Done: "GREEN"
2014
- };
2015
- const allOptions = required.map((name) => ({
2016
- name,
2017
- color: colorMap[name],
2018
- description: ""
2019
- }));
2020
- await graphql(`mutation UpdateField($fieldId: ID!) {
2021
- updateProjectV2Field(input: {
2022
- fieldId: $fieldId,
2023
- singleSelectOptions: [
2024
- { name: "Draft", color: GRAY, description: "" },
2025
- { name: "Ready", color: BLUE, description: "" },
2026
- { name: "In Progress", color: YELLOW, description: "" },
2027
- { name: "Review", color: ORANGE, description: "" },
2028
- { name: "Done", color: GREEN, description: "" }
2029
- ]
2030
- }) {
2031
- projectV2Field {
2032
- ... on ProjectV2SingleSelectField {
2033
- id
2034
- options { id name }
2035
- }
2036
- }
2037
- }
2038
- }`, { fieldId: field.id, allOptions }, this.cwd);
2039
- }
1925
+ };
1926
+ }
1927
+ });
1928
+
1929
+ // ../core/dist/board/providers/github-projects.js
1930
+ import { execFile as execFile2 } from "child_process";
1931
+ import { promisify as promisify2 } from "util";
1932
+ var execFileAsync2, GitHubProjectsProvider;
1933
+ var init_github_projects = __esm({
1934
+ "../core/dist/board/providers/github-projects.js"() {
1935
+ "use strict";
1936
+ init_types2();
1937
+ init_github_graphql();
1938
+ init_github_projects_base();
1939
+ execFileAsync2 = promisify2(execFile2);
1940
+ GitHubProjectsProvider = class extends GitHubProjectsBase {
2040
1941
  async setupBoard(title) {
2041
1942
  const owner = await this.getRepoOwner();
2042
1943
  const existing = await this.findExistingProject(owner, title);
@@ -2048,12 +1949,7 @@ var init_github_projects = __esm({
2048
1949
  }
2049
1950
  const createData = await graphql(`mutation CreateProject($ownerId: ID!, $title: String!) {
2050
1951
  createProjectV2(input: { ownerId: $ownerId, title: $title }) {
2051
- projectV2 {
2052
- id
2053
- number
2054
- url
2055
- title
2056
- }
1952
+ projectV2 { id number url title }
2057
1953
  }
2058
1954
  }`, { ownerId: owner.id, title }, this.cwd);
2059
1955
  const project = createData.createProjectV2.projectV2;
@@ -2069,27 +1965,23 @@ var init_github_projects = __esm({
2069
1965
  this.cachedOptionIds = refreshed.optionIds;
2070
1966
  } catch (err) {
2071
1967
  const message = err instanceof Error ? err.message : String(err);
2072
- if (!message.includes("Status field not found")) {
1968
+ if (!message.includes("Status field not found"))
2073
1969
  throw err;
2074
- }
2075
1970
  const createFieldData = await graphql(`mutation CreateStatusField($projectId: ID!) {
2076
1971
  createProjectV2Field(input: {
2077
- projectId: $projectId,
2078
- dataType: SINGLE_SELECT,
2079
- name: "Status",
1972
+ projectId: $projectId
1973
+ dataType: SINGLE_SELECT
1974
+ name: "Status"
2080
1975
  singleSelectOptions: [
2081
- { name: "Draft", color: GRAY, description: "" },
2082
- { name: "Ready", color: BLUE, description: "" },
2083
- { name: "In Progress", color: YELLOW, description: "" },
2084
- { name: "Review", color: ORANGE, description: "" },
1976
+ { name: "Draft", color: GRAY, description: "" }
1977
+ { name: "Ready", color: BLUE, description: "" }
1978
+ { name: "In Progress", color: YELLOW, description: "" }
1979
+ { name: "Review", color: ORANGE, description: "" }
2085
1980
  { name: "Done", color: GREEN, description: "" }
2086
1981
  ]
2087
1982
  }) {
2088
1983
  projectV2Field {
2089
- ... on ProjectV2SingleSelectField {
2090
- id
2091
- options { id name }
2092
- }
1984
+ ... on ProjectV2SingleSelectField { id options { id name } }
2093
1985
  }
2094
1986
  }
2095
1987
  }`, { projectId: project.id }, this.cwd);
@@ -2101,25 +1993,12 @@ var init_github_projects = __esm({
2101
1993
  }
2102
1994
  async getBoard() {
2103
1995
  const projectNumber = this.config.projectNumber;
2104
- if (!projectNumber) {
1996
+ if (!projectNumber)
2105
1997
  return null;
2106
- }
2107
1998
  try {
2108
- const ownerLogins = /* @__PURE__ */ new Set([await this.getRepoOwnerLogin()]);
2109
- try {
2110
- ownerLogins.add(await getViewerLogin(this.cwd));
2111
- } catch {
2112
- }
2113
- let node = null;
2114
- for (const login of ownerLogins) {
2115
- node = await this.fetchProjectNode(login, projectNumber);
2116
- if (node) {
2117
- break;
2118
- }
2119
- }
2120
- if (!node) {
1999
+ const node = await this.resolveProjectNode(projectNumber);
2000
+ if (!node)
2121
2001
  return null;
2122
- }
2123
2002
  return { id: node.id, number: node.number, title: node.title, url: node.url };
2124
2003
  } catch {
2125
2004
  return null;
@@ -2127,73 +2006,36 @@ var init_github_projects = __esm({
2127
2006
  }
2128
2007
  async getColumns() {
2129
2008
  const { fieldId, optionIds } = await this.ensureProjectCache();
2130
- return BOARD_COLUMNS.map((name) => ({
2131
- id: optionIds.get(name) ?? fieldId,
2132
- name
2133
- }));
2009
+ return BOARD_COLUMNS.map((name) => ({ id: optionIds.get(name) ?? fieldId, name }));
2134
2010
  }
2135
2011
  async createIssue(input) {
2136
2012
  const repo = await this.getRepo();
2137
2013
  const { projectId, fieldId, optionIds } = await this.ensureProjectCache();
2138
- const issueArgs = [
2139
- "issue",
2140
- "create",
2141
- "--title",
2142
- input.title,
2143
- "--body",
2144
- input.body,
2145
- "--repo",
2146
- repo
2147
- ];
2014
+ const issueArgs = ["issue", "create", "--title", input.title, "--body", input.body, "--repo", repo];
2148
2015
  if (input.labels && input.labels.length > 0) {
2149
2016
  issueArgs.push("--label", input.labels.join(","));
2150
2017
  }
2151
- const { stdout: issueUrlRaw } = await execFileAsync2("gh", issueArgs, {
2152
- cwd: this.cwd,
2153
- encoding: "utf-8"
2154
- });
2018
+ const { stdout: issueUrlRaw } = await execFileAsync2("gh", issueArgs, { cwd: this.cwd, encoding: "utf-8" });
2155
2019
  const issueUrl = issueUrlRaw.trim();
2156
2020
  const issueNumber = parseInt(issueUrl.split("/").pop() ?? "", 10);
2157
- if (!issueNumber) {
2021
+ if (!issueNumber)
2158
2022
  throw new Error(`Failed to parse issue number from URL: ${issueUrl}`);
2159
- }
2160
2023
  const [owner, repoName] = repo.split("/");
2161
2024
  const { stdout: nodeIdRaw } = await execFileAsync2("gh", ["api", `repos/${owner}/${repoName}/issues/${issueNumber}`, "--jq", ".node_id"], { cwd: this.cwd, encoding: "utf-8" });
2162
- const nodeIdOutput = nodeIdRaw.trim();
2163
- const issueJson = { number: issueNumber, id: nodeIdOutput, url: issueUrl };
2025
+ const issueJson = { number: issueNumber, id: nodeIdRaw.trim(), url: issueUrl };
2164
2026
  const addData = await graphql(`mutation AddProjectItem($projectId: ID!, $contentId: ID!) {
2165
2027
  addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
2166
- item {
2167
- id
2168
- }
2028
+ item { id }
2169
2029
  }
2170
2030
  }`, { projectId, contentId: issueJson.id }, this.cwd);
2171
2031
  const itemId = addData.addProjectV2ItemById.item.id;
2172
2032
  const targetColumn = input.column ?? "Draft";
2173
2033
  const optionId = optionIds.get(targetColumn);
2174
- if (optionId) {
2175
- await graphql(`mutation UpdateItemField(
2176
- $projectId: ID!,
2177
- $itemId: ID!,
2178
- $fieldId: ID!,
2179
- $optionId: String!
2180
- ) {
2181
- updateProjectV2ItemFieldValue(input: {
2182
- projectId: $projectId,
2183
- itemId: $itemId,
2184
- fieldId: $fieldId,
2185
- value: { singleSelectOptionId: $optionId }
2186
- }) {
2187
- projectV2Item {
2188
- id
2189
- }
2190
- }
2191
- }`, { projectId, itemId, fieldId, optionId }, this.cwd);
2192
- }
2034
+ if (optionId)
2035
+ await this.setItemStatus(projectId, itemId, fieldId, optionId);
2193
2036
  const fullIssue = await this.getIssue(issueJson.number);
2194
- if (fullIssue) {
2037
+ if (fullIssue)
2195
2038
  return { ...fullIssue, column: targetColumn };
2196
- }
2197
2039
  return {
2198
2040
  id: issueJson.id,
2199
2041
  number: issueJson.number,
@@ -2209,26 +2051,16 @@ var init_github_projects = __esm({
2209
2051
  const repo = await this.getRepo();
2210
2052
  let rawIssue;
2211
2053
  try {
2212
- const { stdout: output } = await execFileAsync2("gh", [
2213
- "issue",
2214
- "view",
2215
- String(issueNumber),
2216
- "--repo",
2217
- repo,
2218
- "--json",
2219
- "number,title,body,url,id,labels,assignees"
2220
- ], { cwd: this.cwd, encoding: "utf-8" });
2054
+ const { stdout: output } = await execFileAsync2("gh", ["issue", "view", String(issueNumber), "--repo", repo, "--json", "number,title,body,url,id,labels,assignees"], { cwd: this.cwd, encoding: "utf-8" });
2221
2055
  rawIssue = JSON.parse(output);
2222
2056
  } catch {
2223
2057
  return null;
2224
2058
  }
2225
2059
  let column = null;
2226
2060
  try {
2227
- const allIssues = await this.getAllIssues();
2228
- const match = allIssues.find((i) => i.number === issueNumber);
2229
- if (match) {
2061
+ const match = (await this.getAllIssues()).find((i) => i.number === issueNumber);
2062
+ if (match)
2230
2063
  column = match.column;
2231
- }
2232
2064
  } catch {
2233
2065
  }
2234
2066
  return {
@@ -2243,53 +2075,36 @@ var init_github_projects = __esm({
2243
2075
  };
2244
2076
  }
2245
2077
  async getIssuesByColumn(column) {
2246
- const all = await this.getAllIssues();
2247
- return all.filter((issue) => issue.column === column);
2078
+ return (await this.getAllIssues()).filter((issue) => issue.column === column);
2248
2079
  }
2249
2080
  async getAllIssues() {
2081
+ const repo = await this.getRepo();
2250
2082
  const { projectId } = await this.ensureProjectCache();
2251
- const allNodes = await this.fetchAllProjectItems(projectId);
2252
2083
  const results = [];
2253
- for (const item of allNodes) {
2254
- const parsed = this.parseItem(item);
2255
- if (parsed) {
2084
+ for (const item of await this.fetchAllProjectItems(projectId)) {
2085
+ const parsed = this.parseItem(item, repo);
2086
+ if (parsed)
2256
2087
  results.push(parsed);
2257
- }
2258
2088
  }
2259
2089
  return results;
2260
2090
  }
2261
2091
  async moveIssue(issueNumber, targetColumn) {
2092
+ const repo = await this.getRepo();
2262
2093
  const { projectId, fieldId, optionIds } = await this.ensureProjectCache();
2263
- const allNodes = await this.fetchAllProjectItemsForMove(projectId);
2264
- const itemNode = allNodes.find((n) => n.content?.number === issueNumber);
2265
- if (!itemNode) {
2094
+ const itemNode = (await this.fetchAllProjectItems(projectId)).find((n) => n.content?.number === issueNumber && this.isCurrentRepoItem(n.content, repo));
2095
+ if (!itemNode)
2266
2096
  throw new Error(`Issue #${issueNumber} not found on the project board.`);
2267
- }
2268
2097
  const optionId = optionIds.get(targetColumn);
2269
- if (!optionId) {
2098
+ if (!optionId)
2270
2099
  throw new Error(`Column "${targetColumn}" not found on the project board.`);
2271
- }
2272
- await graphql(`mutation UpdateItemField(
2273
- $projectId: ID!,
2274
- $itemId: ID!,
2275
- $fieldId: ID!,
2276
- $optionId: String!
2277
- ) {
2278
- updateProjectV2ItemFieldValue(input: {
2279
- projectId: $projectId,
2280
- itemId: $itemId,
2281
- fieldId: $fieldId,
2282
- value: { singleSelectOptionId: $optionId }
2283
- }) {
2284
- projectV2Item {
2285
- id
2286
- }
2287
- }
2288
- }`, { projectId, itemId: itemNode.id, fieldId, optionId }, this.cwd);
2100
+ await this.setItemStatus(projectId, itemNode.id, fieldId, optionId);
2289
2101
  }
2290
2102
  async closeIssue(issueNumber) {
2291
2103
  const repo = await this.getRepo();
2292
- await execFileAsync2("gh", ["issue", "close", String(issueNumber), "--repo", repo], { cwd: this.cwd, encoding: "utf-8" });
2104
+ await execFileAsync2("gh", ["issue", "close", String(issueNumber), "--repo", repo], {
2105
+ cwd: this.cwd,
2106
+ encoding: "utf-8"
2107
+ });
2293
2108
  }
2294
2109
  async commentOnIssue(issueNumber, body) {
2295
2110
  const repo = await this.getRepo();
@@ -2889,123 +2704,6 @@ var init_json_state_migrator = __esm({
2889
2704
  }
2890
2705
  });
2891
2706
 
2892
- // ../core/dist/utils/avatar-generator.js
2893
- function extractOutputUrl(output) {
2894
- if (!output)
2895
- return null;
2896
- return Array.isArray(output) ? output[0] ?? null : output;
2897
- }
2898
- function buildAvatarPrompt(personaName, role) {
2899
- const nameKey = personaName.toLowerCase();
2900
- const personaDescription = PERSONA_PORTRAITS[nameKey];
2901
- if (personaDescription) {
2902
- return `Professional headshot portrait photo of a ${personaDescription}, photorealistic, clean soft neutral background, natural diffused window lighting, shot at f/2.8, shallow depth of field, looking directly at camera, candid professional headshot style, no retouching artifacts, natural skin texture`;
2903
- }
2904
- const lower = role.toLowerCase();
2905
- let descriptor;
2906
- if (lower.includes("security")) {
2907
- descriptor = "sharp-eyed cybersecurity professional with a serious and analytical expression, wearing a dark blazer";
2908
- } else if (lower.includes("architect") || lower.includes("tech lead") || lower.includes("lead")) {
2909
- descriptor = "confident senior software architect with a composed and thoughtful expression, business casual attire";
2910
- } else if (lower.includes("qa") || lower.includes("quality")) {
2911
- descriptor = "meticulous quality engineer with a focused and detail-oriented expression, smart casual attire";
2912
- } else if (lower.includes("implement") || lower.includes("developer") || lower.includes("engineer")) {
2913
- descriptor = "software developer with a creative and approachable expression, casual tech attire";
2914
- } else if (lower.includes("product") || lower.includes("manager")) {
2915
- descriptor = "product manager with a strategic and empathetic expression, business professional attire";
2916
- } else if (lower.includes("design")) {
2917
- descriptor = "UX/UI designer with a creative and innovative expression, modern stylish attire";
2918
- } else {
2919
- descriptor = "professional software team member with a friendly and competent expression, smart casual attire";
2920
- }
2921
- return `Professional headshot portrait photo of a ${descriptor}, photorealistic, clean soft neutral background, natural diffused window lighting, shot at f/2.8, shallow depth of field, looking directly at camera, candid professional headshot style, no retouching artifacts, natural skin texture`;
2922
- }
2923
- function sleep(ms) {
2924
- return new Promise((resolve10) => setTimeout(resolve10, ms));
2925
- }
2926
- async function generatePersonaAvatar(personaName, personaRole, apiToken) {
2927
- const prompt2 = buildAvatarPrompt(personaName, personaRole);
2928
- console.log(`[avatar] Generating avatar for ${personaName} (${personaRole})`);
2929
- const createRes = await fetch(`https://api.replicate.com/v1/models/${REPLICATE_MODEL}/predictions`, {
2930
- method: "POST",
2931
- headers: {
2932
- Authorization: `Bearer ${apiToken}`,
2933
- "Content-Type": "application/json",
2934
- Prefer: "wait"
2935
- },
2936
- body: JSON.stringify({
2937
- input: {
2938
- prompt: prompt2,
2939
- aspect_ratio: "1:1",
2940
- output_format: "webp",
2941
- output_quality: 85
2942
- }
2943
- })
2944
- });
2945
- if (!createRes.ok) {
2946
- const body = await createRes.text();
2947
- throw new Error(`Replicate create failed (${createRes.status}): ${body}`);
2948
- }
2949
- let prediction = await createRes.json();
2950
- if (prediction.status === "succeeded") {
2951
- const url = extractOutputUrl(prediction.output);
2952
- console.log(`[avatar] Generated for ${personaName}: ${url}`);
2953
- return url;
2954
- }
2955
- for (let i = 0; i < MAX_POLLS; i++) {
2956
- await sleep(POLL_INTERVAL_MS);
2957
- const pollRes = await fetch(`https://api.replicate.com/v1/predictions/${prediction.id}`, { headers: { Authorization: `Bearer ${apiToken}` } });
2958
- if (!pollRes.ok)
2959
- continue;
2960
- prediction = await pollRes.json();
2961
- if (prediction.status === "succeeded") {
2962
- const url = extractOutputUrl(prediction.output);
2963
- console.log(`[avatar] Generated for ${personaName}: ${url}`);
2964
- return url;
2965
- }
2966
- if (prediction.status === "failed" || prediction.status === "canceled") {
2967
- throw new Error(`Replicate prediction ${prediction.status}: ${prediction.error ?? "unknown error"}`);
2968
- }
2969
- console.log(`[avatar] Polling\u2026 status=${prediction.status} (${i + 1}/${MAX_POLLS})`);
2970
- }
2971
- throw new Error("Replicate avatar generation timed out after 3 minutes");
2972
- }
2973
- var REPLICATE_MODEL, POLL_INTERVAL_MS, MAX_POLLS, PERSONA_PORTRAITS;
2974
- var init_avatar_generator = __esm({
2975
- "../core/dist/utils/avatar-generator.js"() {
2976
- "use strict";
2977
- REPLICATE_MODEL = "black-forest-labs/flux-1.1-pro";
2978
- POLL_INTERVAL_MS = 3e3;
2979
- MAX_POLLS = 60;
2980
- PERSONA_PORTRAITS = {
2981
- maya: [
2982
- "South Asian woman in her late 20s with sharp dark eyes and straight black hair pulled back in a low ponytail",
2983
- "wearing a dark charcoal blazer over a plain black turtleneck",
2984
- "expression is focused and perceptive \u2014 the look of someone who notices details others miss",
2985
- "minimal jewelry, small silver stud earrings"
2986
- ].join(", "),
2987
- carlos: [
2988
- "Hispanic man in his mid-30s with short dark wavy hair and a neatly trimmed beard",
2989
- "wearing a navy blue henley shirt with sleeves slightly pushed up",
2990
- "expression is calm and confident \u2014 easy authority, like someone used to making decisions",
2991
- "slight smile that reads more thoughtful than warm"
2992
- ].join(", "),
2993
- priya: [
2994
- "Indian woman in her early 30s with shoulder-length dark brown hair with subtle highlights",
2995
- "wearing a soft olive green cardigan over a white crew-neck t-shirt",
2996
- "expression is alert and curious \u2014 bright eyes, slight head tilt, like she just thought of something interesting",
2997
- "round tortoiseshell glasses"
2998
- ].join(", "),
2999
- dev: [
3000
- "East Asian man in his late 20s with short textured black hair styled casually",
3001
- "wearing a heather gray crewneck sweatshirt",
3002
- "expression is friendly and approachable \u2014 relaxed confidence, like someone in the middle of good work",
3003
- "clean-shaven, natural look"
3004
- ].join(", ")
3005
- };
3006
- }
3007
- });
3008
-
3009
2707
  // ../core/dist/utils/logger.js
3010
2708
  import fs4 from "fs";
3011
2709
  import os2 from "os";
@@ -3761,7 +3459,7 @@ function getLockFilePaths(projectDir) {
3761
3459
  reviewer: `${LOCK_FILE_PREFIX}pr-reviewer-${runtimeKey}.lock`
3762
3460
  };
3763
3461
  }
3764
- function sleep2(ms) {
3462
+ function sleep(ms) {
3765
3463
  return new Promise((resolve10) => setTimeout(resolve10, ms));
3766
3464
  }
3767
3465
  async function cancelProcess(processType, lockPath, force = false) {
@@ -3803,7 +3501,7 @@ async function cancelProcess(processType, lockPath, force = false) {
3803
3501
  message: `Failed to send SIGTERM to ${processType} (PID ${pid}): ${errorMessage}`
3804
3502
  };
3805
3503
  }
3806
- await sleep2(3e3);
3504
+ await sleep(3e3);
3807
3505
  if (!isProcessRunning(pid)) {
3808
3506
  try {
3809
3507
  fs7.unlinkSync(lockPath);
@@ -3823,7 +3521,7 @@ async function cancelProcess(processType, lockPath, force = false) {
3823
3521
  message: `Failed to send SIGKILL to ${processType} (PID ${pid}): ${errorMessage}`
3824
3522
  };
3825
3523
  }
3826
- await sleep2(500);
3524
+ await sleep(500);
3827
3525
  if (!isProcessRunning(pid)) {
3828
3526
  try {
3829
3527
  fs7.unlinkSync(lockPath);
@@ -7032,7 +6730,6 @@ __export(dist_exports, {
7032
6730
  formatTelegramPayload: () => formatTelegramPayload,
7033
6731
  generateItemHash: () => generateItemHash,
7034
6732
  generateMarker: () => generateMarker,
7035
- generatePersonaAvatar: () => generatePersonaAvatar,
7036
6733
  getBranchTipTimestamp: () => getBranchTipTimestamp,
7037
6734
  getCrontabInfo: () => getCrontabInfo,
7038
6735
  getDb: () => getDb,
@@ -7132,7 +6829,7 @@ __export(dist_exports, {
7132
6829
  scanRoadmap: () => scanRoadmap,
7133
6830
  sendNotifications: () => sendNotifications,
7134
6831
  sendWebhook: () => sendWebhook,
7135
- sleep: () => sleep2,
6832
+ sleep: () => sleep,
7136
6833
  sliceNextItem: () => sliceNextItem,
7137
6834
  sliceRoadmapItem: () => sliceRoadmapItem,
7138
6835
  slugify: () => slugify,
@@ -7166,7 +6863,6 @@ var init_dist = __esm({
7166
6863
  init_migrations();
7167
6864
  init_json_state_migrator();
7168
6865
  init_container();
7169
- init_avatar_generator();
7170
6866
  init_logger();
7171
6867
  init_cancel();
7172
6868
  init_checks();
@@ -14362,7 +14058,7 @@ async function promptConfirmation(prompt2) {
14362
14058
  });
14363
14059
  });
14364
14060
  }
14365
- function sleep3(ms) {
14061
+ function sleep2(ms) {
14366
14062
  return new Promise((resolve10) => setTimeout(resolve10, ms));
14367
14063
  }
14368
14064
  function isProcessRunning3(pid) {
@@ -14419,7 +14115,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
14419
14115
  };
14420
14116
  }
14421
14117
  info(`Sent SIGTERM to ${processType} (PID ${pid}), waiting 3 seconds...`);
14422
- await sleep3(3e3);
14118
+ await sleep2(3e3);
14423
14119
  if (!isProcessRunning3(pid)) {
14424
14120
  try {
14425
14121
  fs34.unlinkSync(lockPath);
@@ -14454,7 +14150,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
14454
14150
  message: `Failed to send SIGKILL to ${processType} (PID ${pid}): ${errorMessage}`
14455
14151
  };
14456
14152
  }
14457
- await sleep3(500);
14153
+ await sleep2(500);
14458
14154
  if (!isProcessRunning3(pid)) {
14459
14155
  try {
14460
14156
  fs34.unlinkSync(lockPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonit-dev/night-watch-cli",
3
- "version": "1.7.96",
3
+ "version": "1.7.97",
4
4
  "description": "Autonomous PRD execution using AI Provider CLIs + cron",
5
5
  "type": "module",
6
6
  "bin": {