@sherwoodagent/cli 0.7.3 → 0.9.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 (30) hide show
  1. package/dist/{chat-BSLMAYM5.js → chat-BNYWD3EL.js} +5 -5
  2. package/dist/{chunk-EDR5AHSC.js → chunk-4MTHXGTK.js} +2 -2
  3. package/dist/{chunk-QMWMT6EH.js → chunk-5ADWTXNT.js} +115 -31
  4. package/dist/chunk-5ADWTXNT.js.map +1 -0
  5. package/dist/{chunk-P3GYAMGZ.js → chunk-HKZONPXW.js} +15 -5
  6. package/dist/chunk-HKZONPXW.js.map +1 -0
  7. package/dist/{chunk-6MEYSN2W.js → chunk-OUES74ID.js} +488 -47
  8. package/dist/chunk-OUES74ID.js.map +1 -0
  9. package/dist/{chunk-B7XDUFI3.js → chunk-Q37V65B6.js} +7 -7
  10. package/dist/chunk-Q37V65B6.js.map +1 -0
  11. package/dist/{eas-TMHFTX43.js → eas-EL3XCEXQ.js} +4 -4
  12. package/dist/index.js +1077 -228
  13. package/dist/index.js.map +1 -1
  14. package/dist/{research-ILKLSHVP.js → research-57SKO27M.js} +5 -5
  15. package/dist/{research-5QALNYVS.js → research-ZR7HXITG.js} +3 -3
  16. package/dist/{session-XUOMZWOT.js → session-QQSHCGNK.js} +7 -7
  17. package/dist/{session-XUOMZWOT.js.map → session-QQSHCGNK.js.map} +1 -1
  18. package/dist/{xmtp-4XTQQ7RE.js → xmtp-S4VRXMFK.js} +6 -6
  19. package/dist/xmtp-S4VRXMFK.js.map +1 -0
  20. package/package.json +1 -1
  21. package/dist/chunk-6MEYSN2W.js.map +0 -1
  22. package/dist/chunk-B7XDUFI3.js.map +0 -1
  23. package/dist/chunk-P3GYAMGZ.js.map +0 -1
  24. package/dist/chunk-QMWMT6EH.js.map +0 -1
  25. package/dist/xmtp-4XTQQ7RE.js.map +0 -1
  26. /package/dist/{chat-BSLMAYM5.js.map → chat-BNYWD3EL.js.map} +0 -0
  27. /package/dist/{chunk-EDR5AHSC.js.map → chunk-4MTHXGTK.js.map} +0 -0
  28. /package/dist/{eas-TMHFTX43.js.map → eas-EL3XCEXQ.js.map} +0 -0
  29. /package/dist/{research-ILKLSHVP.js.map → research-57SKO27M.js.map} +0 -0
  30. /package/dist/{research-5QALNYVS.js.map → research-ZR7HXITG.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ fetchMetadata,
3
4
  uploadMetadata
4
5
  } from "./chunk-DVWORPEY.js";
5
6
  import {
@@ -9,7 +10,7 @@ import {
9
10
  queryApprovals,
10
11
  queryJoinRequests,
11
12
  revokeAttestation
12
- } from "./chunk-P3GYAMGZ.js";
13
+ } from "./chunk-HKZONPXW.js";
13
14
  import {
14
15
  approveDepositor,
15
16
  deposit,
@@ -27,7 +28,7 @@ import {
27
28
  setTextRecord,
28
29
  setVaultAddress,
29
30
  simulateBatch
30
- } from "./chunk-B7XDUFI3.js";
31
+ } from "./chunk-Q37V65B6.js";
31
32
  import {
32
33
  AGENT_REGISTRY,
33
34
  EAS_SCHEMAS,
@@ -36,20 +37,23 @@ import {
36
37
  SHERWOOD,
37
38
  STRATEGY_REGISTRY_ABI,
38
39
  SYNDICATE_FACTORY_ABI,
40
+ SYNDICATE_GOVERNOR_ABI,
39
41
  SYNDICATE_VAULT_ABI,
40
42
  TOKENS,
41
43
  UNISWAP,
42
44
  UNISWAP_QUOTER_V2_ABI,
43
45
  VENICE,
44
46
  VENICE_STAKING_ABI
45
- } from "./chunk-6MEYSN2W.js";
47
+ } from "./chunk-OUES74ID.js";
46
48
  import {
49
+ VALID_NETWORKS,
47
50
  cacheGroupId,
48
51
  getAccount,
49
52
  getAgentId,
50
53
  getChain,
51
54
  getChainContracts,
52
55
  getExplorerUrl,
56
+ getNetwork,
53
57
  getPublicClient,
54
58
  getRpcUrl,
55
59
  getVeniceApiKey,
@@ -57,17 +61,18 @@ import {
57
61
  loadConfig,
58
62
  setAgentId,
59
63
  setChainContract,
64
+ setConfigRpcUrl,
60
65
  setNetwork,
61
66
  setPrivateKey,
62
67
  setVeniceApiKey
63
- } from "./chunk-QMWMT6EH.js";
68
+ } from "./chunk-5ADWTXNT.js";
64
69
 
65
70
  // src/index.ts
66
71
  import { config as loadDotenv } from "dotenv";
67
- import { Command } from "commander";
72
+ import { Command, Option } from "commander";
68
73
  import { parseUnits as parseUnits8 } from "viem";
69
- import chalk5 from "chalk";
70
- import ora5 from "ora";
74
+ import chalk7 from "chalk";
75
+ import ora7 from "ora";
71
76
  import { input, confirm, select } from "@inquirer/prompts";
72
77
 
73
78
  // src/providers/moonwell.ts
@@ -929,7 +934,7 @@ function registerVeniceCommands(program2) {
929
934
  [assetAddress, totalDeposited, agents] = await Promise.all([
930
935
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
931
936
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
932
- client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentOperators" })
937
+ client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
933
938
  ]);
934
939
  [assetDecimals, assetSymbol, assetBalance] = await Promise.all([
935
940
  client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
@@ -1096,7 +1101,7 @@ function registerVeniceCommands(program2) {
1096
1101
  const [assetAddress, totalDeposited, agents] = await Promise.all([
1097
1102
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
1098
1103
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
1099
- client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentOperators" })
1104
+ client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
1100
1105
  ]);
1101
1106
  const [assetDecimals, assetSymbol, assetBalance] = await Promise.all([
1102
1107
  client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
@@ -1330,7 +1335,7 @@ function registerAllowanceCommands(program2) {
1330
1335
  [assetAddress, totalDeposited, agents] = await Promise.all([
1331
1336
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
1332
1337
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
1333
- client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentOperators" })
1338
+ client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
1334
1339
  ]);
1335
1340
  [assetDecimals, assetSymbol, assetBalance] = await Promise.all([
1336
1341
  client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
@@ -1473,7 +1478,7 @@ function registerAllowanceCommands(program2) {
1473
1478
  const [assetAddress, totalDeposited, agents] = await Promise.all([
1474
1479
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "asset" }),
1475
1480
  client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "totalDeposited" }),
1476
- client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentOperators" })
1481
+ client.readContract({ address: vaultAddress, abi: SYNDICATE_VAULT_ABI, functionName: "getAgentAddresses" })
1477
1482
  ]);
1478
1483
  const [assetDecimals, assetSymbol, assetBalance] = await Promise.all([
1479
1484
  client.readContract({ address: assetAddress, abi: ERC20_ABI, functionName: "decimals" }),
@@ -1674,60 +1679,892 @@ function registerIdentityCommands(program2) {
1674
1679
  });
1675
1680
  }
1676
1681
 
1677
- // src/index.ts
1678
- try {
1679
- loadDotenv();
1680
- } catch {
1682
+ // src/commands/proposal.ts
1683
+ import { isAddress as isAddress4 } from "viem";
1684
+ import chalk5 from "chalk";
1685
+ import ora5 from "ora";
1686
+ import { readFileSync } from "fs";
1687
+
1688
+ // src/lib/governor.ts
1689
+ var PROPOSAL_STATES = [
1690
+ "Pending",
1691
+ "Approved",
1692
+ "Rejected",
1693
+ "Expired",
1694
+ "Executed",
1695
+ "Settled",
1696
+ "Cancelled"
1697
+ ];
1698
+ function parseDuration(input2) {
1699
+ const match = input2.match(/^(\d+)(d|h|m|s)?$/);
1700
+ if (!match) throw new Error(`Invalid duration: ${input2}`);
1701
+ const value = BigInt(match[1]);
1702
+ switch (match[2]) {
1703
+ case "d":
1704
+ return value * 86400n;
1705
+ case "h":
1706
+ return value * 3600n;
1707
+ case "m":
1708
+ return value * 60n;
1709
+ case "s":
1710
+ case void 0:
1711
+ return value;
1712
+ default:
1713
+ throw new Error(`Unknown duration unit: ${match[2]}`);
1714
+ }
1681
1715
  }
1682
- async function loadXmtp() {
1683
- return import("./xmtp-4XTQQ7RE.js");
1716
+ function getGovernorAddress() {
1717
+ return SHERWOOD().GOVERNOR;
1718
+ }
1719
+ async function getGovernorParams() {
1720
+ const client = getPublicClient();
1721
+ const result = await client.readContract({
1722
+ address: getGovernorAddress(),
1723
+ abi: SYNDICATE_GOVERNOR_ABI,
1724
+ functionName: "getGovernorParams"
1725
+ });
1726
+ return result;
1727
+ }
1728
+ async function getProposal(id) {
1729
+ const client = getPublicClient();
1730
+ const result = await client.readContract({
1731
+ address: getGovernorAddress(),
1732
+ abi: SYNDICATE_GOVERNOR_ABI,
1733
+ functionName: "getProposal",
1734
+ args: [id]
1735
+ });
1736
+ return result;
1737
+ }
1738
+ async function getProposalState(id) {
1739
+ const client = getPublicClient();
1740
+ return client.readContract({
1741
+ address: getGovernorAddress(),
1742
+ abi: SYNDICATE_GOVERNOR_ABI,
1743
+ functionName: "getProposalState",
1744
+ args: [id]
1745
+ });
1746
+ }
1747
+ async function proposalCount() {
1748
+ const client = getPublicClient();
1749
+ return client.readContract({
1750
+ address: getGovernorAddress(),
1751
+ abi: SYNDICATE_GOVERNOR_ABI,
1752
+ functionName: "proposalCount"
1753
+ });
1754
+ }
1755
+ async function getVoteWeight(proposalId, voter) {
1756
+ const client = getPublicClient();
1757
+ return client.readContract({
1758
+ address: getGovernorAddress(),
1759
+ abi: SYNDICATE_GOVERNOR_ABI,
1760
+ functionName: "getVoteWeight",
1761
+ args: [proposalId, voter]
1762
+ });
1763
+ }
1764
+ async function hasVoted(proposalId, voter) {
1765
+ const client = getPublicClient();
1766
+ return client.readContract({
1767
+ address: getGovernorAddress(),
1768
+ abi: SYNDICATE_GOVERNOR_ABI,
1769
+ functionName: "hasVoted",
1770
+ args: [proposalId, voter]
1771
+ });
1684
1772
  }
1773
+ async function getProposalCalls(proposalId) {
1774
+ const client = getPublicClient();
1775
+ const result = await client.readContract({
1776
+ address: getGovernorAddress(),
1777
+ abi: SYNDICATE_GOVERNOR_ABI,
1778
+ functionName: "getProposalCalls",
1779
+ args: [proposalId]
1780
+ });
1781
+ return result.map((c) => ({ target: c.target, data: c.data, value: c.value }));
1782
+ }
1783
+ async function getRegisteredVaults() {
1784
+ const client = getPublicClient();
1785
+ return client.readContract({
1786
+ address: getGovernorAddress(),
1787
+ abi: SYNDICATE_GOVERNOR_ABI,
1788
+ functionName: "getRegisteredVaults"
1789
+ });
1790
+ }
1791
+ async function getCapitalSnapshot(proposalId) {
1792
+ const client = getPublicClient();
1793
+ return client.readContract({
1794
+ address: getGovernorAddress(),
1795
+ abi: SYNDICATE_GOVERNOR_ABI,
1796
+ functionName: "getCapitalSnapshot",
1797
+ args: [proposalId]
1798
+ });
1799
+ }
1800
+ async function propose(vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex) {
1801
+ const wallet = getWalletClient();
1802
+ const client = getPublicClient();
1803
+ const hash = await wallet.writeContract({
1804
+ account: getAccount(),
1805
+ chain: getChain(),
1806
+ address: getGovernorAddress(),
1807
+ abi: SYNDICATE_GOVERNOR_ABI,
1808
+ functionName: "propose",
1809
+ args: [vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex]
1810
+ });
1811
+ const receipt = await client.waitForTransactionReceipt({ hash });
1812
+ let proposalId;
1813
+ try {
1814
+ proposalId = await proposalCount();
1815
+ } catch {
1816
+ proposalId = 0n;
1817
+ }
1818
+ return { hash: receipt.transactionHash, proposalId };
1819
+ }
1820
+ async function vote(proposalId, support) {
1821
+ const wallet = getWalletClient();
1822
+ const client = getPublicClient();
1823
+ const hash = await wallet.writeContract({
1824
+ account: getAccount(),
1825
+ chain: getChain(),
1826
+ address: getGovernorAddress(),
1827
+ abi: SYNDICATE_GOVERNOR_ABI,
1828
+ functionName: "vote",
1829
+ args: [proposalId, support]
1830
+ });
1831
+ const receipt = await client.waitForTransactionReceipt({ hash });
1832
+ return receipt.transactionHash;
1833
+ }
1834
+ async function executeProposal(proposalId) {
1835
+ const wallet = getWalletClient();
1836
+ const client = getPublicClient();
1837
+ const hash = await wallet.writeContract({
1838
+ account: getAccount(),
1839
+ chain: getChain(),
1840
+ address: getGovernorAddress(),
1841
+ abi: SYNDICATE_GOVERNOR_ABI,
1842
+ functionName: "executeProposal",
1843
+ args: [proposalId]
1844
+ });
1845
+ const receipt = await client.waitForTransactionReceipt({ hash });
1846
+ return receipt.transactionHash;
1847
+ }
1848
+ async function settleProposal(proposalId) {
1849
+ const wallet = getWalletClient();
1850
+ const client = getPublicClient();
1851
+ const hash = await wallet.writeContract({
1852
+ account: getAccount(),
1853
+ chain: getChain(),
1854
+ address: getGovernorAddress(),
1855
+ abi: SYNDICATE_GOVERNOR_ABI,
1856
+ functionName: "settleProposal",
1857
+ args: [proposalId]
1858
+ });
1859
+ const receipt = await client.waitForTransactionReceipt({ hash });
1860
+ return receipt.transactionHash;
1861
+ }
1862
+ async function settleByAgent(proposalId, calls) {
1863
+ const wallet = getWalletClient();
1864
+ const client = getPublicClient();
1865
+ const hash = await wallet.writeContract({
1866
+ account: getAccount(),
1867
+ chain: getChain(),
1868
+ address: getGovernorAddress(),
1869
+ abi: SYNDICATE_GOVERNOR_ABI,
1870
+ functionName: "settleByAgent",
1871
+ args: [proposalId, calls]
1872
+ });
1873
+ const receipt = await client.waitForTransactionReceipt({ hash });
1874
+ return receipt.transactionHash;
1875
+ }
1876
+ async function emergencySettle(proposalId, calls) {
1877
+ const wallet = getWalletClient();
1878
+ const client = getPublicClient();
1879
+ const hash = await wallet.writeContract({
1880
+ account: getAccount(),
1881
+ chain: getChain(),
1882
+ address: getGovernorAddress(),
1883
+ abi: SYNDICATE_GOVERNOR_ABI,
1884
+ functionName: "emergencySettle",
1885
+ args: [proposalId, calls]
1886
+ });
1887
+ const receipt = await client.waitForTransactionReceipt({ hash });
1888
+ return receipt.transactionHash;
1889
+ }
1890
+ async function cancelProposal(proposalId) {
1891
+ const wallet = getWalletClient();
1892
+ const client = getPublicClient();
1893
+ const hash = await wallet.writeContract({
1894
+ account: getAccount(),
1895
+ chain: getChain(),
1896
+ address: getGovernorAddress(),
1897
+ abi: SYNDICATE_GOVERNOR_ABI,
1898
+ functionName: "cancelProposal",
1899
+ args: [proposalId]
1900
+ });
1901
+ const receipt = await client.waitForTransactionReceipt({ hash });
1902
+ return receipt.transactionHash;
1903
+ }
1904
+ async function emergencyCancel(proposalId) {
1905
+ const wallet = getWalletClient();
1906
+ const client = getPublicClient();
1907
+ const hash = await wallet.writeContract({
1908
+ account: getAccount(),
1909
+ chain: getChain(),
1910
+ address: getGovernorAddress(),
1911
+ abi: SYNDICATE_GOVERNOR_ABI,
1912
+ functionName: "emergencyCancel",
1913
+ args: [proposalId]
1914
+ });
1915
+ const receipt = await client.waitForTransactionReceipt({ hash });
1916
+ return receipt.transactionHash;
1917
+ }
1918
+ async function setVotingPeriod(seconds) {
1919
+ const wallet = getWalletClient();
1920
+ const client = getPublicClient();
1921
+ const hash = await wallet.writeContract({
1922
+ account: getAccount(),
1923
+ chain: getChain(),
1924
+ address: getGovernorAddress(),
1925
+ abi: SYNDICATE_GOVERNOR_ABI,
1926
+ functionName: "setVotingPeriod",
1927
+ args: [seconds]
1928
+ });
1929
+ const receipt = await client.waitForTransactionReceipt({ hash });
1930
+ return receipt.transactionHash;
1931
+ }
1932
+ async function setExecutionWindow(seconds) {
1933
+ const wallet = getWalletClient();
1934
+ const client = getPublicClient();
1935
+ const hash = await wallet.writeContract({
1936
+ account: getAccount(),
1937
+ chain: getChain(),
1938
+ address: getGovernorAddress(),
1939
+ abi: SYNDICATE_GOVERNOR_ABI,
1940
+ functionName: "setExecutionWindow",
1941
+ args: [seconds]
1942
+ });
1943
+ const receipt = await client.waitForTransactionReceipt({ hash });
1944
+ return receipt.transactionHash;
1945
+ }
1946
+ async function setQuorumBps(bps) {
1947
+ const wallet = getWalletClient();
1948
+ const client = getPublicClient();
1949
+ const hash = await wallet.writeContract({
1950
+ account: getAccount(),
1951
+ chain: getChain(),
1952
+ address: getGovernorAddress(),
1953
+ abi: SYNDICATE_GOVERNOR_ABI,
1954
+ functionName: "setQuorumBps",
1955
+ args: [bps]
1956
+ });
1957
+ const receipt = await client.waitForTransactionReceipt({ hash });
1958
+ return receipt.transactionHash;
1959
+ }
1960
+ async function setMaxPerformanceFeeBps(bps) {
1961
+ const wallet = getWalletClient();
1962
+ const client = getPublicClient();
1963
+ const hash = await wallet.writeContract({
1964
+ account: getAccount(),
1965
+ chain: getChain(),
1966
+ address: getGovernorAddress(),
1967
+ abi: SYNDICATE_GOVERNOR_ABI,
1968
+ functionName: "setMaxPerformanceFeeBps",
1969
+ args: [bps]
1970
+ });
1971
+ const receipt = await client.waitForTransactionReceipt({ hash });
1972
+ return receipt.transactionHash;
1973
+ }
1974
+ async function setMaxStrategyDuration(seconds) {
1975
+ const wallet = getWalletClient();
1976
+ const client = getPublicClient();
1977
+ const hash = await wallet.writeContract({
1978
+ account: getAccount(),
1979
+ chain: getChain(),
1980
+ address: getGovernorAddress(),
1981
+ abi: SYNDICATE_GOVERNOR_ABI,
1982
+ functionName: "setMaxStrategyDuration",
1983
+ args: [seconds]
1984
+ });
1985
+ const receipt = await client.waitForTransactionReceipt({ hash });
1986
+ return receipt.transactionHash;
1987
+ }
1988
+ async function setCooldownPeriod(seconds) {
1989
+ const wallet = getWalletClient();
1990
+ const client = getPublicClient();
1991
+ const hash = await wallet.writeContract({
1992
+ account: getAccount(),
1993
+ chain: getChain(),
1994
+ address: getGovernorAddress(),
1995
+ abi: SYNDICATE_GOVERNOR_ABI,
1996
+ functionName: "setCooldownPeriod",
1997
+ args: [seconds]
1998
+ });
1999
+ const receipt = await client.waitForTransactionReceipt({ hash });
2000
+ return receipt.transactionHash;
2001
+ }
2002
+
2003
+ // src/lib/format.ts
2004
+ function formatDurationShort(seconds) {
2005
+ const s = Number(seconds);
2006
+ if (s >= 86400) return `${(s / 86400).toFixed(s % 86400 === 0 ? 0 : 1)}d`;
2007
+ if (s >= 3600) return `${(s / 3600).toFixed(s % 3600 === 0 ? 0 : 1)}h`;
2008
+ if (s >= 60) return `${(s / 60).toFixed(0)}m`;
2009
+ return `${s}s`;
2010
+ }
2011
+ function formatDurationLong(seconds) {
2012
+ const s = Number(seconds);
2013
+ if (s >= 86400) return `${(s / 86400).toFixed(s % 86400 === 0 ? 0 : 1)} day${s >= 172800 ? "s" : ""}`;
2014
+ if (s >= 3600) return `${(s / 3600).toFixed(s % 3600 === 0 ? 0 : 1)} hour${s >= 7200 ? "s" : ""}`;
2015
+ if (s >= 60) return `${(s / 60).toFixed(0)} min`;
2016
+ return `${s}s`;
2017
+ }
2018
+ function formatShares(raw) {
2019
+ const num = Number(raw) / 1e6;
2020
+ return num.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 2 });
2021
+ }
2022
+ function formatUSDC(raw) {
2023
+ const num = Number(raw) / 1e6;
2024
+ return `$${num.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
2025
+ }
2026
+ function parseBigIntArg(value, name) {
2027
+ if (!/^-?\d+$/.test(value)) {
2028
+ throw new Error(`Invalid ${name}: "${value}" is not a valid integer`);
2029
+ }
2030
+ return BigInt(value);
2031
+ }
2032
+
2033
+ // src/commands/proposal.ts
1685
2034
  var G = chalk5.green;
1686
2035
  var W = chalk5.white;
1687
2036
  var DIM = chalk5.gray;
1688
2037
  var BOLD = chalk5.white.bold;
1689
2038
  var LABEL = chalk5.green.bold;
1690
2039
  var SEP = () => console.log(DIM("\u2500".repeat(60)));
2040
+ function formatTimestamp(ts) {
2041
+ if (ts === 0n) return "\u2014";
2042
+ return new Date(Number(ts) * 1e3).toLocaleString();
2043
+ }
2044
+ function parseCallsFile(path) {
2045
+ const raw = readFileSync(path, "utf-8");
2046
+ const parsed = JSON.parse(raw);
2047
+ return parsed.map((c) => ({
2048
+ target: c.target,
2049
+ data: c.data,
2050
+ value: BigInt(c.value || "0")
2051
+ }));
2052
+ }
2053
+ function registerProposalCommands(program2) {
2054
+ const proposal = program2.command("proposal").description("Governance proposals \u2014 create, vote, execute, settle");
2055
+ proposal.command("create").description("Submit a strategy proposal").requiredOption("--vault <address>", "Vault address the proposal targets").requiredOption("--name <name>", "Strategy name").requiredOption("--description <text>", "Strategy rationale and risk summary").requiredOption("--performance-fee <bps>", "Agent fee in bps (e.g. 1500 = 15%)").requiredOption("--duration <duration>", "Strategy duration (e.g. 7d, 24h, 3600)").requiredOption("--calls <path>", "Path to JSON file with Call[] array").requiredOption("--split-index <n>", "Index where execute calls end and settle calls begin").option("--metadata-uri <uri>", "Override \u2014 skip IPFS upload and use this URI directly").action(async (opts) => {
2056
+ try {
2057
+ const vault = opts.vault;
2058
+ if (!isAddress4(vault)) {
2059
+ console.error(chalk5.red(`Invalid vault address: ${opts.vault}`));
2060
+ process.exit(1);
2061
+ }
2062
+ const performanceFeeBps = parseBigIntArg(opts.performanceFee, "performance-fee");
2063
+ const strategyDuration = parseDuration(opts.duration);
2064
+ const splitIndex = parseBigIntArg(opts.splitIndex, "split-index");
2065
+ const calls = parseCallsFile(opts.calls);
2066
+ let metadataURI = opts.metadataUri || "";
2067
+ if (!metadataURI) {
2068
+ const spinner2 = ora5({ text: W("Uploading metadata to IPFS..."), color: "green" }).start();
2069
+ try {
2070
+ const account = getAccount();
2071
+ const metadata = {
2072
+ schema: "sherwood/proposal/v1",
2073
+ name: opts.name,
2074
+ description: opts.description,
2075
+ chain: getNetwork(),
2076
+ strategies: [],
2077
+ terms: { ragequitEnabled: true },
2078
+ links: {}
2079
+ };
2080
+ const proposalMeta = {
2081
+ ...metadata,
2082
+ proposer: account.address,
2083
+ vault,
2084
+ performanceFeeBps: Number(performanceFeeBps),
2085
+ strategyDuration: Number(strategyDuration),
2086
+ createdAt: Math.floor(Date.now() / 1e3)
2087
+ };
2088
+ metadataURI = await uploadMetadata(proposalMeta);
2089
+ spinner2.succeed(G(`Metadata pinned: ${DIM(metadataURI)}`));
2090
+ } catch (err) {
2091
+ spinner2.warn(chalk5.yellow("IPFS upload failed \u2014 using inline metadata"));
2092
+ const json = JSON.stringify({ name: opts.name, description: opts.description, vault });
2093
+ metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
2094
+ }
2095
+ }
2096
+ console.log();
2097
+ console.log(LABEL(" \u25C6 Proposal Summary"));
2098
+ SEP();
2099
+ console.log(W(` Name: ${BOLD(opts.name)}`));
2100
+ console.log(W(` Vault: ${G(vault)}`));
2101
+ console.log(W(` Performance Fee: ${Number(performanceFeeBps) / 100}%`));
2102
+ console.log(W(` Duration: ${formatDurationShort(strategyDuration)}`));
2103
+ console.log(W(` Calls: ${calls.length} (split at ${splitIndex})`));
2104
+ console.log(W(` Metadata: ${DIM(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
2105
+ SEP();
2106
+ const spinner = ora5({ text: W("Submitting proposal..."), color: "green" }).start();
2107
+ const result = await propose(vault, metadataURI, performanceFeeBps, strategyDuration, calls, splitIndex);
2108
+ spinner.succeed(G("Proposal submitted"));
2109
+ console.log();
2110
+ console.log(LABEL(" \u25C6 Proposal Created"));
2111
+ SEP();
2112
+ console.log(W(` Proposal ID: ${G(`#${result.proposalId}`)}`));
2113
+ console.log(W(` Tx: ${DIM(getExplorerUrl(result.hash))}`));
2114
+ SEP();
2115
+ console.log();
2116
+ } catch (err) {
2117
+ console.error(chalk5.red(`
2118
+ \u2716 ${err instanceof Error ? err.message : String(err)}`));
2119
+ process.exit(1);
2120
+ }
2121
+ });
2122
+ proposal.command("list").description("List proposals").option("--vault <address>", "Filter by vault").option("--state <filter>", "Filter by state: pending, approved, executed, settled, all", "all").action(async (opts) => {
2123
+ const spinner = ora5("Loading proposals...").start();
2124
+ try {
2125
+ const count = await proposalCount();
2126
+ if (count === 0n) {
2127
+ spinner.stop();
2128
+ console.log(DIM("\n No proposals found.\n"));
2129
+ return;
2130
+ }
2131
+ const vaultFilter = opts.vault ? opts.vault.toLowerCase() : null;
2132
+ const stateFilter = opts.state.toLowerCase();
2133
+ const stateIndex = PROPOSAL_STATES.findIndex((s) => s.toLowerCase() === stateFilter);
2134
+ const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i + 1));
2135
+ const results = await Promise.all(
2136
+ ids.map(async (id) => {
2137
+ const [p, state] = await Promise.all([getProposal(id), getProposalState(id)]);
2138
+ return { ...p, computedState: state };
2139
+ })
2140
+ );
2141
+ const proposals = results.filter((p) => {
2142
+ if (vaultFilter && p.vault.toLowerCase() !== vaultFilter) return false;
2143
+ if (stateFilter !== "all" && stateIndex >= 0 && p.computedState !== stateIndex) return false;
2144
+ return true;
2145
+ });
2146
+ spinner.stop();
2147
+ if (proposals.length === 0) {
2148
+ console.log(DIM("\n No matching proposals.\n"));
2149
+ return;
2150
+ }
2151
+ console.log();
2152
+ console.log(BOLD(`Proposals (${proposals.length})`));
2153
+ console.log(DIM("\u2500".repeat(90)));
2154
+ console.log(
2155
+ DIM(" ID ") + DIM("Agent".padEnd(14)) + DIM("State".padEnd(12)) + DIM("Votes (For/Against)".padEnd(22)) + DIM("Fee".padEnd(8)) + DIM("Duration".padEnd(10)) + DIM("Created")
2156
+ );
2157
+ console.log(DIM("\u2500".repeat(90)));
2158
+ for (const p of proposals) {
2159
+ const state = PROPOSAL_STATES[p.computedState] || "Unknown";
2160
+ const created = p.snapshotTimestamp > 0n ? new Date(Number(p.snapshotTimestamp) * 1e3).toLocaleDateString() : "\u2014";
2161
+ const agent = `${p.proposer.slice(0, 6)}...${p.proposer.slice(-4)}`;
2162
+ const fee = `${Number(p.performanceFeeBps) / 100}%`;
2163
+ const dur = formatDurationShort(p.strategyDuration);
2164
+ const votes = `${formatShares(p.votesFor)}/${formatShares(p.votesAgainst)}`;
2165
+ console.log(
2166
+ ` ${String(p.id).padEnd(4)}${agent.padEnd(14)}${state.padEnd(12)}${votes.padEnd(22)}${fee.padEnd(8)}${dur.padEnd(10)}${created}`
2167
+ );
2168
+ }
2169
+ console.log();
2170
+ } catch (err) {
2171
+ spinner.fail("Failed to load proposals");
2172
+ console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2173
+ process.exit(1);
2174
+ }
2175
+ });
2176
+ proposal.command("show").description("Show full proposal details").argument("<id>", "Proposal ID").action(async (idStr) => {
2177
+ const spinner = ora5("Loading proposal...").start();
2178
+ try {
2179
+ const id = parseBigIntArg(idStr, "proposal ID");
2180
+ const p = await getProposal(id);
2181
+ const state = await getProposalState(id);
2182
+ const calls = await getProposalCalls(id);
2183
+ const params = await getGovernorParams();
2184
+ spinner.stop();
2185
+ const stateLabel = PROPOSAL_STATES[state] || "Unknown";
2186
+ const totalVotes = p.votesFor + p.votesAgainst;
2187
+ const quorumNeeded = totalVotes > 0n ? `${Number(params.quorumBps) / 100}%` : "\u2014";
2188
+ console.log();
2189
+ console.log(LABEL(` \u25C6 Proposal #${p.id}`));
2190
+ SEP();
2191
+ if (p.metadataURI && p.metadataURI.startsWith("ipfs://")) {
2192
+ try {
2193
+ const meta = await fetchMetadata(p.metadataURI);
2194
+ console.log(W(` Name: ${BOLD(meta.name)}`));
2195
+ console.log(W(` Description: ${DIM(meta.description)}`));
2196
+ } catch {
2197
+ console.log(W(` Metadata: ${DIM(p.metadataURI)}`));
2198
+ }
2199
+ } else if (p.metadataURI) {
2200
+ console.log(W(` Metadata: ${DIM(p.metadataURI)}`));
2201
+ }
2202
+ console.log(W(` State: ${BOLD(stateLabel)}`));
2203
+ console.log(W(` Proposer: ${G(p.proposer)}`));
2204
+ console.log(W(` Vault: ${G(p.vault)}`));
2205
+ console.log(W(` Performance Fee: ${Number(p.performanceFeeBps) / 100}%`));
2206
+ console.log(W(` Duration: ${formatDurationShort(p.strategyDuration)}`));
2207
+ console.log();
2208
+ console.log(LABEL(" Timestamps"));
2209
+ console.log(W(` Snapshot: ${formatTimestamp(p.snapshotTimestamp)}`));
2210
+ console.log(W(` Vote End: ${formatTimestamp(p.voteEnd)}`));
2211
+ console.log(W(` Execute By: ${formatTimestamp(p.executeBy)}`));
2212
+ console.log(W(` Executed At: ${formatTimestamp(p.executedAt)}`));
2213
+ console.log();
2214
+ console.log(LABEL(" Votes"));
2215
+ console.log(W(` For: ${formatShares(p.votesFor)}`));
2216
+ console.log(W(` Against: ${formatShares(p.votesAgainst)}`));
2217
+ console.log(W(` Quorum: ${quorumNeeded}`));
2218
+ if (state === 4 || state === 5) {
2219
+ try {
2220
+ const cap = await getCapitalSnapshot(id);
2221
+ console.log();
2222
+ console.log(LABEL(" Capital"));
2223
+ console.log(W(` Snapshot: ${formatUSDC(cap)}`));
2224
+ } catch {
2225
+ }
2226
+ }
2227
+ console.log();
2228
+ console.log(LABEL(` Calls (${calls.length}, split at ${p.splitIndex})`));
2229
+ for (let i = 0; i < calls.length; i++) {
2230
+ const phase = BigInt(i) < p.splitIndex ? "execute" : "settle";
2231
+ console.log(DIM(` [${i}] (${phase}) target=${calls[i].target}`));
2232
+ console.log(DIM(` data=${calls[i].data.slice(0, 20)}... value=${calls[i].value}`));
2233
+ }
2234
+ SEP();
2235
+ console.log();
2236
+ } catch (err) {
2237
+ spinner.fail("Failed to load proposal");
2238
+ console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2239
+ process.exit(1);
2240
+ }
2241
+ });
2242
+ proposal.command("vote").description("Cast a vote on a pending proposal").requiredOption("--id <proposalId>", "Proposal ID").requiredOption("--support <yes|no>", "Vote direction: yes or no").action(async (opts) => {
2243
+ try {
2244
+ const proposalId = parseBigIntArg(opts.id, "proposal ID");
2245
+ const support = opts.support.toLowerCase() === "yes";
2246
+ const account = getAccount();
2247
+ const spinner = ora5("Loading proposal...").start();
2248
+ const p = await getProposal(proposalId);
2249
+ const state = await getProposalState(proposalId);
2250
+ if (state !== 0) {
2251
+ spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Pending`);
2252
+ process.exit(1);
2253
+ }
2254
+ const alreadyVoted = await hasVoted(proposalId, account.address);
2255
+ if (alreadyVoted) {
2256
+ spinner.fail("You have already voted on this proposal");
2257
+ process.exit(1);
2258
+ }
2259
+ const weight = await getVoteWeight(proposalId, account.address);
2260
+ spinner.stop();
2261
+ console.log();
2262
+ console.log(LABEL(" \u25C6 Cast Vote"));
2263
+ SEP();
2264
+ console.log(W(` Proposal: #${proposalId}`));
2265
+ console.log(W(` Vault: ${G(p.vault)}`));
2266
+ console.log(W(` Support: ${support ? G("YES") : chalk5.red("NO")}`));
2267
+ console.log(W(` Weight: ${formatShares(weight)} shares`));
2268
+ SEP();
2269
+ const voteSpinner = ora5({ text: W("Submitting vote..."), color: "green" }).start();
2270
+ const hash = await vote(proposalId, support);
2271
+ voteSpinner.succeed(G("Vote cast"));
2272
+ console.log(DIM(` ${getExplorerUrl(hash)}`));
2273
+ console.log();
2274
+ } catch (err) {
2275
+ console.error(chalk5.red(`
2276
+ \u2716 ${err instanceof Error ? err.message : String(err)}`));
2277
+ process.exit(1);
2278
+ }
2279
+ });
2280
+ proposal.command("execute").description("Execute an approved proposal").requiredOption("--id <proposalId>", "Proposal ID").action(async (opts) => {
2281
+ try {
2282
+ const proposalId = parseBigIntArg(opts.id, "proposal ID");
2283
+ const spinner = ora5("Loading proposal...").start();
2284
+ const state = await getProposalState(proposalId);
2285
+ if (state !== 1) {
2286
+ spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Approved`);
2287
+ process.exit(1);
2288
+ }
2289
+ spinner.text = W("Executing proposal...");
2290
+ const hash = await executeProposal(proposalId);
2291
+ spinner.succeed(G("Proposal executed"));
2292
+ console.log(DIM(` ${getExplorerUrl(hash)}`));
2293
+ try {
2294
+ const cap = await getCapitalSnapshot(proposalId);
2295
+ console.log(DIM(` Capital snapshot: ${formatUSDC(cap)}`));
2296
+ } catch {
2297
+ }
2298
+ console.log();
2299
+ } catch (err) {
2300
+ console.error(chalk5.red(`
2301
+ \u2716 ${err instanceof Error ? err.message : String(err)}`));
2302
+ process.exit(1);
2303
+ }
2304
+ });
2305
+ proposal.command("settle").description("Settle an executed proposal (auto-routes settlement path)").requiredOption("--id <proposalId>", "Proposal ID").option("--calls <path>", "Path to JSON file with settle Call[] (for agent/emergency settle)").action(async (opts) => {
2306
+ try {
2307
+ const proposalId = parseBigIntArg(opts.id, "proposal ID");
2308
+ const account = getAccount();
2309
+ const spinner = ora5("Loading proposal...").start();
2310
+ const p = await getProposal(proposalId);
2311
+ const state = await getProposalState(proposalId);
2312
+ if (state !== 4) {
2313
+ spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"}, not Executed`);
2314
+ process.exit(1);
2315
+ }
2316
+ const isProposer = account.address.toLowerCase() === p.proposer.toLowerCase();
2317
+ const now = BigInt(Math.floor(Date.now() / 1e3));
2318
+ const durationElapsed = p.executedAt > 0n && now >= p.executedAt + p.strategyDuration;
2319
+ let hash;
2320
+ if (isProposer && opts.calls) {
2321
+ spinner.text = W("Settling by agent...");
2322
+ const calls = parseCallsFile(opts.calls);
2323
+ hash = await settleByAgent(proposalId, calls);
2324
+ spinner.succeed(G("Settled by agent"));
2325
+ } else if (durationElapsed && !opts.calls) {
2326
+ spinner.text = W("Settling (permissionless)...");
2327
+ hash = await settleProposal(proposalId);
2328
+ spinner.succeed(G("Settled (permissionless)"));
2329
+ } else if (durationElapsed && opts.calls) {
2330
+ spinner.text = W("Emergency settling...");
2331
+ const calls = parseCallsFile(opts.calls);
2332
+ hash = await emergencySettle(proposalId, calls);
2333
+ spinner.succeed(G("Emergency settled"));
2334
+ } else {
2335
+ spinner.fail("Cannot settle: duration not elapsed. If you are the proposer, provide --calls.");
2336
+ process.exit(1);
2337
+ }
2338
+ console.log(DIM(` ${getExplorerUrl(hash)}`));
2339
+ console.log();
2340
+ } catch (err) {
2341
+ console.error(chalk5.red(`
2342
+ \u2716 ${err instanceof Error ? err.message : String(err)}`));
2343
+ process.exit(1);
2344
+ }
2345
+ });
2346
+ proposal.command("cancel").description("Cancel a proposal (proposer or vault owner)").requiredOption("--id <proposalId>", "Proposal ID").option("--emergency", "Emergency cancel (vault owner only, any non-settled state)").action(async (opts) => {
2347
+ try {
2348
+ const proposalId = parseBigIntArg(opts.id, "proposal ID");
2349
+ const spinner = ora5("Loading proposal...").start();
2350
+ const state = await getProposalState(proposalId);
2351
+ if (state === 5 || state === 6) {
2352
+ spinner.fail(`Proposal is already ${PROPOSAL_STATES[state]}`);
2353
+ process.exit(1);
2354
+ }
2355
+ let hash;
2356
+ if (opts.emergency) {
2357
+ spinner.text = W("Emergency cancelling...");
2358
+ hash = await emergencyCancel(proposalId);
2359
+ spinner.succeed(G("Emergency cancelled"));
2360
+ } else {
2361
+ if (state !== 0 && state !== 1) {
2362
+ spinner.fail(`Proposal is ${PROPOSAL_STATES[state] || "Unknown"} \u2014 use --emergency for non-pending/approved`);
2363
+ process.exit(1);
2364
+ }
2365
+ spinner.text = W("Cancelling proposal...");
2366
+ hash = await cancelProposal(proposalId);
2367
+ spinner.succeed(G("Proposal cancelled"));
2368
+ }
2369
+ console.log(DIM(` ${getExplorerUrl(hash)}`));
2370
+ console.log();
2371
+ } catch (err) {
2372
+ console.error(chalk5.red(`
2373
+ \u2716 ${err instanceof Error ? err.message : String(err)}`));
2374
+ process.exit(1);
2375
+ }
2376
+ });
2377
+ }
2378
+
2379
+ // src/commands/governor.ts
2380
+ import chalk6 from "chalk";
2381
+ import ora6 from "ora";
2382
+ var G2 = chalk6.green;
2383
+ var W2 = chalk6.white;
2384
+ var DIM2 = chalk6.gray;
2385
+ var BOLD2 = chalk6.white.bold;
2386
+ var LABEL2 = chalk6.green.bold;
2387
+ var SEP2 = () => console.log(DIM2("\u2500".repeat(60)));
2388
+ function registerGovernorCommands(program2) {
2389
+ const governor = program2.command("governor").description("Governor parameters and vault management");
2390
+ governor.command("info").description("Display current governor parameters and registered vaults").action(async () => {
2391
+ const spinner = ora6("Loading governor info...").start();
2392
+ try {
2393
+ const [params, vaults] = await Promise.all([
2394
+ getGovernorParams(),
2395
+ getRegisteredVaults()
2396
+ ]);
2397
+ spinner.stop();
2398
+ console.log();
2399
+ console.log(LABEL2(" \u25C6 Governor Parameters"));
2400
+ SEP2();
2401
+ console.log(W2(` Address: ${G2(getGovernorAddress())}`));
2402
+ console.log(W2(` Voting Period: ${BOLD2(formatDurationLong(params.votingPeriod))}`));
2403
+ console.log(W2(` Execution Window: ${BOLD2(formatDurationLong(params.executionWindow))}`));
2404
+ console.log(W2(` Quorum: ${BOLD2(`${Number(params.quorumBps) / 100}%`)}`));
2405
+ console.log(W2(` Max Performance Fee: ${BOLD2(`${Number(params.maxPerformanceFeeBps) / 100}%`)}`));
2406
+ console.log(W2(` Max Strategy Duration:${BOLD2(` ${formatDurationLong(params.maxStrategyDuration)}`)}`));
2407
+ console.log(W2(` Cooldown Period: ${BOLD2(formatDurationLong(params.cooldownPeriod))}`));
2408
+ console.log();
2409
+ console.log(LABEL2(` Registered Vaults (${vaults.length})`));
2410
+ if (vaults.length === 0) {
2411
+ console.log(DIM2(" (none)"));
2412
+ } else {
2413
+ for (const v of vaults) {
2414
+ console.log(W2(` ${G2(v)}`));
2415
+ }
2416
+ }
2417
+ SEP2();
2418
+ console.log();
2419
+ } catch (err) {
2420
+ spinner.fail("Failed to load governor info");
2421
+ console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
2422
+ process.exit(1);
2423
+ }
2424
+ });
2425
+ governor.command("set-voting-period").description("Set the voting period (owner only)").requiredOption("--seconds <n>", "New voting period in seconds").action(async (opts) => {
2426
+ const spinner = ora6("Setting voting period...").start();
2427
+ try {
2428
+ const hash = await setVotingPeriod(parseBigIntArg(opts.seconds, "seconds"));
2429
+ spinner.succeed(G2(`Voting period updated to ${opts.seconds}s`));
2430
+ console.log(DIM2(` ${getExplorerUrl(hash)}`));
2431
+ } catch (err) {
2432
+ spinner.fail("Failed to set voting period");
2433
+ console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
2434
+ process.exit(1);
2435
+ }
2436
+ });
2437
+ governor.command("set-execution-window").description("Set the execution window (owner only)").requiredOption("--seconds <n>", "New execution window in seconds").action(async (opts) => {
2438
+ const spinner = ora6("Setting execution window...").start();
2439
+ try {
2440
+ const hash = await setExecutionWindow(parseBigIntArg(opts.seconds, "seconds"));
2441
+ spinner.succeed(G2(`Execution window updated to ${opts.seconds}s`));
2442
+ console.log(DIM2(` ${getExplorerUrl(hash)}`));
2443
+ } catch (err) {
2444
+ spinner.fail("Failed to set execution window");
2445
+ console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
2446
+ process.exit(1);
2447
+ }
2448
+ });
2449
+ governor.command("set-quorum").description("Set the quorum threshold in bps (owner only)").requiredOption("--bps <n>", "New quorum in bps (e.g. 4000 = 40%)").action(async (opts) => {
2450
+ const spinner = ora6("Setting quorum...").start();
2451
+ try {
2452
+ const hash = await setQuorumBps(parseBigIntArg(opts.bps, "bps"));
2453
+ spinner.succeed(G2(`Quorum updated to ${Number(opts.bps) / 100}%`));
2454
+ console.log(DIM2(` ${getExplorerUrl(hash)}`));
2455
+ } catch (err) {
2456
+ spinner.fail("Failed to set quorum");
2457
+ console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
2458
+ process.exit(1);
2459
+ }
2460
+ });
2461
+ governor.command("set-max-fee").description("Set the max performance fee in bps (owner only)").requiredOption("--bps <n>", "New max fee in bps (e.g. 3000 = 30%)").action(async (opts) => {
2462
+ const spinner = ora6("Setting max fee...").start();
2463
+ try {
2464
+ const hash = await setMaxPerformanceFeeBps(parseBigIntArg(opts.bps, "bps"));
2465
+ spinner.succeed(G2(`Max performance fee updated to ${Number(opts.bps) / 100}%`));
2466
+ console.log(DIM2(` ${getExplorerUrl(hash)}`));
2467
+ } catch (err) {
2468
+ spinner.fail("Failed to set max fee");
2469
+ console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
2470
+ process.exit(1);
2471
+ }
2472
+ });
2473
+ governor.command("set-max-duration").description("Set the max strategy duration in seconds (owner only)").requiredOption("--seconds <n>", "New max duration in seconds").action(async (opts) => {
2474
+ const spinner = ora6("Setting max duration...").start();
2475
+ try {
2476
+ const hash = await setMaxStrategyDuration(parseBigIntArg(opts.seconds, "seconds"));
2477
+ spinner.succeed(G2(`Max strategy duration updated to ${opts.seconds}s`));
2478
+ console.log(DIM2(` ${getExplorerUrl(hash)}`));
2479
+ } catch (err) {
2480
+ spinner.fail("Failed to set max duration");
2481
+ console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
2482
+ process.exit(1);
2483
+ }
2484
+ });
2485
+ governor.command("set-cooldown").description("Set the cooldown period in seconds (owner only)").requiredOption("--seconds <n>", "New cooldown in seconds").action(async (opts) => {
2486
+ const spinner = ora6("Setting cooldown...").start();
2487
+ try {
2488
+ const hash = await setCooldownPeriod(parseBigIntArg(opts.seconds, "seconds"));
2489
+ spinner.succeed(G2(`Cooldown period updated to ${opts.seconds}s`));
2490
+ console.log(DIM2(` ${getExplorerUrl(hash)}`));
2491
+ } catch (err) {
2492
+ spinner.fail("Failed to set cooldown");
2493
+ console.error(chalk6.red(err instanceof Error ? err.message : String(err)));
2494
+ process.exit(1);
2495
+ }
2496
+ });
2497
+ }
2498
+
2499
+ // src/index.ts
2500
+ try {
2501
+ loadDotenv();
2502
+ } catch {
2503
+ }
2504
+ async function loadXmtp() {
2505
+ return import("./xmtp-S4VRXMFK.js");
2506
+ }
2507
+ var G3 = chalk7.green;
2508
+ var W3 = chalk7.white;
2509
+ var DIM3 = chalk7.gray;
2510
+ var BOLD3 = chalk7.white.bold;
2511
+ var LABEL3 = chalk7.green.bold;
2512
+ var SEP3 = () => console.log(DIM3("\u2500".repeat(60)));
1691
2513
  function resolveVault(opts) {
1692
2514
  if (opts.vault) {
1693
2515
  setVaultAddress(opts.vault);
1694
2516
  }
1695
2517
  }
1696
2518
  var program = new Command();
1697
- program.name("sherwood").description("CLI for agent-managed investment syndicates").version("0.1.0").option("--testnet", "Use Base Sepolia testnet instead of Base mainnet", false).hook("preAction", (thisCommand) => {
2519
+ program.name("sherwood").description("CLI for agent-managed investment syndicates").version("0.1.0").addOption(
2520
+ new Option("--chain <network>", "Target network").choices(VALID_NETWORKS).default("base")
2521
+ ).option("--testnet", "Alias for --chain base-sepolia (deprecated)", false).hook("preAction", (thisCommand) => {
1698
2522
  const opts = thisCommand.optsWithGlobals();
1699
- setNetwork(opts.testnet ? "base-sepolia" : "base");
2523
+ let network = opts.chain;
1700
2524
  if (opts.testnet) {
1701
- console.log(chalk5.yellow("[testnet] Base Sepolia"));
2525
+ if (network !== "base") {
2526
+ console.warn(
2527
+ chalk7.yellow("[warn] --testnet ignored, --chain takes precedence")
2528
+ );
2529
+ } else {
2530
+ network = "base-sepolia";
2531
+ }
2532
+ }
2533
+ setNetwork(network);
2534
+ if (getNetwork() !== "base") {
2535
+ console.log(chalk7.yellow(`[${getNetwork()}]`));
1702
2536
  }
1703
2537
  });
1704
2538
  var syndicate = program.command("syndicate");
1705
- syndicate.command("create").description("Create a new syndicate via the factory (interactive)").option("--subdomain <name>", "ENS subdomain (skip prompt)").option("--name <name>", "Syndicate name (skip prompt)").option("--agent-id <id>", "ERC-8004 agent identity token ID (skip prompt)").option("--asset <symbol-or-address>", "Vault asset: USDC, WETH, or a token address").option("--description <text>", "Short description").option("--metadata-uri <uri>", "Override metadata URI (skip IPFS upload)").option("--open-deposits", "Allow anyone to deposit (no whitelist)").option("--public-chat", "Enable dashboard spectator mode", false).action(async (opts) => {
2539
+ syndicate.command("create").description("Create a new syndicate via the factory (interactive)").option("--subdomain <name>", "ENS subdomain (skip prompt)").option("--name <name>", "Syndicate name (skip prompt)").option("--agent-id <id>", "ERC-8004 agent identity token ID (skip prompt)").option("--asset <symbol-or-address>", "Vault asset: USDC, WETH, or a token address").option("--description <text>", "Short description").option("--metadata-uri <uri>", "Override metadata URI (skip IPFS upload)").option("--open-deposits", "Allow anyone to deposit (no whitelist)").option("--public-chat", "Enable dashboard spectator mode", false).option("-y, --yes", "Skip confirmation prompt (non-interactive mode)", false).action(async (opts) => {
1706
2540
  try {
1707
2541
  console.log();
1708
- console.log(LABEL(" \u25C6 Create Syndicate"));
1709
- SEP();
2542
+ console.log(LABEL3(" \u25C6 Create Syndicate"));
2543
+ SEP3();
1710
2544
  const wallet = getAccount();
1711
- console.log(DIM(` Wallet: ${wallet.address}`));
1712
- console.log(DIM(` Network: ${getChain().name}`));
1713
- SEP();
2545
+ console.log(DIM3(` Wallet: ${wallet.address}`));
2546
+ console.log(DIM3(` Network: ${getChain().name}`));
2547
+ SEP3();
1714
2548
  const savedAgentId = getAgentId();
1715
- const name = opts.name || await input({
1716
- message: G("Syndicate name"),
2549
+ const nonInteractive = opts.yes;
2550
+ const name = opts.name || (nonInteractive ? (() => {
2551
+ throw new Error("--name is required in non-interactive mode (-y)");
2552
+ })() : await input({
2553
+ message: G3("Syndicate name"),
1717
2554
  validate: (v) => v.length > 0 || "Name is required"
1718
- });
1719
- const subdomain = opts.subdomain || await input({
1720
- message: G("ENS subdomain"),
2555
+ }));
2556
+ const subdomain = opts.subdomain || (nonInteractive ? name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") : await input({
2557
+ message: G3("ENS subdomain"),
1721
2558
  default: name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""),
1722
2559
  validate: (v) => v.length >= 3 || "Must be at least 3 characters"
1723
- });
1724
- const description = opts.description || await input({
1725
- message: G("Description"),
2560
+ }));
2561
+ const description = opts.description || (nonInteractive ? `${name} \u2014 a Sherwood syndicate` : await input({
2562
+ message: G3("Description"),
1726
2563
  default: `${name} \u2014 a Sherwood syndicate`
1727
- });
1728
- const agentIdStr = opts.agentId || (savedAgentId ? await input({ message: G("Agent ID (ERC-8004)"), default: String(savedAgentId) }) : await input({ message: G("Agent ID (ERC-8004)"), validate: (v) => /^\d+$/.test(v) || "Must be a number" }));
1729
- const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : await confirm({
1730
- message: G("Open deposits? (anyone can deposit)"),
2564
+ }));
2565
+ const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message: G3("Agent ID (ERC-8004)"), default: String(savedAgentId) }) : nonInteractive ? "0" : await input({ message: G3("Agent ID (ERC-8004)"), validate: (v) => /^\d+$/.test(v) || "Must be a number" }));
2566
+ const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : nonInteractive ? true : await confirm({
2567
+ message: G3("Open deposits? (anyone can deposit)"),
1731
2568
  default: true
1732
2569
  });
1733
2570
  const ASSET_SYMBOLS = {
@@ -1743,12 +2580,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
1743
2580
  asset = opts.asset;
1744
2581
  } else {
1745
2582
  const supported = Object.keys(ASSET_SYMBOLS).join(", ");
1746
- console.error(chalk5.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
2583
+ console.error(chalk7.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
1747
2584
  process.exit(1);
1748
2585
  }
2586
+ } else if (nonInteractive) {
2587
+ asset = TOKENS().USDC !== "0x0000000000000000000000000000000000000000" ? ASSET_SYMBOLS.USDC : ASSET_SYMBOLS.WETH;
1749
2588
  } else {
1750
2589
  const assetChoice = await select({
1751
- message: G("Vault asset (what token do depositors provide?)"),
2590
+ message: G3("Vault asset (what token do depositors provide?)"),
1752
2591
  choices: [
1753
2592
  { name: "USDC", value: "USDC", description: "USD Coin (6 decimals)" },
1754
2593
  { name: "WETH", value: "WETH", description: "Wrapped Ether (18 decimals)" }
@@ -1763,24 +2602,26 @@ syndicate.command("create").description("Create a new syndicate via the factory
1763
2602
  ]);
1764
2603
  const symbol = `sw${assetSymbol}`;
1765
2604
  console.log();
1766
- console.log(LABEL(" \u25C6 Review"));
1767
- SEP();
1768
- console.log(W(` Name: ${BOLD(name)}`));
1769
- console.log(W(` ENS: ${G(`${subdomain}.sherwoodagent.eth`)}`));
1770
- console.log(W(` Description: ${DIM(description)}`));
1771
- console.log(W(` Agent ID: #${agentIdStr}`));
1772
- console.log(W(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
1773
- console.log(W(` Share token: ${symbol}`));
1774
- console.log(W(` Open deposits: ${openDeposits ? G("yes") : chalk5.red("no (whitelist)")}`));
1775
- SEP();
1776
- const go = await confirm({ message: G("Deploy syndicate?"), default: true });
1777
- if (!go) {
1778
- console.log(DIM(" Cancelled."));
1779
- return;
2605
+ console.log(LABEL3(" \u25C6 Review"));
2606
+ SEP3();
2607
+ console.log(W3(` Name: ${BOLD3(name)}`));
2608
+ console.log(W3(` ENS: ${G3(`${subdomain}.sherwoodagent.eth`)}`));
2609
+ console.log(W3(` Description: ${DIM3(description)}`));
2610
+ console.log(W3(` Agent ID: #${agentIdStr}`));
2611
+ console.log(W3(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
2612
+ console.log(W3(` Share token: ${symbol}`));
2613
+ console.log(W3(` Open deposits: ${openDeposits ? G3("yes") : chalk7.red("no (whitelist)")}`));
2614
+ SEP3();
2615
+ if (!nonInteractive) {
2616
+ const go = await confirm({ message: G3("Deploy syndicate?"), default: true });
2617
+ if (!go) {
2618
+ console.log(DIM3(" Cancelled."));
2619
+ return;
2620
+ }
1780
2621
  }
1781
2622
  let metadataURI = opts.metadataUri || "";
1782
2623
  if (!metadataURI) {
1783
- const spinner2 = ora5({ text: W("Uploading metadata to IPFS..."), color: "green" }).start();
2624
+ const spinner2 = ora7({ text: W3("Uploading metadata to IPFS..."), color: "green" }).start();
1784
2625
  try {
1785
2626
  const metadata = {
1786
2627
  schema: "sherwood/syndicate/v1",
@@ -1795,14 +2636,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
1795
2636
  links: {}
1796
2637
  };
1797
2638
  metadataURI = await uploadMetadata(metadata);
1798
- spinner2.succeed(G(`Metadata pinned: ${DIM(metadataURI)}`));
2639
+ spinner2.succeed(G3(`Metadata pinned: ${DIM3(metadataURI)}`));
1799
2640
  } catch (err) {
1800
- spinner2.warn(chalk5.yellow(`IPFS upload failed \u2014 using inline metadata`));
2641
+ spinner2.warn(chalk7.yellow(`IPFS upload failed \u2014 using inline metadata`));
1801
2642
  const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
1802
2643
  metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
1803
2644
  }
1804
2645
  }
1805
- const spinner = ora5({ text: W("Deploying vault via factory..."), color: "green" }).start();
2646
+ const spinner = ora7({ text: W3("Deploying vault via factory..."), color: "green" }).start();
1806
2647
  const result = await createSyndicate({
1807
2648
  creatorAgentId: BigInt(agentIdStr),
1808
2649
  metadataURI,
@@ -1813,21 +2654,19 @@ syndicate.command("create").description("Create a new syndicate via the factory
1813
2654
  subdomain
1814
2655
  });
1815
2656
  setChainContract(getChain().id, "vault", result.vault);
1816
- spinner.text = W("Registering creator as agent...");
2657
+ spinner.text = W3("Registering creator as agent...");
1817
2658
  try {
1818
2659
  setVaultAddress(result.vault);
1819
2660
  const creatorAddress = getAccount().address;
1820
2661
  await registerAgent(
1821
2662
  BigInt(agentIdStr),
1822
- creatorAddress,
1823
- // pkp = creator EOA (direct execution)
1824
2663
  creatorAddress
1825
- // operator = creator EOA
2664
+ // agentAddress = creator EOA (direct execution)
1826
2665
  );
1827
2666
  } catch (regErr) {
1828
- console.warn(chalk5.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
2667
+ console.warn(chalk7.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
1829
2668
  }
1830
- spinner.text = W("Setting up chat...");
2669
+ spinner.text = W3("Setting up chat...");
1831
2670
  try {
1832
2671
  const xmtp = await loadXmtp();
1833
2672
  const xmtpClient = await xmtp.getXmtpClient();
@@ -1835,30 +2674,30 @@ syndicate.command("create").description("Create a new syndicate via the factory
1835
2674
  await setTextRecord(subdomain, "xmtpGroupId", groupId, result.vault);
1836
2675
  cacheGroupId(subdomain, groupId);
1837
2676
  } catch {
1838
- console.warn(chalk5.yellow("\n \u26A0 Could not create XMTP chat group"));
1839
- console.warn(chalk5.dim(` Recover later with: sherwood chat ${subdomain} init`));
2677
+ console.warn(chalk7.yellow("\n \u26A0 Could not create XMTP chat group"));
2678
+ console.warn(chalk7.dim(` Recover later with: sherwood chat ${subdomain} init`));
1840
2679
  }
1841
2680
  spinner.stop();
1842
2681
  console.log();
1843
- console.log(LABEL(" \u25C6 Syndicate Created"));
1844
- SEP();
1845
- console.log(W(` ID: ${G(`#${result.syndicateId}`)}`));
1846
- console.log(W(` Vault: ${G(result.vault)}`));
1847
- console.log(W(` ENS: ${G(`${subdomain}.sherwoodagent.eth`)}`));
1848
- console.log(W(` Metadata: ${DIM(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
1849
- console.log(W(` Explorer: ${DIM(getExplorerUrl(result.hash))}`));
1850
- console.log(W(` Chat: ${DIM(`sherwood chat ${subdomain}`)}`));
1851
- SEP();
1852
- console.log(G(" \u2713 Vault saved to ~/.sherwood/config.json"));
2682
+ console.log(LABEL3(" \u25C6 Syndicate Created"));
2683
+ SEP3();
2684
+ console.log(W3(` ID: ${G3(`#${result.syndicateId}`)}`));
2685
+ console.log(W3(` Vault: ${G3(result.vault)}`));
2686
+ console.log(W3(` ENS: ${G3(`${subdomain}.sherwoodagent.eth`)}`));
2687
+ console.log(W3(` Metadata: ${DIM3(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
2688
+ console.log(W3(` Explorer: ${DIM3(getExplorerUrl(result.hash))}`));
2689
+ console.log(W3(` Chat: ${DIM3(`sherwood chat ${subdomain}`)}`));
2690
+ SEP3();
2691
+ console.log(G3(" \u2713 Vault saved to ~/.sherwood/config.json"));
1853
2692
  console.log();
1854
2693
  } catch (err) {
1855
- console.error(chalk5.red(`
2694
+ console.error(chalk7.red(`
1856
2695
  \u2716 ${err instanceof Error ? err.message : String(err)}`));
1857
2696
  process.exit(1);
1858
2697
  }
1859
2698
  });
1860
2699
  syndicate.command("list").description("List active syndicates (queries subgraph, falls back to on-chain)").option("--creator <address>", "Filter by creator address").action(async (opts) => {
1861
- const spinner = ora5("Loading syndicates...").start();
2700
+ const spinner = ora7("Loading syndicates...").start();
1862
2701
  try {
1863
2702
  let syndicates;
1864
2703
  if (process.env.SUBGRAPH_URL) {
@@ -1877,65 +2716,65 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
1877
2716
  }
1878
2717
  spinner.stop();
1879
2718
  if (syndicates.length === 0) {
1880
- console.log(chalk5.dim("No active syndicates found."));
2719
+ console.log(chalk7.dim("No active syndicates found."));
1881
2720
  return;
1882
2721
  }
1883
2722
  console.log();
1884
- console.log(chalk5.bold(`Active Syndicates (${syndicates.length})`));
2723
+ console.log(chalk7.bold(`Active Syndicates (${syndicates.length})`));
1885
2724
  if (!process.env.SUBGRAPH_URL) {
1886
- console.log(chalk5.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
2725
+ console.log(chalk7.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
1887
2726
  }
1888
- console.log(chalk5.dim("\u2500".repeat(70)));
2727
+ console.log(chalk7.dim("\u2500".repeat(70)));
1889
2728
  for (const s of syndicates) {
1890
2729
  const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
1891
2730
  const date = new Date(ts * 1e3).toLocaleDateString();
1892
2731
  const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
1893
- console.log(` #${s.id} ${chalk5.bold(ensName || String(s.vault))}`);
1894
- if (ensName) console.log(` Vault: ${chalk5.cyan(String(s.vault))}`);
2732
+ console.log(` #${s.id} ${chalk7.bold(ensName || String(s.vault))}`);
2733
+ if (ensName) console.log(` Vault: ${chalk7.cyan(String(s.vault))}`);
1895
2734
  console.log(` Creator: ${s.creator}`);
1896
2735
  console.log(` Created: ${date}`);
1897
2736
  if (s.totalDeposits) {
1898
2737
  console.log(` Deposits: ${s.totalDeposits} USDC`);
1899
2738
  }
1900
2739
  if (s.metadataURI) {
1901
- console.log(` Metadata: ${chalk5.dim(s.metadataURI)}`);
2740
+ console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
1902
2741
  }
1903
2742
  console.log();
1904
2743
  }
1905
2744
  } catch (err) {
1906
2745
  spinner.fail("Failed to load syndicates");
1907
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2746
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
1908
2747
  process.exit(1);
1909
2748
  }
1910
2749
  });
1911
2750
  syndicate.command("info").description("Display syndicate details by ID").argument("<id>", "Syndicate ID").action(async (idStr) => {
1912
- const spinner = ora5("Loading syndicate info...").start();
2751
+ const spinner = ora7("Loading syndicate info...").start();
1913
2752
  try {
1914
2753
  const id = BigInt(idStr);
1915
2754
  const info = await getSyndicate(id);
1916
2755
  spinner.stop();
1917
2756
  if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
1918
- console.log(chalk5.red(`Syndicate #${id} not found.`));
2757
+ console.log(chalk7.red(`Syndicate #${id} not found.`));
1919
2758
  process.exit(1);
1920
2759
  }
1921
2760
  const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
1922
2761
  console.log();
1923
- console.log(chalk5.bold(`Syndicate #${info.id}`));
1924
- console.log(chalk5.dim("\u2500".repeat(40)));
2762
+ console.log(chalk7.bold(`Syndicate #${info.id}`));
2763
+ console.log(chalk7.dim("\u2500".repeat(40)));
1925
2764
  if (info.subdomain) {
1926
- console.log(` ENS: ${chalk5.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
2765
+ console.log(` ENS: ${chalk7.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
1927
2766
  }
1928
- console.log(` Vault: ${chalk5.cyan(info.vault)}`);
2767
+ console.log(` Vault: ${chalk7.cyan(info.vault)}`);
1929
2768
  console.log(` Creator: ${info.creator}`);
1930
2769
  console.log(` Created: ${date}`);
1931
- console.log(` Active: ${info.active ? chalk5.green("yes") : chalk5.red("no")}`);
2770
+ console.log(` Active: ${info.active ? chalk7.green("yes") : chalk7.red("no")}`);
1932
2771
  if (info.metadataURI) {
1933
- console.log(` Metadata: ${chalk5.dim(info.metadataURI)}`);
2772
+ console.log(` Metadata: ${chalk7.dim(info.metadataURI)}`);
1934
2773
  }
1935
2774
  setVaultAddress(info.vault);
1936
2775
  const vaultInfo = await getVaultInfo();
1937
2776
  console.log();
1938
- console.log(chalk5.bold(" Vault Stats"));
2777
+ console.log(chalk7.bold(" Vault Stats"));
1939
2778
  console.log(` Total Assets: ${vaultInfo.totalAssets}`);
1940
2779
  console.log(` Agent Count: ${vaultInfo.agentCount}`);
1941
2780
  console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
@@ -1943,12 +2782,12 @@ syndicate.command("info").description("Display syndicate details by ID").argumen
1943
2782
  console.log();
1944
2783
  } catch (err) {
1945
2784
  spinner.fail("Failed to load syndicate info");
1946
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2785
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
1947
2786
  process.exit(1);
1948
2787
  }
1949
2788
  });
1950
2789
  syndicate.command("update-metadata").description("Update syndicate metadata (creator only)").requiredOption("--id <id>", "Syndicate ID").option("--name <name>", "Syndicate name").option("--description <text>", "Short description").option("--uri <uri>", "Direct metadata URI (skips IPFS upload)").action(async (opts) => {
1951
- const spinner = ora5({ text: W("Loading syndicate..."), color: "green" }).start();
2790
+ const spinner = ora7({ text: W3("Loading syndicate..."), color: "green" }).start();
1952
2791
  try {
1953
2792
  const syndicateId = BigInt(opts.id);
1954
2793
  let metadataURI = opts.uri;
@@ -1960,7 +2799,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
1960
2799
  }
1961
2800
  const name = opts.name || info.subdomain;
1962
2801
  const description = opts.description || `${name} \u2014 a Sherwood syndicate on ${info.subdomain}.sherwoodagent.eth`;
1963
- spinner.text = W("Uploading metadata to IPFS...");
2802
+ spinner.text = W3("Uploading metadata to IPFS...");
1964
2803
  const metadata = {
1965
2804
  schema: "sherwood/syndicate/v1",
1966
2805
  name,
@@ -1971,46 +2810,46 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
1971
2810
  links: {}
1972
2811
  };
1973
2812
  metadataURI = await uploadMetadata(metadata);
1974
- spinner.text = W("Updating on-chain metadata...");
2813
+ spinner.text = W3("Updating on-chain metadata...");
1975
2814
  }
1976
2815
  const hash = await updateMetadata(syndicateId, metadataURI);
1977
- spinner.succeed(G(`Metadata updated`));
1978
- console.log(DIM(` IPFS: ${metadataURI}`));
1979
- console.log(DIM(` ${getExplorerUrl(hash)}`));
2816
+ spinner.succeed(G3(`Metadata updated`));
2817
+ console.log(DIM3(` IPFS: ${metadataURI}`));
2818
+ console.log(DIM3(` ${getExplorerUrl(hash)}`));
1980
2819
  } catch (err) {
1981
2820
  spinner.fail("Metadata update failed");
1982
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2821
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
1983
2822
  process.exit(1);
1984
2823
  }
1985
2824
  });
1986
2825
  syndicate.command("approve-depositor").description("Approve an address to deposit (owner only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--depositor <address>", "Address to approve").action(async (opts) => {
1987
2826
  resolveVault(opts);
1988
- const spinner = ora5("Approving depositor...").start();
2827
+ const spinner = ora7("Approving depositor...").start();
1989
2828
  try {
1990
2829
  const hash = await approveDepositor(opts.depositor);
1991
2830
  spinner.succeed(`Depositor approved: ${hash}`);
1992
- console.log(chalk5.dim(` ${getExplorerUrl(hash)}`));
2831
+ console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
1993
2832
  } catch (err) {
1994
2833
  spinner.fail("Approval failed");
1995
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2834
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
1996
2835
  process.exit(1);
1997
2836
  }
1998
2837
  });
1999
2838
  syndicate.command("remove-depositor").description("Remove an address from the depositor whitelist (owner only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--depositor <address>", "Address to remove").action(async (opts) => {
2000
2839
  resolveVault(opts);
2001
- const spinner = ora5("Removing depositor...").start();
2840
+ const spinner = ora7("Removing depositor...").start();
2002
2841
  try {
2003
2842
  const hash = await removeDepositor(opts.depositor);
2004
2843
  spinner.succeed(`Depositor removed: ${hash}`);
2005
- console.log(chalk5.dim(` ${getExplorerUrl(hash)}`));
2844
+ console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
2006
2845
  } catch (err) {
2007
2846
  spinner.fail("Removal failed");
2008
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2847
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2009
2848
  process.exit(1);
2010
2849
  }
2011
2850
  });
2012
- syndicate.command("add").description("Register an agent on a syndicate vault (creator only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--pkp <address>", "Agent PKP address").requiredOption("--eoa <address>", "Operator EOA address").action(async (opts) => {
2013
- const spinner = ora5("Verifying creator...").start();
2851
+ syndicate.command("add").description("Register an agent on a syndicate vault (creator only)").option("--vault <address>", "Vault address (default: from config)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--wallet <address>", "Agent wallet address").action(async (opts) => {
2852
+ const spinner = ora7("Verifying creator...").start();
2014
2853
  try {
2015
2854
  resolveVault(opts);
2016
2855
  const vaultAddress = getVaultAddress();
@@ -2023,35 +2862,34 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
2023
2862
  spinner.text = "Registering agent...";
2024
2863
  const hash = await registerAgent(
2025
2864
  BigInt(opts.agentId),
2026
- opts.pkp,
2027
- opts.eoa
2865
+ opts.wallet
2028
2866
  );
2029
2867
  spinner.succeed(`Agent registered: ${hash}`);
2030
- console.log(chalk5.dim(` ${getExplorerUrl(hash)}`));
2868
+ console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
2031
2869
  try {
2032
2870
  const xmtp = await loadXmtp();
2033
2871
  const xmtpClient = await xmtp.getXmtpClient();
2034
2872
  const group = await xmtp.getGroup(xmtpClient, subdomain);
2035
- await xmtp.addMember(group, opts.pkp);
2873
+ await xmtp.addMember(group, opts.wallet);
2036
2874
  await xmtp.sendEnvelope(group, {
2037
2875
  type: "AGENT_REGISTERED",
2038
- agent: { erc8004Id: Number(opts.agentId), address: opts.pkp },
2876
+ agent: { erc8004Id: Number(opts.agentId), address: opts.wallet },
2039
2877
  syndicate: subdomain,
2040
2878
  timestamp: Math.floor(Date.now() / 1e3)
2041
2879
  });
2042
- console.log(chalk5.dim(` Added to chat: ${subdomain}`));
2880
+ console.log(chalk7.dim(` Added to chat: ${subdomain}`));
2043
2881
  } catch {
2044
- console.warn(chalk5.yellow(" \u26A0 Could not add agent to chat group"));
2045
- console.warn(chalk5.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
2882
+ console.warn(chalk7.yellow(" \u26A0 Could not add agent to chat group"));
2883
+ console.warn(chalk7.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
2046
2884
  }
2047
2885
  } catch (err) {
2048
2886
  spinner.fail("Registration failed");
2049
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2887
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2050
2888
  process.exit(1);
2051
2889
  }
2052
2890
  });
2053
2891
  syndicate.command("join").description("Request to join a syndicate (creates an EAS attestation)").requiredOption("--subdomain <name>", "Syndicate subdomain to join").option("--message <text>", "Message to the creator", "Requesting to join your syndicate").action(async (opts) => {
2054
- const spinner = ora5("Resolving syndicate...").start();
2892
+ const spinner = ora7("Resolving syndicate...").start();
2055
2893
  try {
2056
2894
  const agentId = getAgentId();
2057
2895
  if (!agentId) {
@@ -2068,9 +2906,9 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
2068
2906
  try {
2069
2907
  const xmtp = await loadXmtp();
2070
2908
  await xmtp.getXmtpClient();
2071
- console.log(chalk5.dim(" XMTP identity ready"));
2909
+ console.log(chalk7.dim(" XMTP identity ready"));
2072
2910
  } catch {
2073
- console.warn(chalk5.yellow(" \u26A0 Could not initialize XMTP identity"));
2911
+ console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
2074
2912
  }
2075
2913
  return;
2076
2914
  }
@@ -2081,14 +2919,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
2081
2919
  );
2082
2920
  if (existingRequest) {
2083
2921
  spinner.succeed("You already have a pending join request for this syndicate");
2084
- console.log(chalk5.dim(` Attestation: ${existingRequest.uid}`));
2085
- console.log(chalk5.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
2922
+ console.log(chalk7.dim(` Attestation: ${existingRequest.uid}`));
2923
+ console.log(chalk7.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
2086
2924
  try {
2087
2925
  const xmtp = await loadXmtp();
2088
2926
  await xmtp.getXmtpClient();
2089
- console.log(chalk5.dim(" XMTP identity ready"));
2927
+ console.log(chalk7.dim(" XMTP identity ready"));
2090
2928
  } catch {
2091
- console.warn(chalk5.yellow(" \u26A0 Could not initialize XMTP identity"));
2929
+ console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity"));
2092
2930
  }
2093
2931
  return;
2094
2932
  }
@@ -2107,29 +2945,29 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
2107
2945
  spinner.succeed("Join request created (XMTP identity ready)");
2108
2946
  } catch {
2109
2947
  spinner.succeed("Join request created");
2110
- console.warn(chalk5.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
2948
+ console.warn(chalk7.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
2111
2949
  }
2112
2950
  console.log();
2113
- console.log(LABEL(" \u25C6 Join Request Submitted"));
2114
- SEP();
2115
- console.log(W(` Syndicate: ${G(`${opts.subdomain}.sherwoodagent.eth`)}`));
2116
- console.log(W(` Agent ID: #${agentId}`));
2117
- console.log(W(` Creator: ${DIM(syndicate2.creator)}`));
2118
- console.log(W(` Attestation: ${DIM(uid)}`));
2119
- console.log(W(` EAS Scan: ${DIM(getEasScanUrl(uid))}`));
2120
- console.log(W(` Explorer: ${DIM(getExplorerUrl(hash))}`));
2121
- SEP();
2122
- console.log(G(" \u2713 The creator can review with:"));
2123
- console.log(DIM(` sherwood syndicate requests --subdomain ${opts.subdomain}`));
2951
+ console.log(LABEL3(" \u25C6 Join Request Submitted"));
2952
+ SEP3();
2953
+ console.log(W3(` Syndicate: ${G3(`${opts.subdomain}.sherwoodagent.eth`)}`));
2954
+ console.log(W3(` Agent ID: #${agentId}`));
2955
+ console.log(W3(` Creator: ${DIM3(syndicate2.creator)}`));
2956
+ console.log(W3(` Attestation: ${DIM3(uid)}`));
2957
+ console.log(W3(` EAS Scan: ${DIM3(getEasScanUrl(uid))}`));
2958
+ console.log(W3(` Explorer: ${DIM3(getExplorerUrl(hash))}`));
2959
+ SEP3();
2960
+ console.log(G3(" \u2713 The creator can review with:"));
2961
+ console.log(DIM3(` sherwood syndicate requests --subdomain ${opts.subdomain}`));
2124
2962
  console.log();
2125
2963
  } catch (err) {
2126
2964
  spinner.fail("Join request failed");
2127
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
2965
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2128
2966
  process.exit(1);
2129
2967
  }
2130
2968
  });
2131
2969
  syndicate.command("requests").description("View pending join requests for a syndicate (creator only)").option("--subdomain <name>", "Syndicate subdomain").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
2132
- const spinner = ora5("Loading join requests...").start();
2970
+ const spinner = ora7("Loading join requests...").start();
2133
2971
  try {
2134
2972
  let creatorAddress;
2135
2973
  let subdomain;
@@ -2160,34 +2998,34 @@ syndicate.command("requests").description("View pending join requests for a synd
2160
2998
  );
2161
2999
  spinner.stop();
2162
3000
  if (requests.length === 0) {
2163
- console.log(DIM("\n No pending join requests.\n"));
3001
+ console.log(DIM3("\n No pending join requests.\n"));
2164
3002
  return;
2165
3003
  }
2166
3004
  console.log();
2167
- console.log(LABEL(` \u25C6 Pending Join Requests (${requests.length})`));
2168
- SEP();
3005
+ console.log(LABEL3(` \u25C6 Pending Join Requests (${requests.length})`));
3006
+ SEP3();
2169
3007
  for (let i = 0; i < requests.length; i++) {
2170
3008
  const req = requests[i];
2171
3009
  const date = new Date(req.time * 1e3).toLocaleString();
2172
- console.log(W(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM(`(${req.attester})`)}`));
2173
- console.log(DIM(` Message: "${req.decoded.message}"`));
2174
- console.log(DIM(` Requested: ${date}`));
2175
- console.log(DIM(` Attestation: ${req.uid}`));
3010
+ console.log(W3(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM3(`(${req.attester})`)}`));
3011
+ console.log(DIM3(` Message: "${req.decoded.message}"`));
3012
+ console.log(DIM3(` Requested: ${date}`));
3013
+ console.log(DIM3(` Attestation: ${req.uid}`));
2176
3014
  console.log();
2177
3015
  }
2178
- console.log(G(" To approve:"));
2179
- console.log(DIM(` sherwood syndicate approve --agent-id <id> --pkp <addr> --eoa <addr>`));
2180
- console.log(G(" To reject:"));
2181
- console.log(DIM(` sherwood syndicate reject --attestation <uid>`));
3016
+ console.log(G3(" To approve:"));
3017
+ console.log(DIM3(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
3018
+ console.log(G3(" To reject:"));
3019
+ console.log(DIM3(` sherwood syndicate reject --attestation <uid>`));
2182
3020
  console.log();
2183
3021
  } catch (err) {
2184
3022
  spinner.fail("Failed to load requests");
2185
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3023
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2186
3024
  process.exit(1);
2187
3025
  }
2188
3026
  });
2189
- syndicate.command("approve").description("Approve an agent join request (registers agent + creates EAS approval)").option("--vault <address>", "Vault address (default: from config)").option("--subdomain <name>", "Syndicate subdomain (alternative to --vault)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--pkp <address>", "Agent PKP address").requiredOption("--eoa <address>", "Operator EOA address").action(async (opts) => {
2190
- const spinner = ora5("Verifying creator...").start();
3027
+ syndicate.command("approve").description("Approve an agent join request (registers agent + creates EAS approval)").option("--vault <address>", "Vault address (default: from config)").option("--subdomain <name>", "Syndicate subdomain (alternative to --vault)").requiredOption("--agent-id <id>", "Agent's ERC-8004 identity token ID").requiredOption("--wallet <address>", "Agent wallet address").action(async (opts) => {
3028
+ const spinner = ora7("Verifying creator...").start();
2191
3029
  try {
2192
3030
  if (opts.subdomain && !opts.vault) {
2193
3031
  const syndicateInfo = await resolveSyndicate(opts.subdomain);
@@ -2206,14 +3044,13 @@ syndicate.command("approve").description("Approve an agent join request (registe
2206
3044
  try {
2207
3045
  const regHash = await registerAgent(
2208
3046
  BigInt(opts.agentId),
2209
- opts.pkp,
2210
- opts.eoa
3047
+ opts.wallet
2211
3048
  );
2212
- console.log(DIM(` Agent registered: ${getExplorerUrl(regHash)}`));
3049
+ console.log(DIM3(` Agent registered: ${getExplorerUrl(regHash)}`));
2213
3050
  } catch (regErr) {
2214
3051
  const msg = regErr instanceof Error ? regErr.message : String(regErr);
2215
3052
  if (msg.includes("0xe098d3ee") || msg.includes("AgentAlreadyRegistered")) {
2216
- console.log(DIM(" Agent already registered on vault \u2014 skipping"));
3053
+ console.log(DIM3(" Agent already registered on vault \u2014 skipping"));
2217
3054
  } else {
2218
3055
  throw regErr;
2219
3056
  }
@@ -2226,14 +3063,14 @@ syndicate.command("approve").description("Approve an agent join request (registe
2226
3063
  let approvalUid;
2227
3064
  if (alreadyApproved) {
2228
3065
  approvalUid = alreadyApproved.uid;
2229
- console.log(DIM(` Approval attestation already exists \u2014 skipping`));
3066
+ console.log(DIM3(` Approval attestation already exists \u2014 skipping`));
2230
3067
  } else {
2231
3068
  spinner.text = "Creating approval attestation...";
2232
3069
  const result = await createApproval(
2233
3070
  syndicateId,
2234
3071
  BigInt(opts.agentId),
2235
3072
  vaultAddress,
2236
- opts.eoa
3073
+ opts.wallet
2237
3074
  );
2238
3075
  approvalUid = result.uid;
2239
3076
  }
@@ -2242,46 +3079,45 @@ syndicate.command("approve").description("Approve an agent join request (registe
2242
3079
  const xmtp = await loadXmtp();
2243
3080
  const xmtpClient = await xmtp.getXmtpClient();
2244
3081
  const group = await xmtp.getGroup(xmtpClient, subdomain);
2245
- await xmtp.addMember(group, opts.pkp);
3082
+ await xmtp.addMember(group, opts.wallet);
2246
3083
  await xmtp.sendEnvelope(group, {
2247
3084
  type: "AGENT_REGISTERED",
2248
- agent: { erc8004Id: Number(opts.agentId), address: opts.pkp },
3085
+ agent: { erc8004Id: Number(opts.agentId), address: opts.wallet },
2249
3086
  syndicate: subdomain,
2250
3087
  timestamp: Math.floor(Date.now() / 1e3)
2251
3088
  });
2252
- console.log(DIM(` Added to chat: ${subdomain}`));
3089
+ console.log(DIM3(` Added to chat: ${subdomain}`));
2253
3090
  } catch {
2254
- console.warn(chalk5.yellow(" \u26A0 Could not add agent to chat group"));
2255
- console.warn(chalk5.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
3091
+ console.warn(chalk7.yellow(" \u26A0 Could not add agent to chat group"));
3092
+ console.warn(chalk7.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
2256
3093
  }
2257
3094
  spinner.succeed("Agent approved and registered");
2258
3095
  console.log();
2259
- console.log(LABEL(" \u25C6 Agent Approved"));
2260
- SEP();
2261
- console.log(W(` Agent ID: #${opts.agentId}`));
2262
- console.log(W(` PKP: ${G(opts.pkp)}`));
2263
- console.log(W(` EOA: ${G(opts.eoa)}`));
2264
- console.log(W(` Approval: ${DIM(approvalUid)}`));
2265
- console.log(W(` EAS Scan: ${DIM(getEasScanUrl(approvalUid))}`));
2266
- SEP();
3096
+ console.log(LABEL3(" \u25C6 Agent Approved"));
3097
+ SEP3();
3098
+ console.log(W3(` Agent ID: #${opts.agentId}`));
3099
+ console.log(W3(` Wallet: ${G3(opts.wallet)}`));
3100
+ console.log(W3(` Approval: ${DIM3(approvalUid)}`));
3101
+ console.log(W3(` EAS Scan: ${DIM3(getEasScanUrl(approvalUid))}`));
3102
+ SEP3();
2267
3103
  } catch (err) {
2268
3104
  spinner.fail("Approval failed");
2269
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3105
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2270
3106
  process.exit(1);
2271
3107
  }
2272
3108
  });
2273
3109
  syndicate.command("reject").description("Reject a join request by revoking its attestation").requiredOption("--attestation <uid>", "Join request attestation UID to revoke").action(async (opts) => {
2274
- const spinner = ora5("Revoking attestation...").start();
3110
+ const spinner = ora7("Revoking attestation...").start();
2275
3111
  try {
2276
3112
  const hash = await revokeAttestation(
2277
3113
  EAS_SCHEMAS().SYNDICATE_JOIN_REQUEST,
2278
3114
  opts.attestation
2279
3115
  );
2280
3116
  spinner.succeed("Join request rejected");
2281
- console.log(DIM(` ${getExplorerUrl(hash)}`));
3117
+ console.log(DIM3(` ${getExplorerUrl(hash)}`));
2282
3118
  } catch (err) {
2283
3119
  spinner.fail("Rejection failed");
2284
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3120
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2285
3121
  process.exit(1);
2286
3122
  }
2287
3123
  });
@@ -2290,39 +3126,39 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
2290
3126
  resolveVault(opts);
2291
3127
  const decimals = await getAssetDecimals();
2292
3128
  const amount = parseUnits8(opts.amount, decimals);
2293
- const spinner = ora5(`Depositing ${opts.amount}...`).start();
3129
+ const spinner = ora7(`Depositing ${opts.amount}...`).start();
2294
3130
  try {
2295
3131
  const hash = await deposit(amount);
2296
3132
  spinner.succeed(`Deposited: ${hash}`);
2297
- console.log(chalk5.dim(` ${getExplorerUrl(hash)}`));
3133
+ console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
2298
3134
  } catch (err) {
2299
3135
  spinner.fail("Deposit failed");
2300
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3136
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2301
3137
  process.exit(1);
2302
3138
  }
2303
3139
  });
2304
3140
  vaultCmd.command("ragequit").description("Withdraw all shares from a vault").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
2305
3141
  resolveVault(opts);
2306
- const spinner = ora5("Ragequitting...").start();
3142
+ const spinner = ora7("Ragequitting...").start();
2307
3143
  try {
2308
3144
  const hash = await ragequit();
2309
3145
  spinner.succeed(`Ragequit: ${hash}`);
2310
- console.log(chalk5.dim(` ${getExplorerUrl(hash)}`));
3146
+ console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
2311
3147
  } catch (err) {
2312
3148
  spinner.fail("Ragequit failed");
2313
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3149
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2314
3150
  process.exit(1);
2315
3151
  }
2316
3152
  });
2317
3153
  vaultCmd.command("info").description("Display vault state").option("--vault <address>", "Vault address (default: from config)").action(async (opts) => {
2318
3154
  resolveVault(opts);
2319
- const spinner = ora5("Loading vault info...").start();
3155
+ const spinner = ora7("Loading vault info...").start();
2320
3156
  try {
2321
3157
  const info = await getVaultInfo();
2322
3158
  spinner.stop();
2323
3159
  console.log();
2324
- console.log(chalk5.bold("Vault Info"));
2325
- console.log(chalk5.dim("\u2500".repeat(40)));
3160
+ console.log(chalk7.bold("Vault Info"));
3161
+ console.log(chalk7.dim("\u2500".repeat(40)));
2326
3162
  console.log(` Address: ${info.address}`);
2327
3163
  console.log(` Total Assets: ${info.totalAssets}`);
2328
3164
  console.log(` Agent Count: ${info.agentCount}`);
@@ -2331,85 +3167,85 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
2331
3167
  console.log();
2332
3168
  } catch (err) {
2333
3169
  spinner.fail("Failed to load vault info");
2334
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3170
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2335
3171
  process.exit(1);
2336
3172
  }
2337
3173
  });
2338
3174
  vaultCmd.command("balance").description("Show LP share balance and asset value").option("--vault <address>", "Vault address (default: from config)").option("--address <address>", "Address to check (default: your wallet)").action(async (opts) => {
2339
3175
  resolveVault(opts);
2340
- const spinner = ora5("Loading balance...").start();
3176
+ const spinner = ora7("Loading balance...").start();
2341
3177
  try {
2342
3178
  const balance = await getBalance(opts.address);
2343
3179
  spinner.stop();
2344
3180
  console.log();
2345
- console.log(chalk5.bold("LP Position"));
2346
- console.log(chalk5.dim("\u2500".repeat(40)));
3181
+ console.log(chalk7.bold("LP Position"));
3182
+ console.log(chalk7.dim("\u2500".repeat(40)));
2347
3183
  console.log(` Shares: ${balance.shares.toString()}`);
2348
3184
  console.log(` Asset Value: ${balance.assetsValue}`);
2349
3185
  console.log(` % of Vault: ${balance.percentOfVault}`);
2350
3186
  console.log();
2351
3187
  } catch (err) {
2352
3188
  spinner.fail("Failed to load balance");
2353
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3189
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2354
3190
  process.exit(1);
2355
3191
  }
2356
3192
  });
2357
3193
  var strategy = program.command("strategy");
2358
3194
  strategy.command("list").description("List registered strategies").option("--type <id>", "Filter by strategy type").action(async (opts) => {
2359
- const spinner = ora5("Loading strategies...").start();
3195
+ const spinner = ora7("Loading strategies...").start();
2360
3196
  try {
2361
3197
  const strategies = await listStrategies(
2362
3198
  opts.type ? BigInt(opts.type) : void 0
2363
3199
  );
2364
3200
  spinner.stop();
2365
3201
  if (strategies.length === 0) {
2366
- console.log(chalk5.dim("No strategies registered."));
3202
+ console.log(chalk7.dim("No strategies registered."));
2367
3203
  return;
2368
3204
  }
2369
3205
  console.log();
2370
- console.log(chalk5.bold(`Strategies (${strategies.length})`));
2371
- console.log(chalk5.dim("\u2500".repeat(70)));
3206
+ console.log(chalk7.bold(`Strategies (${strategies.length})`));
3207
+ console.log(chalk7.dim("\u2500".repeat(70)));
2372
3208
  for (const s of strategies) {
2373
- const status = s.active ? chalk5.green("active") : chalk5.red("inactive");
2374
- console.log(` #${s.id} ${chalk5.bold(s.name)} [type: ${s.strategyTypeId}] ${status}`);
3209
+ const status = s.active ? chalk7.green("active") : chalk7.red("inactive");
3210
+ console.log(` #${s.id} ${chalk7.bold(s.name)} [type: ${s.strategyTypeId}] ${status}`);
2375
3211
  console.log(` Creator: ${s.creator}`);
2376
3212
  console.log(` Implementation: ${s.implementation}`);
2377
3213
  if (s.metadataURI) {
2378
- console.log(` Metadata: ${chalk5.dim(s.metadataURI)}`);
3214
+ console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
2379
3215
  }
2380
3216
  console.log();
2381
3217
  }
2382
3218
  } catch (err) {
2383
3219
  spinner.fail("Failed to load strategies");
2384
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3220
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2385
3221
  process.exit(1);
2386
3222
  }
2387
3223
  });
2388
3224
  strategy.command("info").description("Show strategy details").argument("<id>", "Strategy ID").action(async (idStr) => {
2389
- const spinner = ora5("Loading strategy...").start();
3225
+ const spinner = ora7("Loading strategy...").start();
2390
3226
  try {
2391
3227
  const s = await getStrategy(BigInt(idStr));
2392
3228
  spinner.stop();
2393
3229
  console.log();
2394
- console.log(chalk5.bold(`Strategy #${s.id}`));
2395
- console.log(chalk5.dim("\u2500".repeat(40)));
3230
+ console.log(chalk7.bold(`Strategy #${s.id}`));
3231
+ console.log(chalk7.dim("\u2500".repeat(40)));
2396
3232
  console.log(` Name: ${s.name}`);
2397
3233
  console.log(` Type: ${s.strategyTypeId}`);
2398
- console.log(` Active: ${s.active ? chalk5.green("yes") : chalk5.red("no")}`);
3234
+ console.log(` Active: ${s.active ? chalk7.green("yes") : chalk7.red("no")}`);
2399
3235
  console.log(` Creator: ${s.creator}`);
2400
3236
  console.log(` Implementation: ${s.implementation}`);
2401
3237
  if (s.metadataURI) {
2402
- console.log(` Metadata: ${chalk5.dim(s.metadataURI)}`);
3238
+ console.log(` Metadata: ${chalk7.dim(s.metadataURI)}`);
2403
3239
  }
2404
3240
  console.log();
2405
3241
  } catch (err) {
2406
3242
  spinner.fail("Failed to load strategy");
2407
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3243
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2408
3244
  process.exit(1);
2409
3245
  }
2410
3246
  });
2411
3247
  strategy.command("register").description("Register a new strategy on-chain").requiredOption("--implementation <address>", "Strategy contract address").requiredOption("--type <id>", "Strategy type ID").requiredOption("--name <name>", "Strategy name").option("--metadata <uri>", "Metadata URI (IPFS/Arweave)", "").action(async (opts) => {
2412
- const spinner = ora5("Registering strategy...").start();
3248
+ const spinner = ora7("Registering strategy...").start();
2413
3249
  try {
2414
3250
  const hash = await registerStrategy(
2415
3251
  opts.implementation,
@@ -2418,10 +3254,10 @@ strategy.command("register").description("Register a new strategy on-chain").req
2418
3254
  opts.metadata
2419
3255
  );
2420
3256
  spinner.succeed(`Strategy registered: ${hash}`);
2421
- console.log(chalk5.dim(` ${getExplorerUrl(hash)}`));
3257
+ console.log(chalk7.dim(` ${getExplorerUrl(hash)}`));
2422
3258
  } catch (err) {
2423
3259
  spinner.fail("Registration failed");
2424
- console.error(chalk5.red(err instanceof Error ? err.message : String(err)));
3260
+ console.error(chalk7.red(err instanceof Error ? err.message : String(err)));
2425
3261
  process.exit(1);
2426
3262
  }
2427
3263
  });
@@ -2430,7 +3266,7 @@ strategy.command("run").description("Execute the levered swap strategy").option(
2430
3266
  await runLeveredSwap(opts);
2431
3267
  });
2432
3268
  program.command("providers").description("List available DeFi providers").action(async () => {
2433
- const { MessariProvider, NansenProvider } = await import("./research-5QALNYVS.js");
3269
+ const { MessariProvider, NansenProvider } = await import("./research-ZR7HXITG.js");
2434
3270
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
2435
3271
  for (const p of providers) {
2436
3272
  const info = p.info();
@@ -2441,57 +3277,70 @@ ${info.name} (${info.type})`);
2441
3277
  }
2442
3278
  });
2443
3279
  try {
2444
- const { registerChatCommands } = await import("./chat-BSLMAYM5.js");
3280
+ const { registerChatCommands } = await import("./chat-BNYWD3EL.js");
2445
3281
  registerChatCommands(program);
2446
3282
  } catch {
2447
3283
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
2448
- console.error(chalk5.red("XMTP CLI not available."));
2449
- console.error(chalk5.dim("Install with: npm install -g @xmtp/cli"));
2450
- console.error(chalk5.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
3284
+ console.error(chalk7.red("XMTP CLI not available."));
3285
+ console.error(chalk7.dim("Install with: npm install -g @xmtp/cli"));
3286
+ console.error(chalk7.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
2451
3287
  process.exit(1);
2452
3288
  });
2453
3289
  }
2454
- var { registerSessionCommands } = await import("./session-XUOMZWOT.js");
3290
+ var { registerSessionCommands } = await import("./session-QQSHCGNK.js");
2455
3291
  registerSessionCommands(program);
2456
3292
  registerVeniceCommands(program);
2457
3293
  registerAllowanceCommands(program);
2458
3294
  registerIdentityCommands(program);
2459
- var { registerResearchCommands } = await import("./research-ILKLSHVP.js");
3295
+ registerProposalCommands(program);
3296
+ registerGovernorCommands(program);
3297
+ var { registerResearchCommands } = await import("./research-57SKO27M.js");
2460
3298
  registerResearchCommands(program);
2461
3299
  var configCmd = program.command("config");
2462
- configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").action((opts) => {
3300
+ configCmd.command("set").description("Save settings to ~/.sherwood/config.json (persists across sessions)").option("--private-key <key>", "Wallet private key (0x-prefixed)").option("--vault <address>", "Default SyndicateVault address").option("--rpc <url>", "Custom RPC URL for the active --chain network").action((opts) => {
2463
3301
  let saved = false;
2464
3302
  if (opts.privateKey) {
2465
3303
  setPrivateKey(opts.privateKey);
2466
3304
  const account = getAccount();
2467
- console.log(chalk5.green("Private key saved to ~/.sherwood/config.json"));
2468
- console.log(chalk5.dim(` Wallet: ${account.address}`));
3305
+ console.log(chalk7.green("Private key saved to ~/.sherwood/config.json"));
3306
+ console.log(chalk7.dim(` Wallet: ${account.address}`));
2469
3307
  saved = true;
2470
3308
  }
2471
3309
  if (opts.vault) {
2472
3310
  const chainId = getChain().id;
2473
3311
  setChainContract(chainId, "vault", opts.vault);
2474
- console.log(chalk5.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
2475
- console.log(chalk5.dim(` Vault: ${opts.vault}`));
3312
+ console.log(chalk7.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
3313
+ console.log(chalk7.dim(` Vault: ${opts.vault}`));
3314
+ saved = true;
3315
+ }
3316
+ if (opts.rpc) {
3317
+ const network = getNetwork();
3318
+ setConfigRpcUrl(network, opts.rpc);
3319
+ console.log(chalk7.green(`RPC URL saved for ${network}`));
3320
+ console.log(chalk7.dim(` RPC: ${opts.rpc}`));
2476
3321
  saved = true;
2477
3322
  }
2478
3323
  if (!saved) {
2479
- console.log(chalk5.red("Provide at least one of: --private-key, --vault"));
3324
+ console.log(chalk7.red("Provide at least one of: --private-key, --vault, --rpc"));
2480
3325
  process.exit(1);
2481
3326
  }
2482
3327
  });
2483
3328
  configCmd.command("show").description("Display current config for the active network").action(() => {
3329
+ const network = getNetwork();
2484
3330
  const chainId = getChain().id;
2485
3331
  const contracts = getChainContracts(chainId);
2486
3332
  const config = loadConfig();
3333
+ const customRpc = config.rpc?.[network];
2487
3334
  console.log();
2488
- console.log(chalk5.bold(`Sherwood Config (chain ${chainId})`));
2489
- console.log(chalk5.dim("\u2500".repeat(50)));
2490
- console.log(` Wallet: ${config.privateKey ? chalk5.green("configured") : chalk5.dim("not set")}`);
2491
- console.log(` Agent ID: ${config.agentId ?? chalk5.dim("not set")}`);
2492
- console.log(` Vault: ${contracts.vault ?? chalk5.dim("not set")}`);
3335
+ console.log(chalk7.bold(`Sherwood Config`));
3336
+ console.log(chalk7.dim("\u2500".repeat(50)));
3337
+ console.log(` Network: ${chalk7.cyan(network)} (chain ${chainId})`);
3338
+ console.log(` RPC: ${customRpc ? chalk7.green(customRpc) : chalk7.dim("default")}`);
3339
+ console.log(` Wallet: ${config.privateKey ? chalk7.green("configured") : chalk7.dim("not set")}`);
3340
+ console.log(` Agent ID: ${config.agentId ?? chalk7.dim("not set")}`);
3341
+ console.log(` Vault: ${contracts.vault ?? chalk7.dim("not set")}`);
2493
3342
  console.log();
2494
- console.log(chalk5.dim(" Config file: ~/.sherwood/config.json"));
3343
+ console.log(chalk7.dim(" Config file: ~/.sherwood/config.json"));
2495
3344
  console.log();
2496
3345
  });
2497
3346
  program.parse();