opencode-swarm-plugin 0.42.9 → 0.44.0

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 (48) hide show
  1. package/.hive/issues.jsonl +14 -0
  2. package/.turbo/turbo-build.log +2 -2
  3. package/CHANGELOG.md +110 -0
  4. package/README.md +296 -6
  5. package/bin/cass.characterization.test.ts +422 -0
  6. package/bin/swarm.test.ts +683 -0
  7. package/bin/swarm.ts +501 -0
  8. package/dist/contributor-tools.d.ts +42 -0
  9. package/dist/contributor-tools.d.ts.map +1 -0
  10. package/dist/dashboard.d.ts +83 -0
  11. package/dist/dashboard.d.ts.map +1 -0
  12. package/dist/error-enrichment.d.ts +49 -0
  13. package/dist/error-enrichment.d.ts.map +1 -0
  14. package/dist/export-tools.d.ts +76 -0
  15. package/dist/export-tools.d.ts.map +1 -0
  16. package/dist/index.d.ts +14 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +95 -2
  19. package/dist/observability-tools.d.ts +2 -2
  20. package/dist/plugin.js +95 -2
  21. package/dist/query-tools.d.ts +59 -0
  22. package/dist/query-tools.d.ts.map +1 -0
  23. package/dist/replay-tools.d.ts +28 -0
  24. package/dist/replay-tools.d.ts.map +1 -0
  25. package/dist/sessions/agent-discovery.d.ts +59 -0
  26. package/dist/sessions/agent-discovery.d.ts.map +1 -0
  27. package/dist/sessions/index.d.ts +10 -0
  28. package/dist/sessions/index.d.ts.map +1 -0
  29. package/docs/planning/ADR-010-cass-inhousing.md +1215 -0
  30. package/evals/fixtures/cass-baseline.ts +217 -0
  31. package/examples/plugin-wrapper-template.ts +89 -0
  32. package/package.json +1 -1
  33. package/src/contributor-tools.test.ts +133 -0
  34. package/src/contributor-tools.ts +201 -0
  35. package/src/dashboard.test.ts +611 -0
  36. package/src/dashboard.ts +462 -0
  37. package/src/error-enrichment.test.ts +403 -0
  38. package/src/error-enrichment.ts +219 -0
  39. package/src/export-tools.test.ts +476 -0
  40. package/src/export-tools.ts +257 -0
  41. package/src/index.ts +8 -3
  42. package/src/query-tools.test.ts +636 -0
  43. package/src/query-tools.ts +324 -0
  44. package/src/replay-tools.test.ts +496 -0
  45. package/src/replay-tools.ts +240 -0
  46. package/src/sessions/agent-discovery.test.ts +137 -0
  47. package/src/sessions/agent-discovery.ts +112 -0
  48. package/src/sessions/index.ts +15 -0
package/bin/swarm.test.ts CHANGED
@@ -1449,3 +1449,686 @@ describe("swarm history", () => {
1449
1449
  });
1450
1450
  });
1451
1451
  });
1452
+
1453
+ // ============================================================================
1454
+ // Observability Commands Tests (TDD - Phase 5)
1455
+ // ============================================================================
1456
+
1457
+ describe("swarm query", () => {
1458
+ test("executes SQL query with table format", () => {
1459
+ // Mock function - to be implemented in swarm.ts
1460
+ function executeQueryCommand(args: string[]): { format: string; query?: string; preset?: string } {
1461
+ let format = "table";
1462
+ let query: string | undefined;
1463
+ let preset: string | undefined;
1464
+
1465
+ for (let i = 0; i < args.length; i++) {
1466
+ if (args[i] === "--format") {
1467
+ format = args[i + 1] || "table";
1468
+ i++;
1469
+ } else if (args[i] === "--sql") {
1470
+ query = args[i + 1];
1471
+ i++;
1472
+ } else if (args[i] === "--preset") {
1473
+ preset = args[i + 1];
1474
+ i++;
1475
+ }
1476
+ }
1477
+
1478
+ return { format, query, preset };
1479
+ }
1480
+
1481
+ const result = executeQueryCommand(["--sql", "SELECT * FROM events", "--format", "table"]);
1482
+
1483
+ expect(result.query).toBe("SELECT * FROM events");
1484
+ expect(result.format).toBe("table");
1485
+ });
1486
+
1487
+ test("executes preset query", () => {
1488
+ function executeQueryCommand(args: string[]): { format: string; query?: string; preset?: string } {
1489
+ let format = "table";
1490
+ let query: string | undefined;
1491
+ let preset: string | undefined;
1492
+
1493
+ for (let i = 0; i < args.length; i++) {
1494
+ if (args[i] === "--format") {
1495
+ format = args[i + 1] || "table";
1496
+ i++;
1497
+ } else if (args[i] === "--sql") {
1498
+ query = args[i + 1];
1499
+ i++;
1500
+ } else if (args[i] === "--preset") {
1501
+ preset = args[i + 1];
1502
+ i++;
1503
+ }
1504
+ }
1505
+
1506
+ return { format, query, preset };
1507
+ }
1508
+
1509
+ const result = executeQueryCommand(["--preset", "failed_decompositions", "--format", "csv"]);
1510
+
1511
+ expect(result.preset).toBe("failed_decompositions");
1512
+ expect(result.format).toBe("csv");
1513
+ });
1514
+
1515
+ test("defaults to table format", () => {
1516
+ function executeQueryCommand(args: string[]): { format: string; query?: string; preset?: string } {
1517
+ let format = "table";
1518
+ let query: string | undefined;
1519
+ let preset: string | undefined;
1520
+
1521
+ for (let i = 0; i < args.length; i++) {
1522
+ if (args[i] === "--format") {
1523
+ format = args[i + 1] || "table";
1524
+ i++;
1525
+ } else if (args[i] === "--sql") {
1526
+ query = args[i + 1];
1527
+ i++;
1528
+ } else if (args[i] === "--preset") {
1529
+ preset = args[i + 1];
1530
+ i++;
1531
+ }
1532
+ }
1533
+
1534
+ return { format, query, preset };
1535
+ }
1536
+
1537
+ const result = executeQueryCommand(["--sql", "SELECT * FROM events"]);
1538
+
1539
+ expect(result.format).toBe("table");
1540
+ });
1541
+ });
1542
+
1543
+ describe("swarm dashboard", () => {
1544
+ test("parses epic filter flag", () => {
1545
+ function parseDashboardArgs(args: string[]): { epic?: string; refresh: number } {
1546
+ let epic: string | undefined;
1547
+ let refresh = 1000;
1548
+
1549
+ for (let i = 0; i < args.length; i++) {
1550
+ if (args[i] === "--epic") {
1551
+ epic = args[i + 1];
1552
+ i++;
1553
+ } else if (args[i] === "--refresh") {
1554
+ const ms = parseInt(args[i + 1], 10);
1555
+ if (!isNaN(ms) && ms > 0) {
1556
+ refresh = ms;
1557
+ }
1558
+ i++;
1559
+ }
1560
+ }
1561
+
1562
+ return { epic, refresh };
1563
+ }
1564
+
1565
+ const result = parseDashboardArgs(["--epic", "mjkw1234567"]);
1566
+
1567
+ expect(result.epic).toBe("mjkw1234567");
1568
+ expect(result.refresh).toBe(1000); // default
1569
+ });
1570
+
1571
+ test("parses refresh interval flag", () => {
1572
+ function parseDashboardArgs(args: string[]): { epic?: string; refresh: number } {
1573
+ let epic: string | undefined;
1574
+ let refresh = 1000;
1575
+
1576
+ for (let i = 0; i < args.length; i++) {
1577
+ if (args[i] === "--epic") {
1578
+ epic = args[i + 1];
1579
+ i++;
1580
+ } else if (args[i] === "--refresh") {
1581
+ const ms = parseInt(args[i + 1], 10);
1582
+ if (!isNaN(ms) && ms > 0) {
1583
+ refresh = ms;
1584
+ }
1585
+ i++;
1586
+ }
1587
+ }
1588
+
1589
+ return { epic, refresh };
1590
+ }
1591
+
1592
+ const result = parseDashboardArgs(["--refresh", "2000"]);
1593
+
1594
+ expect(result.refresh).toBe(2000);
1595
+ });
1596
+
1597
+ test("defaults to 1000ms refresh", () => {
1598
+ function parseDashboardArgs(args: string[]): { epic?: string; refresh: number } {
1599
+ let epic: string | undefined;
1600
+ let refresh = 1000;
1601
+
1602
+ for (let i = 0; i < args.length; i++) {
1603
+ if (args[i] === "--epic") {
1604
+ epic = args[i + 1];
1605
+ i++;
1606
+ } else if (args[i] === "--refresh") {
1607
+ const ms = parseInt(args[i + 1], 10);
1608
+ if (!isNaN(ms) && ms > 0) {
1609
+ refresh = ms;
1610
+ }
1611
+ i++;
1612
+ }
1613
+ }
1614
+
1615
+ return { epic, refresh };
1616
+ }
1617
+
1618
+ const result = parseDashboardArgs([]);
1619
+
1620
+ expect(result.refresh).toBe(1000);
1621
+ });
1622
+ });
1623
+
1624
+ describe("swarm replay", () => {
1625
+ test("parses speed multiplier flag", () => {
1626
+ function parseReplayArgs(args: string[]): {
1627
+ epicId?: string;
1628
+ speed: number;
1629
+ types: string[];
1630
+ agent?: string;
1631
+ since?: Date;
1632
+ until?: Date;
1633
+ } {
1634
+ let epicId: string | undefined;
1635
+ let speed = 1;
1636
+ let types: string[] = [];
1637
+ let agent: string | undefined;
1638
+ let since: Date | undefined;
1639
+ let until: Date | undefined;
1640
+
1641
+ // First positional arg is epic ID
1642
+ if (args.length > 0 && !args[0].startsWith("--")) {
1643
+ epicId = args[0];
1644
+ }
1645
+
1646
+ for (let i = 0; i < args.length; i++) {
1647
+ if (args[i] === "--speed") {
1648
+ const val = args[i + 1];
1649
+ if (val === "instant") {
1650
+ speed = Infinity;
1651
+ } else {
1652
+ const parsed = parseFloat(val?.replace("x", "") || "1");
1653
+ if (!isNaN(parsed) && parsed > 0) {
1654
+ speed = parsed;
1655
+ }
1656
+ }
1657
+ i++;
1658
+ } else if (args[i] === "--type") {
1659
+ types = args[i + 1]?.split(",").map((t) => t.trim()) || [];
1660
+ i++;
1661
+ } else if (args[i] === "--agent") {
1662
+ agent = args[i + 1];
1663
+ i++;
1664
+ } else if (args[i] === "--since") {
1665
+ const dateStr = args[i + 1];
1666
+ if (dateStr) {
1667
+ since = new Date(dateStr);
1668
+ }
1669
+ i++;
1670
+ } else if (args[i] === "--until") {
1671
+ const dateStr = args[i + 1];
1672
+ if (dateStr) {
1673
+ until = new Date(dateStr);
1674
+ }
1675
+ i++;
1676
+ }
1677
+ }
1678
+
1679
+ return { epicId, speed, types, agent, since, until };
1680
+ }
1681
+
1682
+ const result = parseReplayArgs(["mjkw1234567", "--speed", "2x"]);
1683
+
1684
+ expect(result.epicId).toBe("mjkw1234567");
1685
+ expect(result.speed).toBe(2);
1686
+ });
1687
+
1688
+ test("parses instant speed", () => {
1689
+ function parseReplayArgs(args: string[]): {
1690
+ epicId?: string;
1691
+ speed: number;
1692
+ types: string[];
1693
+ agent?: string;
1694
+ since?: Date;
1695
+ until?: Date;
1696
+ } {
1697
+ let epicId: string | undefined;
1698
+ let speed = 1;
1699
+ let types: string[] = [];
1700
+ let agent: string | undefined;
1701
+ let since: Date | undefined;
1702
+ let until: Date | undefined;
1703
+
1704
+ if (args.length > 0 && !args[0].startsWith("--")) {
1705
+ epicId = args[0];
1706
+ }
1707
+
1708
+ for (let i = 0; i < args.length; i++) {
1709
+ if (args[i] === "--speed") {
1710
+ const val = args[i + 1];
1711
+ if (val === "instant") {
1712
+ speed = Infinity;
1713
+ } else {
1714
+ const parsed = parseFloat(val?.replace("x", "") || "1");
1715
+ if (!isNaN(parsed) && parsed > 0) {
1716
+ speed = parsed;
1717
+ }
1718
+ }
1719
+ i++;
1720
+ } else if (args[i] === "--type") {
1721
+ types = args[i + 1]?.split(",").map((t) => t.trim()) || [];
1722
+ i++;
1723
+ } else if (args[i] === "--agent") {
1724
+ agent = args[i + 1];
1725
+ i++;
1726
+ } else if (args[i] === "--since") {
1727
+ const dateStr = args[i + 1];
1728
+ if (dateStr) {
1729
+ since = new Date(dateStr);
1730
+ }
1731
+ i++;
1732
+ } else if (args[i] === "--until") {
1733
+ const dateStr = args[i + 1];
1734
+ if (dateStr) {
1735
+ until = new Date(dateStr);
1736
+ }
1737
+ i++;
1738
+ }
1739
+ }
1740
+
1741
+ return { epicId, speed, types, agent, since, until };
1742
+ }
1743
+
1744
+ const result = parseReplayArgs(["mjkw1234567", "--speed", "instant"]);
1745
+
1746
+ expect(result.speed).toBe(Infinity);
1747
+ });
1748
+
1749
+ test("parses event type filters", () => {
1750
+ function parseReplayArgs(args: string[]): {
1751
+ epicId?: string;
1752
+ speed: number;
1753
+ types: string[];
1754
+ agent?: string;
1755
+ since?: Date;
1756
+ until?: Date;
1757
+ } {
1758
+ let epicId: string | undefined;
1759
+ let speed = 1;
1760
+ let types: string[] = [];
1761
+ let agent: string | undefined;
1762
+ let since: Date | undefined;
1763
+ let until: Date | undefined;
1764
+
1765
+ if (args.length > 0 && !args[0].startsWith("--")) {
1766
+ epicId = args[0];
1767
+ }
1768
+
1769
+ for (let i = 0; i < args.length; i++) {
1770
+ if (args[i] === "--speed") {
1771
+ const val = args[i + 1];
1772
+ if (val === "instant") {
1773
+ speed = Infinity;
1774
+ } else {
1775
+ const parsed = parseFloat(val?.replace("x", "") || "1");
1776
+ if (!isNaN(parsed) && parsed > 0) {
1777
+ speed = parsed;
1778
+ }
1779
+ }
1780
+ i++;
1781
+ } else if (args[i] === "--type") {
1782
+ types = args[i + 1]?.split(",").map((t) => t.trim()) || [];
1783
+ i++;
1784
+ } else if (args[i] === "--agent") {
1785
+ agent = args[i + 1];
1786
+ i++;
1787
+ } else if (args[i] === "--since") {
1788
+ const dateStr = args[i + 1];
1789
+ if (dateStr) {
1790
+ since = new Date(dateStr);
1791
+ }
1792
+ i++;
1793
+ } else if (args[i] === "--until") {
1794
+ const dateStr = args[i + 1];
1795
+ if (dateStr) {
1796
+ until = new Date(dateStr);
1797
+ }
1798
+ i++;
1799
+ }
1800
+ }
1801
+
1802
+ return { epicId, speed, types, agent, since, until };
1803
+ }
1804
+
1805
+ const result = parseReplayArgs(["--type", "DECISION,VIOLATION"]);
1806
+
1807
+ expect(result.types).toEqual(["DECISION", "VIOLATION"]);
1808
+ });
1809
+
1810
+ test("parses agent filter", () => {
1811
+ function parseReplayArgs(args: string[]): {
1812
+ epicId?: string;
1813
+ speed: number;
1814
+ types: string[];
1815
+ agent?: string;
1816
+ since?: Date;
1817
+ until?: Date;
1818
+ } {
1819
+ let epicId: string | undefined;
1820
+ let speed = 1;
1821
+ let types: string[] = [];
1822
+ let agent: string | undefined;
1823
+ let since: Date | undefined;
1824
+ let until: Date | undefined;
1825
+
1826
+ if (args.length > 0 && !args[0].startsWith("--")) {
1827
+ epicId = args[0];
1828
+ }
1829
+
1830
+ for (let i = 0; i < args.length; i++) {
1831
+ if (args[i] === "--speed") {
1832
+ const val = args[i + 1];
1833
+ if (val === "instant") {
1834
+ speed = Infinity;
1835
+ } else {
1836
+ const parsed = parseFloat(val?.replace("x", "") || "1");
1837
+ if (!isNaN(parsed) && parsed > 0) {
1838
+ speed = parsed;
1839
+ }
1840
+ }
1841
+ i++;
1842
+ } else if (args[i] === "--type") {
1843
+ types = args[i + 1]?.split(",").map((t) => t.trim()) || [];
1844
+ i++;
1845
+ } else if (args[i] === "--agent") {
1846
+ agent = args[i + 1];
1847
+ i++;
1848
+ } else if (args[i] === "--since") {
1849
+ const dateStr = args[i + 1];
1850
+ if (dateStr) {
1851
+ since = new Date(dateStr);
1852
+ }
1853
+ i++;
1854
+ } else if (args[i] === "--until") {
1855
+ const dateStr = args[i + 1];
1856
+ if (dateStr) {
1857
+ until = new Date(dateStr);
1858
+ }
1859
+ i++;
1860
+ }
1861
+ }
1862
+
1863
+ return { epicId, speed, types, agent, since, until };
1864
+ }
1865
+
1866
+ const result = parseReplayArgs(["--agent", "WildLake"]);
1867
+
1868
+ expect(result.agent).toBe("WildLake");
1869
+ });
1870
+
1871
+ test("parses time range filters", () => {
1872
+ function parseReplayArgs(args: string[]): {
1873
+ epicId?: string;
1874
+ speed: number;
1875
+ types: string[];
1876
+ agent?: string;
1877
+ since?: Date;
1878
+ until?: Date;
1879
+ } {
1880
+ let epicId: string | undefined;
1881
+ let speed = 1;
1882
+ let types: string[] = [];
1883
+ let agent: string | undefined;
1884
+ let since: Date | undefined;
1885
+ let until: Date | undefined;
1886
+
1887
+ if (args.length > 0 && !args[0].startsWith("--")) {
1888
+ epicId = args[0];
1889
+ }
1890
+
1891
+ for (let i = 0; i < args.length; i++) {
1892
+ if (args[i] === "--speed") {
1893
+ const val = args[i + 1];
1894
+ if (val === "instant") {
1895
+ speed = Infinity;
1896
+ } else {
1897
+ const parsed = parseFloat(val?.replace("x", "") || "1");
1898
+ if (!isNaN(parsed) && parsed > 0) {
1899
+ speed = parsed;
1900
+ }
1901
+ }
1902
+ i++;
1903
+ } else if (args[i] === "--type") {
1904
+ types = args[i + 1]?.split(",").map((t) => t.trim()) || [];
1905
+ i++;
1906
+ } else if (args[i] === "--agent") {
1907
+ agent = args[i + 1];
1908
+ i++;
1909
+ } else if (args[i] === "--since") {
1910
+ const dateStr = args[i + 1];
1911
+ if (dateStr) {
1912
+ since = new Date(dateStr);
1913
+ }
1914
+ i++;
1915
+ } else if (args[i] === "--until") {
1916
+ const dateStr = args[i + 1];
1917
+ if (dateStr) {
1918
+ until = new Date(dateStr);
1919
+ }
1920
+ i++;
1921
+ }
1922
+ }
1923
+
1924
+ return { epicId, speed, types, agent, since, until };
1925
+ }
1926
+
1927
+ const result = parseReplayArgs([
1928
+ "--since",
1929
+ "2025-12-01T00:00:00Z",
1930
+ "--until",
1931
+ "2025-12-31T23:59:59Z",
1932
+ ]);
1933
+
1934
+ expect(result.since).toBeInstanceOf(Date);
1935
+ expect(result.until).toBeInstanceOf(Date);
1936
+ expect(result.since!.getFullYear()).toBe(2025);
1937
+ expect(result.until!.getMonth()).toBe(11); // December is month 11
1938
+ });
1939
+ });
1940
+
1941
+ describe("swarm viz", () => {
1942
+ test("parses port flag", () => {
1943
+ function parseVizArgs(args: string[]): { port: number } {
1944
+ let port = 4483; // HIVE on phone keypad
1945
+
1946
+ for (let i = 0; i < args.length; i++) {
1947
+ if (args[i] === "--port") {
1948
+ const portNum = Number.parseInt(args[i + 1]);
1949
+ if (!isNaN(portNum) && portNum > 0) {
1950
+ port = portNum;
1951
+ }
1952
+ i++;
1953
+ }
1954
+ }
1955
+
1956
+ return { port };
1957
+ }
1958
+
1959
+ const result = parseVizArgs(["--port", "8080"]);
1960
+
1961
+ expect(result.port).toBe(8080);
1962
+ });
1963
+
1964
+ test("defaults to port 4483 (HIVE)", () => {
1965
+ function parseVizArgs(args: string[]): { port: number } {
1966
+ let port = 4483;
1967
+
1968
+ for (let i = 0; i < args.length; i++) {
1969
+ if (args[i] === "--port") {
1970
+ const portNum = Number.parseInt(args[i + 1]);
1971
+ if (!isNaN(portNum) && portNum > 0) {
1972
+ port = portNum;
1973
+ }
1974
+ i++;
1975
+ }
1976
+ }
1977
+
1978
+ return { port };
1979
+ }
1980
+
1981
+ const result = parseVizArgs([]);
1982
+
1983
+ expect(result.port).toBe(4483);
1984
+ });
1985
+
1986
+ test("ignores invalid port values", () => {
1987
+ function parseVizArgs(args: string[]): { port: number } {
1988
+ let port = 4483;
1989
+
1990
+ for (let i = 0; i < args.length; i++) {
1991
+ if (args[i] === "--port") {
1992
+ const portNum = Number.parseInt(args[i + 1]);
1993
+ if (!isNaN(portNum) && portNum > 0) {
1994
+ port = portNum;
1995
+ }
1996
+ i++;
1997
+ }
1998
+ }
1999
+
2000
+ return { port };
2001
+ }
2002
+
2003
+ const result = parseVizArgs(["--port", "invalid"]);
2004
+
2005
+ expect(result.port).toBe(4483); // Falls back to default
2006
+ });
2007
+ });
2008
+
2009
+ describe("swarm export", () => {
2010
+ test("parses format flag", () => {
2011
+ function parseExportArgs(args: string[]): {
2012
+ format: string;
2013
+ epic?: string;
2014
+ output?: string;
2015
+ } {
2016
+ let format = "json";
2017
+ let epic: string | undefined;
2018
+ let output: string | undefined;
2019
+
2020
+ for (let i = 0; i < args.length; i++) {
2021
+ if (args[i] === "--format") {
2022
+ format = args[i + 1] || "json";
2023
+ i++;
2024
+ } else if (args[i] === "--epic") {
2025
+ epic = args[i + 1];
2026
+ i++;
2027
+ } else if (args[i] === "--output") {
2028
+ output = args[i + 1];
2029
+ i++;
2030
+ }
2031
+ }
2032
+
2033
+ return { format, epic, output };
2034
+ }
2035
+
2036
+ const result = parseExportArgs(["--format", "otlp"]);
2037
+
2038
+ expect(result.format).toBe("otlp");
2039
+ });
2040
+
2041
+ test("parses epic filter", () => {
2042
+ function parseExportArgs(args: string[]): {
2043
+ format: string;
2044
+ epic?: string;
2045
+ output?: string;
2046
+ } {
2047
+ let format = "json";
2048
+ let epic: string | undefined;
2049
+ let output: string | undefined;
2050
+
2051
+ for (let i = 0; i < args.length; i++) {
2052
+ if (args[i] === "--format") {
2053
+ format = args[i + 1] || "json";
2054
+ i++;
2055
+ } else if (args[i] === "--epic") {
2056
+ epic = args[i + 1];
2057
+ i++;
2058
+ } else if (args[i] === "--output") {
2059
+ output = args[i + 1];
2060
+ i++;
2061
+ }
2062
+ }
2063
+
2064
+ return { format, epic, output };
2065
+ }
2066
+
2067
+ const result = parseExportArgs(["--epic", "mjkw1234567"]);
2068
+
2069
+ expect(result.epic).toBe("mjkw1234567");
2070
+ });
2071
+
2072
+ test("parses output file path", () => {
2073
+ function parseExportArgs(args: string[]): {
2074
+ format: string;
2075
+ epic?: string;
2076
+ output?: string;
2077
+ } {
2078
+ let format = "json";
2079
+ let epic: string | undefined;
2080
+ let output: string | undefined;
2081
+
2082
+ for (let i = 0; i < args.length; i++) {
2083
+ if (args[i] === "--format") {
2084
+ format = args[i + 1] || "json";
2085
+ i++;
2086
+ } else if (args[i] === "--epic") {
2087
+ epic = args[i + 1];
2088
+ i++;
2089
+ } else if (args[i] === "--output") {
2090
+ output = args[i + 1];
2091
+ i++;
2092
+ }
2093
+ }
2094
+
2095
+ return { format, epic, output };
2096
+ }
2097
+
2098
+ const result = parseExportArgs(["--output", "/tmp/export.json"]);
2099
+
2100
+ expect(result.output).toBe("/tmp/export.json");
2101
+ });
2102
+
2103
+ test("defaults to json format", () => {
2104
+ function parseExportArgs(args: string[]): {
2105
+ format: string;
2106
+ epic?: string;
2107
+ output?: string;
2108
+ } {
2109
+ let format = "json";
2110
+ let epic: string | undefined;
2111
+ let output: string | undefined;
2112
+
2113
+ for (let i = 0; i < args.length; i++) {
2114
+ if (args[i] === "--format") {
2115
+ format = args[i + 1] || "json";
2116
+ i++;
2117
+ } else if (args[i] === "--epic") {
2118
+ epic = args[i + 1];
2119
+ i++;
2120
+ } else if (args[i] === "--output") {
2121
+ output = args[i + 1];
2122
+ i++;
2123
+ }
2124
+ }
2125
+
2126
+ return { format, epic, output };
2127
+ }
2128
+
2129
+ const result = parseExportArgs([]);
2130
+
2131
+ expect(result.format).toBe("json");
2132
+ });
2133
+ });
2134
+