@sherwoodagent/cli 0.42.0 → 0.51.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 (68) hide show
  1. package/dist/{addresses-DCQMJJFI.js → addresses-DOWLGXGE.js} +4 -4
  2. package/dist/{agent-5YEJ6W6W.js → agent-ZPAZTJG3.js} +2923 -2023
  3. package/dist/agent-ZPAZTJG3.js.map +1 -0
  4. package/dist/autoresearch-IA7HGSNW.js +267 -0
  5. package/dist/autoresearch-IA7HGSNW.js.map +1 -0
  6. package/dist/{chat-UWJ6JBFE.js → chat-MRTPUZ3X.js} +8 -8
  7. package/dist/{chunk-FOOZC4AM.js → chunk-2LRC7I33.js} +2 -2
  8. package/dist/{chunk-4EHI4SKM.js → chunk-2W4KGGPU.js} +11 -1
  9. package/dist/chunk-2W4KGGPU.js.map +1 -0
  10. package/dist/chunk-7HJXZ4NM.js +189 -0
  11. package/dist/chunk-7HJXZ4NM.js.map +1 -0
  12. package/dist/{chunk-JNDEZFXW.js → chunk-E6LH2FJ6.js} +4 -4
  13. package/dist/{chunk-XAUMJZU2.js → chunk-FWC57PT7.js} +2 -2
  14. package/dist/{chunk-4OAZ7TP5.js → chunk-IS2CLLUT.js} +4 -4
  15. package/dist/{chunk-T2IA6CCW.js → chunk-KJ35EF7N.js} +4 -4
  16. package/dist/{chunk-GMVDHSCF.js → chunk-OHQOTSFG.js} +39 -3
  17. package/dist/chunk-OHQOTSFG.js.map +1 -0
  18. package/dist/chunk-OMW47WOO.js +700 -0
  19. package/dist/chunk-OMW47WOO.js.map +1 -0
  20. package/dist/{chunk-5Q3VT6NQ.js → chunk-R7X33D2S.js} +5 -5
  21. package/dist/{chunk-6U3A6ZJ2.js → chunk-WMZEOR5W.js} +6 -127
  22. package/dist/chunk-WMZEOR5W.js.map +1 -0
  23. package/dist/{chunk-SWKHSKDH.js → chunk-XDBRKW7I.js} +3 -3
  24. package/dist/chunk-YTYXBX7B.js +495 -0
  25. package/dist/chunk-YTYXBX7B.js.map +1 -0
  26. package/dist/{client-DRVUILER.js → client-UHHCB6EZ.js} +4 -4
  27. package/dist/{config-YRERDZSL.js → config-REASKDIK.js} +6 -2
  28. package/dist/{eas-KBU2BAOD.js → eas-PEEWGSAO.js} +6 -6
  29. package/dist/{governor-KH7RNH2E.js → governor-MG5VVSU4.js} +6 -6
  30. package/dist/index.js +1424 -270
  31. package/dist/index.js.map +1 -1
  32. package/dist/{network-3HB46JMQ.js → network-3ZU7UBDU.js} +3 -3
  33. package/dist/{price-TTQWI672.js → price-WCCXWPDM.js} +5 -5
  34. package/dist/{research-J3HYU3N6.js → research-QBKISW2M.js} +9 -9
  35. package/dist/research-YJWSQS2A.js +14 -0
  36. package/dist/serve-6MYBOAX6.js +222 -0
  37. package/dist/serve-6MYBOAX6.js.map +1 -0
  38. package/dist/{session-NUPHC6ZN.js → session-FPCPCVQX.js} +10 -10
  39. package/dist/summary-formatter-JIGWN4QS.js +180 -0
  40. package/dist/summary-formatter-JIGWN4QS.js.map +1 -0
  41. package/dist/{trade-VGECC4QZ.js → trade-NO6CNWCA.js} +19 -17
  42. package/dist/{trade-VGECC4QZ.js.map → trade-NO6CNWCA.js.map} +1 -1
  43. package/dist/{xmtp-JO7W6RFK.js → xmtp-ACB2W573.js} +6 -6
  44. package/package.json +5 -2
  45. package/dist/agent-5YEJ6W6W.js.map +0 -1
  46. package/dist/chunk-4EHI4SKM.js.map +0 -1
  47. package/dist/chunk-6U3A6ZJ2.js.map +0 -1
  48. package/dist/chunk-GMVDHSCF.js.map +0 -1
  49. package/dist/research-NR4VC5LN.js +0 -14
  50. /package/dist/{addresses-DCQMJJFI.js.map → addresses-DOWLGXGE.js.map} +0 -0
  51. /package/dist/{chat-UWJ6JBFE.js.map → chat-MRTPUZ3X.js.map} +0 -0
  52. /package/dist/{chunk-FOOZC4AM.js.map → chunk-2LRC7I33.js.map} +0 -0
  53. /package/dist/{chunk-JNDEZFXW.js.map → chunk-E6LH2FJ6.js.map} +0 -0
  54. /package/dist/{chunk-XAUMJZU2.js.map → chunk-FWC57PT7.js.map} +0 -0
  55. /package/dist/{chunk-4OAZ7TP5.js.map → chunk-IS2CLLUT.js.map} +0 -0
  56. /package/dist/{chunk-T2IA6CCW.js.map → chunk-KJ35EF7N.js.map} +0 -0
  57. /package/dist/{chunk-5Q3VT6NQ.js.map → chunk-R7X33D2S.js.map} +0 -0
  58. /package/dist/{chunk-SWKHSKDH.js.map → chunk-XDBRKW7I.js.map} +0 -0
  59. /package/dist/{client-DRVUILER.js.map → client-UHHCB6EZ.js.map} +0 -0
  60. /package/dist/{config-YRERDZSL.js.map → config-REASKDIK.js.map} +0 -0
  61. /package/dist/{eas-KBU2BAOD.js.map → eas-PEEWGSAO.js.map} +0 -0
  62. /package/dist/{governor-KH7RNH2E.js.map → governor-MG5VVSU4.js.map} +0 -0
  63. /package/dist/{network-3HB46JMQ.js.map → network-3ZU7UBDU.js.map} +0 -0
  64. /package/dist/{price-TTQWI672.js.map → price-WCCXWPDM.js.map} +0 -0
  65. /package/dist/{research-J3HYU3N6.js.map → research-QBKISW2M.js.map} +0 -0
  66. /package/dist/{research-NR4VC5LN.js.map → research-YJWSQS2A.js.map} +0 -0
  67. /package/dist/{session-NUPHC6ZN.js.map → session-FPCPCVQX.js.map} +0 -0
  68. /package/dist/{xmtp-JO7W6RFK.js.map → xmtp-ACB2W573.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,15 +1,25 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ HyperliquidProvider,
4
+ getLatestSignals,
5
+ hlCancelAllOrders,
6
+ hlGetMeta,
7
+ hlPlaceLimitOrder,
8
+ resolveHLCoin
9
+ } from "./chunk-OMW47WOO.js";
2
10
  import {
3
11
  UniswapProvider,
4
12
  applySlippage,
5
- chatCompletion,
6
- checkApiKeyValid,
7
13
  encodeSwapPath,
8
14
  getMultiHopQuote,
9
- getQuote,
15
+ getQuote
16
+ } from "./chunk-WMZEOR5W.js";
17
+ import {
18
+ chatCompletion,
19
+ checkApiKeyValid,
10
20
  listModels,
11
21
  provisionApiKey
12
- } from "./chunk-6U3A6ZJ2.js";
22
+ } from "./chunk-7HJXZ4NM.js";
13
23
  import {
14
24
  createApproval,
15
25
  createJoinRequest,
@@ -20,14 +30,14 @@ import {
20
30
  queryJoinRequests,
21
31
  revokeJoinRequest,
22
32
  stripReferrerPrefix
23
- } from "./chunk-JNDEZFXW.js";
33
+ } from "./chunk-E6LH2FJ6.js";
24
34
  import {
25
35
  createSyndicate,
26
36
  getActiveSyndicates,
27
37
  getSyndicate,
28
38
  subdomainExists,
29
39
  updateMetadata
30
- } from "./chunk-4OAZ7TP5.js";
40
+ } from "./chunk-IS2CLLUT.js";
31
41
  import {
32
42
  approveDepositor,
33
43
  deposit,
@@ -51,7 +61,7 @@ import {
51
61
  searchSyndicateAcrossChains,
52
62
  setTextRecord,
53
63
  setVaultAddress
54
- } from "./chunk-5Q3VT6NQ.js";
64
+ } from "./chunk-R7X33D2S.js";
55
65
  import {
56
66
  PROPOSAL_STATE,
57
67
  PROPOSAL_STATES,
@@ -83,7 +93,7 @@ import {
83
93
  setVotingPeriod,
84
94
  settleProposal,
85
95
  vote
86
- } from "./chunk-T2IA6CCW.js";
96
+ } from "./chunk-KJ35EF7N.js";
87
97
  import {
88
98
  AERODROME,
89
99
  AGENT_REGISTRY,
@@ -95,7 +105,7 @@ import {
95
105
  TOKENS,
96
106
  UNISWAP,
97
107
  VENICE
98
- } from "./chunk-XAUMJZU2.js";
108
+ } from "./chunk-FWC57PT7.js";
99
109
  import {
100
110
  formatContractError,
101
111
  getAccount,
@@ -104,7 +114,7 @@ import {
104
114
  sendTxWithRetry,
105
115
  waitForReceipt,
106
116
  writeContractWithRetry
107
- } from "./chunk-SWKHSKDH.js";
117
+ } from "./chunk-XDBRKW7I.js";
108
118
  import {
109
119
  CHAIN_REGISTRY,
110
120
  VALID_NETWORKS,
@@ -115,7 +125,7 @@ import {
115
125
  isTestnet,
116
126
  setNetwork,
117
127
  withNetwork
118
- } from "./chunk-FOOZC4AM.js";
128
+ } from "./chunk-2LRC7I33.js";
119
129
  import {
120
130
  addSyndicate,
121
131
  cacheGroupId,
@@ -131,6 +141,7 @@ import {
131
141
  getVeniceApiKey,
132
142
  loadConfig,
133
143
  setAgentId,
144
+ setAnthropicApiKey,
134
145
  setChainContract,
135
146
  setConfigRpcUrl,
136
147
  setNotifyTo,
@@ -138,7 +149,7 @@ import {
138
149
  setPrivateKey,
139
150
  setUniswapApiKey,
140
151
  setVeniceApiKey
141
- } from "./chunk-4EHI4SKM.js";
152
+ } from "./chunk-2W4KGGPU.js";
142
153
  import {
143
154
  BASE_STRATEGY_ABI,
144
155
  ERC20_ABI,
@@ -157,8 +168,8 @@ import {
157
168
  import { config as loadDotenv } from "dotenv";
158
169
  import { createRequire } from "module";
159
170
  import { Command, Option } from "commander";
160
- import { parseUnits as parseUnits6, formatUnits as formatUnits5, isAddress as isAddress6 } from "viem";
161
- import chalk9 from "chalk";
171
+ import { parseUnits as parseUnits7, formatUnits as formatUnits5, isAddress as isAddress6 } from "viem";
172
+ import chalk15 from "chalk";
162
173
  import ora8 from "ora";
163
174
  import { input, confirm, select } from "@inquirer/prompts";
164
175
 
@@ -1447,9 +1458,9 @@ function registerStrategyTemplateCommands(strategy2) {
1447
1458
  console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
1448
1459
  process.exit(1);
1449
1460
  }
1450
- const { propose: propose2 } = await import("./governor-KH7RNH2E.js");
1461
+ const { propose: propose2 } = await import("./governor-MG5VVSU4.js");
1451
1462
  const { pinJSON } = await import("./ipfs-IGXLLJCF.js");
1452
- const { parseDuration: parseDuration2 } = await import("./governor-KH7RNH2E.js");
1463
+ const { parseDuration: parseDuration2 } = await import("./governor-MG5VVSU4.js");
1453
1464
  const performanceFeeBps = BigInt(opts.performanceFee);
1454
1465
  if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
1455
1466
  console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
@@ -1556,7 +1567,7 @@ function registerStrategyTemplateCommands(strategy2) {
1556
1567
  let prices = [];
1557
1568
  let priceSource = null;
1558
1569
  try {
1559
- const { getTokenPricesInAsset } = await import("./price-TTQWI672.js");
1570
+ const { getTokenPricesInAsset } = await import("./price-WCCXWPDM.js");
1560
1571
  prices = await getTokenPricesInAsset({
1561
1572
  tokens: allocations.map((a, i) => ({
1562
1573
  token: a.token,
@@ -1986,7 +1997,7 @@ function registerVeniceCommands(program2) {
1986
1997
  console.log(chalk2.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
1987
1998
  console.log(chalk2.dim(" Saved to ~/.sherwood/config.json"));
1988
1999
  try {
1989
- const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-KBU2BAOD.js");
2000
+ const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
1990
2001
  const { uid } = await createVeniceProvisionAttestation(account.address);
1991
2002
  if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1992
2003
  console.log(chalk2.dim(` Attested: ${getEasScanUrl2(uid)}`));
@@ -2342,12 +2353,12 @@ ${opts.prompt}`;
2342
2353
  console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
2343
2354
  }
2344
2355
  try {
2345
- const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-KBU2BAOD.js");
2346
- const { keccak256, toHex, isAddress: isAddr } = await import("viem");
2347
- const { getChainContracts: getChainContracts2 } = await import("./config-YRERDZSL.js");
2348
- const { getChain: getActiveChain } = await import("./network-3HB46JMQ.js");
2356
+ const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
2357
+ const { keccak256: keccak2562, toHex, isAddress: isAddr } = await import("viem");
2358
+ const { getChainContracts: getChainContracts2 } = await import("./config-REASKDIK.js");
2359
+ const { getChain: getActiveChain } = await import("./network-3ZU7UBDU.js");
2349
2360
  const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
2350
- const promptHash = keccak256(toHex(userContent)).slice(0, 18);
2361
+ const promptHash = keccak2562(toHex(userContent)).slice(0, 18);
2351
2362
  const { uid } = await createVeniceInferenceAttestation(
2352
2363
  result.model,
2353
2364
  result.usage.promptTokens,
@@ -3518,8 +3529,8 @@ function printSimulationResult(result, callType) {
3518
3529
  }
3519
3530
  async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
3520
3531
  try {
3521
- const xmtp = await import("./xmtp-JO7W6RFK.js");
3522
- const { getAccount: getAccount2 } = await import("./client-DRVUILER.js");
3532
+ const xmtp = await import("./xmtp-ACB2W573.js");
3533
+ const { getAccount: getAccount2 } = await import("./client-UHHCB6EZ.js");
3523
3534
  const group = await xmtp.getGroup("", subdomain);
3524
3535
  const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
3525
3536
  const criticals = allRisks.filter((r) => r.level === "critical");
@@ -4610,6 +4621,1143 @@ function registerGuardianCommands(program2) {
4610
4621
  });
4611
4622
  }
4612
4623
 
4624
+ // src/commands/grid.ts
4625
+ import chalk14 from "chalk";
4626
+
4627
+ // src/grid/loop.ts
4628
+ import chalk13 from "chalk";
4629
+ import { appendFile, mkdir as mkdir4 } from "fs/promises";
4630
+ import { join as join4 } from "path";
4631
+ import { homedir as homedir4 } from "os";
4632
+
4633
+ // src/grid/manager.ts
4634
+ import chalk9 from "chalk";
4635
+
4636
+ // src/grid/portfolio.ts
4637
+ import { readFile, writeFile, rename, mkdir } from "fs/promises";
4638
+ import { join, dirname } from "path";
4639
+ import { homedir } from "os";
4640
+ var GRID_STATE_PATH = join(homedir(), ".sherwood", "grid", "portfolio.json");
4641
+ function emptyStats() {
4642
+ return {
4643
+ totalRoundTrips: 0,
4644
+ totalPnlUsd: 0,
4645
+ todayPnlUsd: 0,
4646
+ totalFills: 0,
4647
+ todayFills: 0,
4648
+ lastDailyReset: 0,
4649
+ lastRebalanceAt: 0
4650
+ };
4651
+ }
4652
+ var GridPortfolio = class {
4653
+ state = null;
4654
+ /** Load grid state from disk. Returns null if no grid initialized yet. */
4655
+ async load() {
4656
+ try {
4657
+ const raw = await readFile(GRID_STATE_PATH, "utf-8");
4658
+ this.state = JSON.parse(raw);
4659
+ return this.state;
4660
+ } catch {
4661
+ return null;
4662
+ }
4663
+ }
4664
+ /** Save grid state to disk (atomic write). */
4665
+ async save(state) {
4666
+ this.state = state;
4667
+ await mkdir(dirname(GRID_STATE_PATH), { recursive: true });
4668
+ const tmp = `${GRID_STATE_PATH}.tmp.${process.pid}`;
4669
+ await writeFile(tmp, JSON.stringify(state, null, 2), "utf-8");
4670
+ await rename(tmp, GRID_STATE_PATH);
4671
+ }
4672
+ /**
4673
+ * Initialize the grid by carving capital from the directional portfolio.
4674
+ * Called once on first startup when grid-portfolio.json doesn't exist.
4675
+ *
4676
+ * Returns the USD amount carved out (caller must deduct from directional portfolio).
4677
+ */
4678
+ async initialize(capital, config) {
4679
+ const allocation = capital;
4680
+ const grids = config.tokens.map((token) => ({
4681
+ token,
4682
+ levels: [],
4683
+ openFills: [],
4684
+ allocation: allocation * (config.tokenSplit[token] ?? 1 / config.tokens.length),
4685
+ stats: emptyStats(),
4686
+ centerPrice: 0,
4687
+ atr: 0
4688
+ }));
4689
+ const state = {
4690
+ totalAllocation: allocation,
4691
+ grids,
4692
+ paused: false,
4693
+ pauseReason: "",
4694
+ initializedAt: Date.now()
4695
+ };
4696
+ await this.save(state);
4697
+ this.state = state;
4698
+ }
4699
+ /** Get current state (must load() first). */
4700
+ getState() {
4701
+ return this.state;
4702
+ }
4703
+ /** Reset daily counters if UTC day boundary crossed. */
4704
+ resetDailyStats(state) {
4705
+ const now = Date.now();
4706
+ const todayMidnight = /* @__PURE__ */ new Date();
4707
+ todayMidnight.setUTCHours(0, 0, 0, 0);
4708
+ const todayMs = todayMidnight.getTime();
4709
+ let changed = false;
4710
+ for (const grid of state.grids) {
4711
+ if (!grid.stats.lastDailyReset || grid.stats.lastDailyReset < todayMs) {
4712
+ grid.stats.todayPnlUsd = 0;
4713
+ grid.stats.todayFills = 0;
4714
+ grid.stats.lastDailyReset = now;
4715
+ changed = true;
4716
+ }
4717
+ }
4718
+ return changed;
4719
+ }
4720
+ /** Check if grid should be paused (pool dropped below threshold). */
4721
+ checkPauseThreshold(state, config) {
4722
+ const currentValue = state.grids.reduce((sum, g) => {
4723
+ const openFillValue = g.openFills.filter((f) => !f.closed).reduce((s, f) => s + f.quantity * f.buyPrice, 0);
4724
+ return sum + g.allocation + openFillValue;
4725
+ }, 0);
4726
+ const dropPct = 1 - currentValue / state.totalAllocation;
4727
+ if (dropPct >= config.pauseThresholdPct) {
4728
+ state.paused = true;
4729
+ state.pauseReason = `Grid pool dropped ${(dropPct * 100).toFixed(1)}% (threshold: ${(config.pauseThresholdPct * 100).toFixed(0)}%)`;
4730
+ return true;
4731
+ }
4732
+ return false;
4733
+ }
4734
+ /** Aggregate stats across all token grids. */
4735
+ aggregateStats(state) {
4736
+ return {
4737
+ totalPnlUsd: state.grids.reduce((s, g) => s + g.stats.totalPnlUsd, 0),
4738
+ todayPnlUsd: state.grids.reduce((s, g) => s + g.stats.todayPnlUsd, 0),
4739
+ todayFills: state.grids.reduce((s, g) => s + g.stats.todayFills, 0),
4740
+ totalRoundTrips: state.grids.reduce((s, g) => s + g.stats.totalRoundTrips, 0)
4741
+ };
4742
+ }
4743
+ };
4744
+
4745
+ // src/grid/manager.ts
4746
+ var GridManager = class {
4747
+ config;
4748
+ portfolio;
4749
+ hl;
4750
+ constructor(config) {
4751
+ this.config = config;
4752
+ this.portfolio = new GridPortfolio();
4753
+ this.hl = new HyperliquidProvider();
4754
+ }
4755
+ /** Load or initialize grid state.
4756
+ * Also syncs new tokens added to config after initial grid setup. */
4757
+ async init(totalPortfolioValue) {
4758
+ const existing = await this.portfolio.load();
4759
+ if (!existing) {
4760
+ await this.portfolio.initialize(totalPortfolioValue, this.config);
4761
+ return;
4762
+ }
4763
+ let addedAllocation = 0;
4764
+ for (const token of this.config.tokens) {
4765
+ if (!existing.grids.find((g) => g.token === token)) {
4766
+ const split = this.config.tokenSplit[token] ?? 1 / this.config.tokens.length;
4767
+ const alloc = existing.totalAllocation * split;
4768
+ existing.grids.push({
4769
+ token,
4770
+ levels: [],
4771
+ openFills: [],
4772
+ allocation: alloc,
4773
+ stats: { totalRoundTrips: 0, totalPnlUsd: 0, todayPnlUsd: 0, totalFills: 0, todayFills: 0, lastDailyReset: 0, lastRebalanceAt: 0 },
4774
+ centerPrice: 0,
4775
+ atr: 0
4776
+ });
4777
+ addedAllocation += alloc;
4778
+ console.error(` [grid] Added new token grid: ${token} ($${alloc.toFixed(0)} allocation)`);
4779
+ }
4780
+ }
4781
+ if (addedAllocation > 0) {
4782
+ await this.portfolio.save(existing);
4783
+ }
4784
+ }
4785
+ /**
4786
+ * Run one grid tick — called every cycle from the agent loop.
4787
+ *
4788
+ * 1. Load state + reset daily counters
4789
+ * 2. For each token grid: fetch price, simulate fills, check rebalance
4790
+ * 3. Persist state
4791
+ *
4792
+ * Returns a summary for the cycle log.
4793
+ */
4794
+ async tick(prices, regime) {
4795
+ const state = await this.portfolio.load();
4796
+ if (!state || state.paused || !this.config.enabled) {
4797
+ return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: state?.paused ?? false };
4798
+ }
4799
+ if (regime === "high-volatility") {
4800
+ console.error(chalk9.dim(` [grid] Paused: high-volatility regime \u2014 too risky for grid`));
4801
+ return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: false };
4802
+ }
4803
+ this.portfolio.resetDailyStats(state);
4804
+ let totalFills = 0;
4805
+ let totalRoundTrips = 0;
4806
+ let totalPnl = 0;
4807
+ for (const grid of state.grids) {
4808
+ const price = prices[grid.token];
4809
+ if (!price || price <= 0) continue;
4810
+ if (grid.levels.length === 0 || this.needsFullRebuild(grid)) {
4811
+ await this.buildGrid(grid, price);
4812
+ }
4813
+ const { fills, roundTrips, pnlUsd } = this.simulateFills(grid, price);
4814
+ totalFills += fills;
4815
+ totalRoundTrips += roundTrips;
4816
+ totalPnl += pnlUsd;
4817
+ if (this.needsShift(grid, price)) {
4818
+ await this.shiftGrid(grid, price);
4819
+ }
4820
+ }
4821
+ this.portfolio.checkPauseThreshold(state, this.config);
4822
+ await this.portfolio.save(state);
4823
+ return { fills: totalFills, roundTrips: totalRoundTrips, pnlUsd: totalPnl, paused: false };
4824
+ }
4825
+ /** Build a fresh grid centered on the current price using ATR. */
4826
+ async buildGrid(grid, currentPrice) {
4827
+ const candles = await this.hl.getCandles(grid.token, "4h", 14 * 24 * 60 * 60 * 1e3);
4828
+ if (!candles || candles.length < this.config.atrPeriod) {
4829
+ console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: insufficient candle data`));
4830
+ return;
4831
+ }
4832
+ const signals = getLatestSignals(candles);
4833
+ const atr = signals.atr;
4834
+ if (!Number.isFinite(atr) || atr <= 0) {
4835
+ console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: invalid ATR (${atr})`));
4836
+ return;
4837
+ }
4838
+ const range = atr * this.config.atrMultiplier;
4839
+ const spacing = range / this.config.levelsPerSide;
4840
+ const effectiveCapital = grid.allocation * this.config.leverage;
4841
+ const quantity = effectiveCapital / (this.config.levelsPerSide * currentPrice);
4842
+ const levels = [];
4843
+ for (let i = 1; i <= this.config.levelsPerSide; i++) {
4844
+ levels.push({
4845
+ price: currentPrice - spacing * i,
4846
+ side: "buy",
4847
+ quantity,
4848
+ filled: false,
4849
+ filledAt: 0
4850
+ });
4851
+ }
4852
+ for (let i = 1; i <= this.config.levelsPerSide; i++) {
4853
+ levels.push({
4854
+ price: currentPrice + spacing * i,
4855
+ side: "sell",
4856
+ quantity,
4857
+ filled: false,
4858
+ filledAt: 0
4859
+ });
4860
+ }
4861
+ grid.levels = levels;
4862
+ grid.centerPrice = currentPrice;
4863
+ grid.atr = atr;
4864
+ grid.stats.lastRebalanceAt = Date.now();
4865
+ console.error(chalk9.dim(
4866
+ ` [grid] Built ${grid.token} grid: center=$${currentPrice.toFixed(2)} ATR=$${atr.toFixed(2)} range=$${(currentPrice - range).toFixed(2)}-$${(currentPrice + range).toFixed(2)} spacing=$${spacing.toFixed(2)} qty=${quantity.toFixed(6)}`
4867
+ ));
4868
+ }
4869
+ /** Simulate order fills against the current price. */
4870
+ simulateFills(grid, currentPrice) {
4871
+ let fills = 0;
4872
+ let roundTrips = 0;
4873
+ let pnlUsd = 0;
4874
+ const now = Date.now();
4875
+ const spacing = grid.atr > 0 ? grid.atr * this.config.atrMultiplier / this.config.levelsPerSide : 0;
4876
+ if (spacing <= 0) return { fills: 0, roundTrips: 0, pnlUsd: 0 };
4877
+ for (const level of grid.levels) {
4878
+ if (level.filled) continue;
4879
+ if (level.side === "buy" && currentPrice <= level.price) {
4880
+ level.filled = true;
4881
+ level.filledAt = now;
4882
+ fills++;
4883
+ grid.stats.totalFills++;
4884
+ grid.stats.todayFills++;
4885
+ grid.openFills.push({
4886
+ token: grid.token,
4887
+ buyPrice: level.price,
4888
+ targetSellPrice: level.price + spacing,
4889
+ quantity: level.quantity,
4890
+ filledAt: now,
4891
+ closed: false,
4892
+ pnlUsd: 0,
4893
+ closedAt: 0
4894
+ });
4895
+ console.error(chalk9.dim(
4896
+ ` [grid] BUY fill ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
4897
+ ));
4898
+ }
4899
+ if (level.side === "sell" && currentPrice >= level.price) {
4900
+ level.filled = true;
4901
+ level.filledAt = now;
4902
+ fills++;
4903
+ grid.stats.totalFills++;
4904
+ grid.stats.todayFills++;
4905
+ console.error(chalk9.dim(
4906
+ ` [grid] SELL level ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
4907
+ ));
4908
+ }
4909
+ }
4910
+ for (const openFill of grid.openFills) {
4911
+ if (openFill.closed) continue;
4912
+ if (currentPrice >= openFill.targetSellPrice) {
4913
+ const profit = (currentPrice - openFill.buyPrice) * openFill.quantity * this.config.leverage;
4914
+ if (profit >= this.config.minProfitPerFillUsd) {
4915
+ openFill.closed = true;
4916
+ openFill.pnlUsd = profit;
4917
+ openFill.closedAt = now;
4918
+ pnlUsd += profit;
4919
+ roundTrips++;
4920
+ grid.stats.totalRoundTrips++;
4921
+ grid.stats.totalPnlUsd += profit;
4922
+ grid.stats.todayPnlUsd += profit;
4923
+ grid.allocation += profit;
4924
+ console.error(chalk9.green(
4925
+ ` [grid] ROUND-TRIP ${grid.token}: buy $${openFill.buyPrice.toFixed(2)} \u2192 sell $${currentPrice.toFixed(2)} = +$${profit.toFixed(2)}`
4926
+ ));
4927
+ }
4928
+ }
4929
+ }
4930
+ const pruneThreshold = now - 24 * 60 * 60 * 1e3;
4931
+ grid.openFills = grid.openFills.filter((f) => !f.closed || f.closedAt > pruneThreshold);
4932
+ return { fills, roundTrips, pnlUsd };
4933
+ }
4934
+ /** Check if the grid needs a full rebuild. */
4935
+ needsFullRebuild(grid) {
4936
+ if (grid.levels.length === 0) return true;
4937
+ const elapsed = Date.now() - grid.stats.lastRebalanceAt;
4938
+ return elapsed >= this.config.fullRebuildIntervalMs;
4939
+ }
4940
+ /** Check if price has drifted enough to warrant shifting the grid. */
4941
+ needsShift(grid, currentPrice) {
4942
+ if (grid.centerPrice <= 0 || grid.atr <= 0) return false;
4943
+ const range = grid.atr * this.config.atrMultiplier;
4944
+ const distFromCenter = Math.abs(currentPrice - grid.centerPrice);
4945
+ return distFromCenter / range >= this.config.rebalanceDriftPct;
4946
+ }
4947
+ /** Shift the grid to re-center on the current price. */
4948
+ async shiftGrid(grid, currentPrice) {
4949
+ console.error(chalk9.dim(
4950
+ ` [grid] Shifting ${grid.token} grid: center $${grid.centerPrice.toFixed(2)} \u2192 $${currentPrice.toFixed(2)}`
4951
+ ));
4952
+ await this.buildGrid(grid, currentPrice);
4953
+ }
4954
+ /** Get open fill exposure per token — used by the hedge manager. */
4955
+ getOpenFillExposure() {
4956
+ const state = this.portfolio.getState();
4957
+ if (!state) return [];
4958
+ return state.grids.map((g) => {
4959
+ const opens = g.openFills.filter((f) => !f.closed);
4960
+ const totalQty = opens.reduce((s, f) => s + f.quantity, 0);
4961
+ const totalNotional = opens.reduce((s, f) => s + f.quantity * f.buyPrice, 0);
4962
+ return {
4963
+ token: g.token,
4964
+ totalQuantity: totalQty,
4965
+ totalNotional,
4966
+ avgEntryPrice: totalQty > 0 ? totalNotional / totalQty : 0,
4967
+ fillCount: opens.length
4968
+ };
4969
+ }).filter((e) => e.fillCount > 0);
4970
+ }
4971
+ /**
4972
+ * Compute the orders that should be placed for the current grid state,
4973
+ * without simulating fills. Used by the live executor.
4974
+ *
4975
+ * Returns:
4976
+ * - ordersToPlace: all current grid levels that haven't been filled
4977
+ * - assetsToCancel: tokens whose grid was rebalanced (need cancel-and-place)
4978
+ * - needsRebalance: whether any grid was rebuilt this tick
4979
+ */
4980
+ computeOrders(prices) {
4981
+ const state = this.portfolio.getState();
4982
+ if (!state || state.paused || !this.config.enabled) {
4983
+ return { ordersToPlace: [], assetsToCancel: [], needsRebalance: false };
4984
+ }
4985
+ const ordersToPlace = [];
4986
+ const assetsToCancel = [];
4987
+ let needsRebalance = false;
4988
+ for (const grid of state.grids) {
4989
+ const price = prices[grid.token];
4990
+ if (!price || price <= 0) continue;
4991
+ const wasEmpty = grid.levels.length === 0;
4992
+ const fullRebuild = wasEmpty || this.needsFullRebuild(grid);
4993
+ const shift = !fullRebuild && grid.centerPrice > 0 && this.needsShift(grid, price);
4994
+ if (fullRebuild || shift) {
4995
+ needsRebalance = true;
4996
+ if (!wasEmpty) assetsToCancel.push(grid.token);
4997
+ }
4998
+ for (const level of grid.levels) {
4999
+ if (level.filled) continue;
5000
+ ordersToPlace.push({
5001
+ token: grid.token,
5002
+ isBuy: level.side === "buy",
5003
+ price: level.price,
5004
+ quantity: level.quantity
5005
+ });
5006
+ }
5007
+ }
5008
+ return { ordersToPlace, assetsToCancel, needsRebalance };
5009
+ }
5010
+ /** Get aggregate stats for display. */
5011
+ getStats() {
5012
+ const state = this.portfolio.getState();
5013
+ if (!state) return null;
5014
+ const agg = this.portfolio.aggregateStats(state);
5015
+ return {
5016
+ ...agg,
5017
+ allocation: state.totalAllocation,
5018
+ paused: state.paused
5019
+ };
5020
+ }
5021
+ };
5022
+
5023
+ // src/grid/config.ts
5024
+ var DEFAULT_GRID_CONFIG = {
5025
+ enabled: true,
5026
+ tokens: ["bitcoin", "ethereum", "solana"],
5027
+ allocationPct: 0.5,
5028
+ leverage: 5,
5029
+ // was 4 — +25% profit per RT
5030
+ levelsPerSide: 15,
5031
+ atrMultiplier: 2,
5032
+ atrPeriod: 14,
5033
+ rebalanceDriftPct: 0.4,
5034
+ // was 0.55 — rebalance faster, keep levels near price
5035
+ fullRebuildIntervalMs: 12 * 60 * 60 * 1e3,
5036
+ // 12h
5037
+ tokenSplit: { bitcoin: 0.45, ethereum: 0.3, solana: 0.25 },
5038
+ minProfitPerFillUsd: 0.5,
5039
+ pauseThresholdPct: 0.2
5040
+ };
5041
+
5042
+ // src/grid/hedge.ts
5043
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
5044
+ import { join as join2, dirname as dirname2 } from "path";
5045
+ import { homedir as homedir2 } from "os";
5046
+ import chalk10 from "chalk";
5047
+ var HEDGE_RATIO = 0.3;
5048
+ var MIN_FILLS_TO_HEDGE = 3;
5049
+ var ADJUSTMENT_THRESHOLD = 0.1;
5050
+ var HEDGE_STATE_PATH = join2(homedir2(), ".sherwood", "grid", "hedge.json");
5051
+ var GridHedgeManager = class {
5052
+ state = null;
5053
+ async load() {
5054
+ if (this.state) return this.state;
5055
+ try {
5056
+ const raw = await readFile2(HEDGE_STATE_PATH, "utf-8");
5057
+ this.state = JSON.parse(raw);
5058
+ } catch {
5059
+ this.state = {
5060
+ positions: [],
5061
+ totalRealizedPnl: 0,
5062
+ todayRealizedPnl: 0,
5063
+ lastDailyReset: Date.now()
5064
+ };
5065
+ }
5066
+ const todayMidnight = /* @__PURE__ */ new Date();
5067
+ todayMidnight.setUTCHours(0, 0, 0, 0);
5068
+ if (this.state.lastDailyReset < todayMidnight.getTime()) {
5069
+ this.state.todayRealizedPnl = 0;
5070
+ this.state.lastDailyReset = Date.now();
5071
+ }
5072
+ return this.state;
5073
+ }
5074
+ async save() {
5075
+ if (!this.state) return;
5076
+ await mkdir2(dirname2(HEDGE_STATE_PATH), { recursive: true });
5077
+ const tmp = `${HEDGE_STATE_PATH}.tmp.${process.pid}`;
5078
+ await writeFile2(tmp, JSON.stringify(this.state, null, 2), "utf-8");
5079
+ const { rename: rename3 } = await import("fs/promises");
5080
+ await rename3(tmp, HEDGE_STATE_PATH);
5081
+ }
5082
+ /**
5083
+ * Run one hedge tick. Call after grid tick with current prices and open fill data.
5084
+ *
5085
+ * @param openFills - Per-token open fill exposure from the grid portfolio
5086
+ * @param prices - Current mark prices per token
5087
+ */
5088
+ async tick(openFills, prices) {
5089
+ const state = await this.load();
5090
+ let adjustments = 0;
5091
+ let unrealizedPnl = 0;
5092
+ for (const fill of openFills) {
5093
+ const price = prices[fill.token];
5094
+ if (!price || price <= 0) continue;
5095
+ const desiredShortQty = fill.fillCount >= MIN_FILLS_TO_HEDGE ? fill.totalQuantity * HEDGE_RATIO : 0;
5096
+ const existing = state.positions.find((p) => p.token === fill.token);
5097
+ if (desiredShortQty <= 0) {
5098
+ if (existing && existing.quantity > 0) {
5099
+ const closePnl = (existing.entryPrice - price) * existing.quantity;
5100
+ state.totalRealizedPnl += closePnl;
5101
+ state.todayRealizedPnl += closePnl;
5102
+ existing.realizedPnl += closePnl;
5103
+ console.error(chalk10.yellow(
5104
+ ` [hedge] CLOSE ${fill.token} short ${existing.quantity.toFixed(6)} @ $${price.toFixed(2)} PnL: $${closePnl.toFixed(2)}`
5105
+ ));
5106
+ existing.quantity = 0;
5107
+ existing.entryPrice = 0;
5108
+ adjustments++;
5109
+ }
5110
+ continue;
5111
+ }
5112
+ if (!existing) {
5113
+ state.positions.push({
5114
+ token: fill.token,
5115
+ entryPrice: price,
5116
+ quantity: desiredShortQty,
5117
+ lastAdjustedAt: Date.now(),
5118
+ realizedPnl: 0
5119
+ });
5120
+ console.error(chalk10.magenta(
5121
+ ` [hedge] OPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)} (${(HEDGE_RATIO * 100).toFixed(0)}% of ${fill.fillCount} open fills)`
5122
+ ));
5123
+ adjustments++;
5124
+ } else if (existing.quantity > 0) {
5125
+ const diff = Math.abs(desiredShortQty - existing.quantity) / existing.quantity;
5126
+ if (diff > ADJUSTMENT_THRESHOLD) {
5127
+ if (desiredShortQty > existing.quantity) {
5128
+ const addQty = desiredShortQty - existing.quantity;
5129
+ const newAvgEntry = (existing.entryPrice * existing.quantity + price * addQty) / desiredShortQty;
5130
+ existing.entryPrice = newAvgEntry;
5131
+ existing.quantity = desiredShortQty;
5132
+ existing.lastAdjustedAt = Date.now();
5133
+ console.error(chalk10.magenta(
5134
+ ` [hedge] INCREASE ${fill.token} short to ${desiredShortQty.toFixed(6)} @ avg $${newAvgEntry.toFixed(2)}`
5135
+ ));
5136
+ adjustments++;
5137
+ } else {
5138
+ const closeQty = existing.quantity - desiredShortQty;
5139
+ const closePnl = (existing.entryPrice - price) * closeQty;
5140
+ state.totalRealizedPnl += closePnl;
5141
+ state.todayRealizedPnl += closePnl;
5142
+ existing.realizedPnl += closePnl;
5143
+ existing.quantity = desiredShortQty;
5144
+ existing.lastAdjustedAt = Date.now();
5145
+ console.error(chalk10.magenta(
5146
+ ` [hedge] DECREASE ${fill.token} short to ${desiredShortQty.toFixed(6)}, closed ${closeQty.toFixed(6)} PnL: $${closePnl.toFixed(2)}`
5147
+ ));
5148
+ adjustments++;
5149
+ }
5150
+ }
5151
+ const uPnl = (existing.entryPrice - price) * existing.quantity;
5152
+ unrealizedPnl += uPnl;
5153
+ } else {
5154
+ existing.entryPrice = price;
5155
+ existing.quantity = desiredShortQty;
5156
+ existing.lastAdjustedAt = Date.now();
5157
+ console.error(chalk10.magenta(
5158
+ ` [hedge] REOPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)}`
5159
+ ));
5160
+ adjustments++;
5161
+ }
5162
+ }
5163
+ for (const pos of state.positions) {
5164
+ if (pos.quantity <= 0) continue;
5165
+ if (!openFills.find((f) => f.token === pos.token)) {
5166
+ const price = prices[pos.token];
5167
+ if (price) {
5168
+ const closePnl = (pos.entryPrice - price) * pos.quantity;
5169
+ state.totalRealizedPnl += closePnl;
5170
+ state.todayRealizedPnl += closePnl;
5171
+ pos.realizedPnl += closePnl;
5172
+ console.error(chalk10.yellow(
5173
+ ` [hedge] CLOSE orphaned ${pos.token} short, PnL: $${closePnl.toFixed(2)}`
5174
+ ));
5175
+ pos.quantity = 0;
5176
+ adjustments++;
5177
+ }
5178
+ }
5179
+ }
5180
+ await this.save();
5181
+ return {
5182
+ adjustments,
5183
+ unrealizedPnl,
5184
+ totalRealizedPnl: state.totalRealizedPnl
5185
+ };
5186
+ }
5187
+ /** Get current hedge status for display. */
5188
+ getStatus() {
5189
+ if (!this.state) return null;
5190
+ return {
5191
+ positions: this.state.positions.filter((p) => p.quantity > 0),
5192
+ totalRealizedPnl: this.state.totalRealizedPnl,
5193
+ todayRealizedPnl: this.state.todayRealizedPnl
5194
+ };
5195
+ }
5196
+ };
5197
+
5198
+ // src/grid/executor.ts
5199
+ import chalk11 from "chalk";
5200
+ var GridExecutor = class {
5201
+ cfg;
5202
+ constructor(cfg) {
5203
+ this.cfg = cfg;
5204
+ }
5205
+ /**
5206
+ * Execute the order plan against Hyperliquid.
5207
+ * Cancels stale orders for rebalanced tokens, then places new orders.
5208
+ */
5209
+ async execute(plan) {
5210
+ const errors = [];
5211
+ let cancelled = 0;
5212
+ let placed = 0;
5213
+ for (const token of plan.assetsToCancel) {
5214
+ const coin = resolveHLCoin(token);
5215
+ if (!coin) {
5216
+ errors.push(`No HL ticker for ${token}`);
5217
+ continue;
5218
+ }
5219
+ try {
5220
+ await hlCancelAllOrders(coin);
5221
+ cancelled++;
5222
+ console.error(chalk11.dim(` [grid-exec] Cancelled all orders for ${coin}`));
5223
+ } catch (e) {
5224
+ errors.push(`Cancel ${coin} failed: ${e.message}`);
5225
+ }
5226
+ }
5227
+ for (const order of plan.ordersToPlace) {
5228
+ const coin = resolveHLCoin(order.token);
5229
+ if (!coin) {
5230
+ errors.push(`No HL ticker for ${order.token}`);
5231
+ continue;
5232
+ }
5233
+ try {
5234
+ const res = await hlPlaceLimitOrder(coin, order.isBuy, order.quantity, order.price);
5235
+ if (res.success) {
5236
+ placed++;
5237
+ } else {
5238
+ errors.push(`Place ${coin} ${order.isBuy ? "buy" : "sell"} @${order.price}: ${res.error}`);
5239
+ }
5240
+ } catch (e) {
5241
+ errors.push(`Place ${coin} threw: ${e.message}`);
5242
+ }
5243
+ }
5244
+ return { placed, cancelled, errors };
5245
+ }
5246
+ };
5247
+
5248
+ // src/grid/onchain-executor.ts
5249
+ import chalk12 from "chalk";
5250
+ import { mkdir as mkdir3, readFile as readFile3, rename as rename2, writeFile as writeFile3 } from "fs/promises";
5251
+ import { dirname as dirname3, join as join3 } from "path";
5252
+ import { homedir as homedir3 } from "os";
5253
+ import { encodeAbiParameters as encodeAbiParameters10, parseUnits as parseUnits6 } from "viem";
5254
+
5255
+ // src/grid/cloid.ts
5256
+ import { keccak256, encodeAbiParameters as encodeAbiParameters9 } from "viem";
5257
+ function gridCloid(strategy2, assetIndex, isBuy, levelIndex, nonce) {
5258
+ const hash = keccak256(
5259
+ encodeAbiParameters9(
5260
+ [
5261
+ { type: "address" },
5262
+ { type: "uint32" },
5263
+ { type: "bool" },
5264
+ { type: "uint32" },
5265
+ { type: "uint32" }
5266
+ ],
5267
+ [strategy2, assetIndex, isBuy, levelIndex, nonce]
5268
+ )
5269
+ );
5270
+ const top16 = hash.slice(0, 2 + 32);
5271
+ return BigInt(top16);
5272
+ }
5273
+
5274
+ // src/grid/strategy-abi.ts
5275
+ var HYPERLIQUID_GRID_STRATEGY_ABI = [
5276
+ {
5277
+ type: "function",
5278
+ name: "updateParams",
5279
+ inputs: [{ name: "data", type: "bytes" }],
5280
+ outputs: [],
5281
+ stateMutability: "nonpayable"
5282
+ }
5283
+ ];
5284
+
5285
+ // src/grid/onchain-executor.ts
5286
+ var ONCHAIN_STATE_PATH = join3(homedir3(), ".sherwood", "grid", "onchain-state.json");
5287
+ var UINT64_MAX = 18446744073709551615n;
5288
+ var ACTION_PLACE_GRID = 1;
5289
+ var ACTION_CANCEL_ALL = 2;
5290
+ var ACTION_CANCEL_AND_PLACE = 3;
5291
+ var GRID_ORDER_COMPONENTS = [
5292
+ { name: "assetIndex", type: "uint32" },
5293
+ { name: "isBuy", type: "bool" },
5294
+ { name: "limitPx", type: "uint64" },
5295
+ { name: "sz", type: "uint64" },
5296
+ { name: "cloid", type: "uint128" }
5297
+ ];
5298
+ var OnchainGridExecutor = class {
5299
+ cfg;
5300
+ nonces = /* @__PURE__ */ new Map();
5301
+ placedCloids = /* @__PURE__ */ new Map();
5302
+ meta = null;
5303
+ busy = false;
5304
+ constructor(cfg) {
5305
+ this.cfg = cfg;
5306
+ }
5307
+ /** Load persisted state. Call once before the first execute(). */
5308
+ async load() {
5309
+ try {
5310
+ const raw = await readFile3(ONCHAIN_STATE_PATH, "utf-8");
5311
+ const state = JSON.parse(raw);
5312
+ for (const [tok, n] of Object.entries(state.nonces)) this.nonces.set(tok, n);
5313
+ for (const [tok, cloids] of Object.entries(state.placedCloids)) {
5314
+ this.placedCloids.set(tok, cloids.map((s) => BigInt(s)));
5315
+ }
5316
+ console.error(chalk12.dim(` [grid-onchain] Loaded state: ${this.nonces.size} tokens`));
5317
+ } catch (e) {
5318
+ const code = e.code;
5319
+ if (code === "ENOENT") {
5320
+ } else {
5321
+ console.error(
5322
+ chalk12.yellow(
5323
+ ` [grid-onchain] Failed to load state (${e.message}) \u2014 starting fresh. Existing on-chain orders may be orphaned.`
5324
+ )
5325
+ );
5326
+ }
5327
+ }
5328
+ if (!this.meta) this.meta = await hlGetMeta();
5329
+ }
5330
+ /**
5331
+ * Atomic save: write to .tmp, then POSIX-rename. A crash mid-write leaves
5332
+ * either the old file intact or the new file fully written — never a
5333
+ * partial JSON that would crash load() on restart.
5334
+ */
5335
+ async save() {
5336
+ const state = {
5337
+ nonces: Object.fromEntries(this.nonces),
5338
+ placedCloids: Object.fromEntries(
5339
+ [...this.placedCloids].map(([tok, cloids]) => [tok, cloids.map((c) => c.toString())])
5340
+ )
5341
+ };
5342
+ await mkdir3(dirname3(ONCHAIN_STATE_PATH), { recursive: true });
5343
+ const tmp = ONCHAIN_STATE_PATH + ".tmp";
5344
+ await writeFile3(tmp, JSON.stringify(state, null, 2));
5345
+ await rename2(tmp, ONCHAIN_STATE_PATH);
5346
+ }
5347
+ /**
5348
+ * Execute the order plan: cancel stale orders for rebalanced tokens, then
5349
+ * place new orders. Each (token) is one tx. Persists state after each tx
5350
+ * so a crash mid-execution still leaves a recoverable on-disk record.
5351
+ *
5352
+ * Concurrency-guarded: a second concurrent call returns immediately rather
5353
+ * than racing tx submission against another in-flight execute().
5354
+ */
5355
+ async execute(plan) {
5356
+ if (this.busy) {
5357
+ return { placed: 0, cancelled: 0, txs: [], errors: ["execute() already in flight \u2014 skipping"] };
5358
+ }
5359
+ this.busy = true;
5360
+ try {
5361
+ if (!this.meta) await this.load();
5362
+ const errors = [];
5363
+ const txs = [];
5364
+ let placed = 0;
5365
+ let cancelled = 0;
5366
+ const ordersByToken = /* @__PURE__ */ new Map();
5367
+ for (const o of plan.ordersToPlace) {
5368
+ if (!ordersByToken.has(o.token)) ordersByToken.set(o.token, []);
5369
+ ordersByToken.get(o.token).push(o);
5370
+ }
5371
+ const cancelSet = new Set(plan.assetsToCancel);
5372
+ const allTokens = /* @__PURE__ */ new Set([...ordersByToken.keys(), ...cancelSet]);
5373
+ for (const token of allTokens) {
5374
+ const assetIndex = this.cfg.assetIndices[token];
5375
+ if (assetIndex === void 0) {
5376
+ errors.push(`No asset index for ${token}`);
5377
+ continue;
5378
+ }
5379
+ const coin = resolveHLCoin(token);
5380
+ if (!coin) {
5381
+ errors.push(`No HL ticker for ${token}`);
5382
+ continue;
5383
+ }
5384
+ const meta = this.meta.get(coin);
5385
+ if (!meta) {
5386
+ errors.push(`No HL meta for ${coin} (run \`sherwood grid status\` to refresh)`);
5387
+ continue;
5388
+ }
5389
+ const wantCancel = cancelSet.has(token);
5390
+ const orders = ordersByToken.get(token) ?? [];
5391
+ const wantPlace = orders.length > 0;
5392
+ try {
5393
+ if (wantCancel && wantPlace) {
5394
+ const tx = await this.cancelAndPlace(token, assetIndex, orders, meta);
5395
+ txs.push(tx);
5396
+ cancelled += 1;
5397
+ placed += orders.length;
5398
+ } else if (wantCancel) {
5399
+ const tx = await this.cancelAll(token, assetIndex);
5400
+ txs.push(tx);
5401
+ cancelled += 1;
5402
+ } else if (wantPlace) {
5403
+ const tx = await this.placeGrid(token, assetIndex, orders, meta);
5404
+ txs.push(tx);
5405
+ placed += orders.length;
5406
+ }
5407
+ await this.save();
5408
+ } catch (e) {
5409
+ errors.push(`${token}: ${e.message}`);
5410
+ }
5411
+ }
5412
+ return { placed, cancelled, txs, errors };
5413
+ } finally {
5414
+ this.busy = false;
5415
+ }
5416
+ }
5417
+ currentNonce(token) {
5418
+ return this.nonces.get(token) ?? 0;
5419
+ }
5420
+ bumpNonce(token) {
5421
+ const next = this.currentNonce(token) + 1;
5422
+ this.nonces.set(token, next);
5423
+ return next;
5424
+ }
5425
+ /**
5426
+ * Pure encoding — does NOT mutate placedCloids. Caller commits CLOIDs
5427
+ * only after the on-chain tx confirms successfully.
5428
+ *
5429
+ * Uses viem's `parseUnits` for decimal scaling instead of float math so
5430
+ * high-szDecimals tokens (BTC=5, ETH=4) keep full precision on small sizes.
5431
+ */
5432
+ encodeOrders(assetIndex, orders, meta, nonce) {
5433
+ const cloids = [];
5434
+ const encoded = orders.map((o, i) => {
5435
+ const cloid = gridCloid(this.cfg.strategyAddress, assetIndex, o.isBuy, i, nonce);
5436
+ cloids.push(cloid);
5437
+ const limitPx = parseUnits6(o.price.toFixed(meta.pxDecimals), meta.pxDecimals);
5438
+ const sz = parseUnits6(o.quantity.toFixed(meta.szDecimals), meta.szDecimals);
5439
+ if (limitPx > UINT64_MAX || sz > UINT64_MAX) {
5440
+ throw new Error(
5441
+ `Order exceeds uint64: token=${o.token} px=${limitPx} sz=${sz}`
5442
+ );
5443
+ }
5444
+ return { assetIndex, isBuy: o.isBuy, limitPx, sz, cloid };
5445
+ });
5446
+ return { encoded, cloids };
5447
+ }
5448
+ async placeGrid(token, assetIndex, orders, meta) {
5449
+ const nonce = this.bumpNonce(token);
5450
+ const { encoded, cloids } = this.encodeOrders(assetIndex, orders, meta, nonce);
5451
+ const data = encodeAbiParameters10(
5452
+ [{ type: "uint8" }, { type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }],
5453
+ [ACTION_PLACE_GRID, encoded]
5454
+ );
5455
+ const tx = await this.send(data);
5456
+ this.placedCloids.set(token, cloids);
5457
+ return tx;
5458
+ }
5459
+ async cancelAll(token, assetIndex) {
5460
+ const cloids = this.placedCloids.get(token) ?? [];
5461
+ if (cloids.length === 0) {
5462
+ this.bumpNonce(token);
5463
+ return "0x";
5464
+ }
5465
+ const data = encodeAbiParameters10(
5466
+ [{ type: "uint8" }, { type: "uint32" }, { type: "uint128[]" }],
5467
+ [ACTION_CANCEL_ALL, assetIndex, cloids]
5468
+ );
5469
+ const tx = await this.send(data);
5470
+ this.placedCloids.set(token, []);
5471
+ this.bumpNonce(token);
5472
+ return tx;
5473
+ }
5474
+ async cancelAndPlace(token, assetIndex, orders, meta) {
5475
+ const oldCloids = this.placedCloids.get(token) ?? [];
5476
+ const newNonce = this.bumpNonce(token);
5477
+ const { encoded, cloids: newCloids } = this.encodeOrders(assetIndex, orders, meta, newNonce);
5478
+ const data = encodeAbiParameters10(
5479
+ [
5480
+ { type: "uint8" },
5481
+ { type: "uint32" },
5482
+ { type: "uint128[]" },
5483
+ { type: "tuple[]", components: [...GRID_ORDER_COMPONENTS] }
5484
+ ],
5485
+ [ACTION_CANCEL_AND_PLACE, assetIndex, oldCloids, encoded]
5486
+ );
5487
+ const tx = await this.send(data);
5488
+ this.placedCloids.set(token, newCloids);
5489
+ return tx;
5490
+ }
5491
+ /**
5492
+ * Submit and wait for receipt. Throwing here surfaces to the per-token
5493
+ * try/catch in execute(), recording the error and skipping the save() so
5494
+ * placedCloids never reflects orders that didn't land.
5495
+ */
5496
+ async send(data) {
5497
+ const tx = await writeContractWithRetry({
5498
+ address: this.cfg.strategyAddress,
5499
+ abi: HYPERLIQUID_GRID_STRATEGY_ABI,
5500
+ functionName: "updateParams",
5501
+ args: [data]
5502
+ });
5503
+ const pub = getPublicClient();
5504
+ const receipt = await pub.waitForTransactionReceipt({ hash: tx, timeout: 3e4 });
5505
+ if (receipt.status !== "success") {
5506
+ throw new Error(`Tx ${tx} reverted on-chain`);
5507
+ }
5508
+ console.error(chalk12.dim(` [grid-onchain] tx ${tx.slice(0, 10)}... confirmed`));
5509
+ return tx;
5510
+ }
5511
+ };
5512
+
5513
+ // src/grid/loop.ts
5514
+ var GRID_CYCLES_PATH = join4(homedir4(), ".sherwood", "grid", "cycles.jsonl");
5515
+ var GridLoop = class {
5516
+ cfg;
5517
+ gridConfig;
5518
+ manager;
5519
+ hedge;
5520
+ hl;
5521
+ executor = null;
5522
+ running = false;
5523
+ cycleCount = 0;
5524
+ timer = null;
5525
+ constructor(cfg) {
5526
+ this.cfg = cfg;
5527
+ this.gridConfig = { ...DEFAULT_GRID_CONFIG, ...cfg.config };
5528
+ this.manager = new GridManager(this.gridConfig);
5529
+ this.hedge = new GridHedgeManager();
5530
+ this.hl = new HyperliquidProvider();
5531
+ if (cfg.live) {
5532
+ if (!cfg.assetIndices) {
5533
+ throw new Error("assetIndices required when live=true");
5534
+ }
5535
+ if (cfg.strategyAddress) {
5536
+ this.executor = new OnchainGridExecutor({
5537
+ strategyAddress: cfg.strategyAddress,
5538
+ assetIndices: cfg.assetIndices
5539
+ });
5540
+ } else {
5541
+ this.executor = new GridExecutor({ assetIndices: cfg.assetIndices });
5542
+ }
5543
+ }
5544
+ }
5545
+ /** Start the grid loop. Resolves when shut down via SIGINT/SIGTERM. */
5546
+ async start() {
5547
+ this.running = true;
5548
+ const shutdown = () => {
5549
+ console.error(chalk13.yellow("\n [grid-loop] Shutting down\u2026"));
5550
+ this.running = false;
5551
+ if (this.timer) {
5552
+ clearTimeout(this.timer);
5553
+ this.timer = null;
5554
+ }
5555
+ };
5556
+ process.on("SIGINT", shutdown);
5557
+ process.on("SIGTERM", shutdown);
5558
+ await this.manager.init(this.cfg.capital);
5559
+ if (this.executor instanceof OnchainGridExecutor) {
5560
+ await this.executor.load();
5561
+ }
5562
+ console.error(chalk13.cyan(
5563
+ `
5564
+ [grid-loop] Started \u2014 capital=$${this.cfg.capital.toFixed(0)} cycle=${(this.cfg.cycle / 1e3).toFixed(0)}s tokens=[${this.gridConfig.tokens.join(", ")}] leverage=${this.gridConfig.leverage}x levels=${this.gridConfig.levelsPerSide}/side
5565
+ `
5566
+ ));
5567
+ while (this.running) {
5568
+ try {
5569
+ await this.tick();
5570
+ } catch (err) {
5571
+ console.error(chalk13.red(` [grid-loop] Tick error: ${err.message}`));
5572
+ }
5573
+ if (this.running) {
5574
+ await new Promise((resolve2) => {
5575
+ this.timer = setTimeout(resolve2, this.cfg.cycle);
5576
+ });
5577
+ }
5578
+ }
5579
+ process.off("SIGINT", shutdown);
5580
+ process.off("SIGTERM", shutdown);
5581
+ console.error(chalk13.yellow(" [grid-loop] Stopped."));
5582
+ }
5583
+ /** Execute one grid cycle. */
5584
+ async tick() {
5585
+ this.cycleCount++;
5586
+ const start = Date.now();
5587
+ const prices = {};
5588
+ for (const token of this.gridConfig.tokens) {
5589
+ const data = await this.hl.getHyperliquidData(token);
5590
+ if (data?.markPrice && data.markPrice > 0) {
5591
+ prices[token] = data.markPrice;
5592
+ }
5593
+ }
5594
+ if (Object.keys(prices).length === 0) {
5595
+ console.error(chalk13.dim(` [grid-loop] #${this.cycleCount} \u2014 no prices, skipping`));
5596
+ return;
5597
+ }
5598
+ const result = await this.manager.tick(prices);
5599
+ const elapsed = Date.now() - start;
5600
+ if (this.executor) {
5601
+ const plan = this.manager.computeOrders(prices);
5602
+ if (plan.ordersToPlace.length > 0 || plan.assetsToCancel.length > 0) {
5603
+ const res = await this.executor.execute(plan);
5604
+ if (res.errors.length > 0) {
5605
+ console.error(chalk13.yellow(` [grid-loop] Executor errors: ${res.errors.join("; ")}`));
5606
+ }
5607
+ if (res.placed > 0 || res.cancelled > 0) {
5608
+ console.error(chalk13.cyan(` [grid-loop] Live: placed=${res.placed} cancelled=${res.cancelled}`));
5609
+ }
5610
+ }
5611
+ }
5612
+ if (result.roundTrips > 0) {
5613
+ console.error(chalk13.green(
5614
+ ` [grid-loop] #${this.cycleCount} \u2014 ${result.roundTrips} RT(s), +$${result.pnlUsd.toFixed(2)} PnL, ${result.fills} fill(s) [${elapsed}ms]`
5615
+ ));
5616
+ }
5617
+ if (result.fills > 0 && result.roundTrips === 0) {
5618
+ console.error(chalk13.dim(
5619
+ ` [grid-loop] #${this.cycleCount} \u2014 ${result.fills} fill(s), 0 RTs [${elapsed}ms]`
5620
+ ));
5621
+ }
5622
+ const openExposure = this.manager.getOpenFillExposure();
5623
+ const hedgeResult = await this.hedge.tick(openExposure, prices);
5624
+ if (hedgeResult.adjustments > 0) {
5625
+ console.error(chalk13.magenta(
5626
+ ` [hedge] ${hedgeResult.adjustments} adjustment(s), unrealized: $${hedgeResult.unrealizedPnl.toFixed(2)}, total realized: $${hedgeResult.totalRealizedPnl.toFixed(2)}`
5627
+ ));
5628
+ }
5629
+ const stats = this.manager.getStats();
5630
+ const cycleEntry = {
5631
+ cycleNumber: this.cycleCount,
5632
+ timestamp: Date.now(),
5633
+ gridFills: result.fills,
5634
+ gridRoundTrips: result.roundTrips,
5635
+ gridPnlUsd: result.pnlUsd,
5636
+ totalPnlUsd: stats?.totalPnlUsd ?? 0,
5637
+ todayPnlUsd: stats?.todayPnlUsd ?? 0,
5638
+ totalRoundTrips: stats?.totalRoundTrips ?? 0,
5639
+ allocation: stats?.allocation ?? 0,
5640
+ paused: stats?.paused ?? false,
5641
+ hedgeUnrealizedPnl: hedgeResult.unrealizedPnl,
5642
+ hedgeTotalRealizedPnl: hedgeResult.totalRealizedPnl
5643
+ };
5644
+ try {
5645
+ await mkdir4(join4(homedir4(), ".sherwood", "grid"), { recursive: true });
5646
+ await appendFile(GRID_CYCLES_PATH, JSON.stringify(cycleEntry) + "\n");
5647
+ } catch {
5648
+ }
5649
+ if (this.cycleCount % 60 === 0) {
5650
+ const stats2 = this.manager.getStats();
5651
+ if (stats2) {
5652
+ const hedgeStatus = this.hedge.getStatus();
5653
+ const hedgeInfo = hedgeStatus && hedgeStatus.positions.length > 0 ? ` hedge=$${hedgeResult.unrealizedPnl.toFixed(2)}unr/$${hedgeStatus.totalRealizedPnl.toFixed(2)}real` : "";
5654
+ console.error(chalk13.cyan(
5655
+ ` [grid-loop] Status #${this.cycleCount} \u2014 totalPnL=$${stats2.totalPnlUsd.toFixed(2)} todayPnL=$${stats2.todayPnlUsd.toFixed(2)} RTs=${stats2.totalRoundTrips} alloc=$${stats2.allocation.toFixed(0)}${hedgeInfo}${stats2.paused ? " (PAUSED)" : ""}`
5656
+ ));
5657
+ }
5658
+ }
5659
+ }
5660
+ };
5661
+
5662
+ // src/commands/grid.ts
5663
+ var DIM5 = chalk14.gray;
5664
+ var G5 = chalk14.green;
5665
+ var BOLD5 = chalk14.white.bold;
5666
+ var W5 = chalk14.white;
5667
+ var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
5668
+ function registerGridCommand(program2) {
5669
+ const grid = program2.command("grid").description("Grid trading strategy \u2014 ATR-based grid on BTC/ETH/SOL");
5670
+ grid.command("start").description("Start the grid trading loop").option("--capital <usd>", "Starting capital in USD", "5000").option("--cycle <seconds>", "Cycle interval in seconds", "60").option("--tokens <list>", "Comma-separated token list", "bitcoin,ethereum,solana").option("--leverage <n>", "Leverage multiplier", "5").option("--levels <n>", "Grid levels per side", "15").option("--live", "enable live execution (places real orders on Hyperliquid)").option("--asset-indices <pairs>", "comma-separated token=index pairs (e.g. bitcoin=3,ethereum=4,solana=5)").option("--strategy <address>", "on-chain strategy contract address (enables on-chain executor)").action(async (opts) => {
5671
+ const capital = parseFloat(opts.capital);
5672
+ const cycleMs = parseInt(opts.cycle, 10) * 1e3;
5673
+ const tokens = opts.tokens.split(",").map((t) => t.trim());
5674
+ const leverage = parseFloat(opts.leverage);
5675
+ const levels = parseInt(opts.levels, 10);
5676
+ const live = !!opts.live;
5677
+ let assetIndices;
5678
+ if (live) {
5679
+ if (!opts.assetIndices) {
5680
+ throw new Error("--asset-indices required when --live (e.g. --asset-indices bitcoin=3,ethereum=4,solana=5)");
5681
+ }
5682
+ assetIndices = {};
5683
+ for (const pair of opts.assetIndices.split(",")) {
5684
+ const [tok, idx] = pair.split("=");
5685
+ if (!tok || !idx) throw new Error(`Bad asset-indices pair: ${pair}`);
5686
+ assetIndices[tok.trim()] = Number(idx);
5687
+ }
5688
+ }
5689
+ const strategyAddress = opts.strategy;
5690
+ if (strategyAddress && !live) {
5691
+ throw new Error("--strategy requires --live");
5692
+ }
5693
+ const weight = 1 / tokens.length;
5694
+ const tokenSplit = {};
5695
+ for (const t of tokens) {
5696
+ tokenSplit[t] = weight;
5697
+ }
5698
+ console.log();
5699
+ console.log(G5.bold(" Grid Strategy"));
5700
+ SEP5();
5701
+ console.log(W5(` Capital: $${capital.toLocaleString()}`));
5702
+ console.log(W5(` Cycle: ${opts.cycle}s`));
5703
+ console.log(W5(` Tokens: ${tokens.join(", ")}`));
5704
+ console.log(W5(` Leverage: ${leverage}x`));
5705
+ console.log(W5(` Levels: ${levels}/side`));
5706
+ SEP5();
5707
+ const loop = new GridLoop({
5708
+ capital,
5709
+ cycle: cycleMs,
5710
+ live,
5711
+ assetIndices,
5712
+ strategyAddress,
5713
+ config: {
5714
+ ...DEFAULT_GRID_CONFIG,
5715
+ tokens,
5716
+ leverage,
5717
+ levelsPerSide: levels,
5718
+ tokenSplit
5719
+ }
5720
+ });
5721
+ await loop.start();
5722
+ });
5723
+ grid.command("status").description("Show current grid portfolio status").action(async () => {
5724
+ const portfolio = new GridPortfolio();
5725
+ const state = await portfolio.load();
5726
+ if (!state) {
5727
+ console.log(DIM5("\n No grid portfolio found. Run `sherwood grid start` first.\n"));
5728
+ return;
5729
+ }
5730
+ portfolio.resetDailyStats(state);
5731
+ const agg = portfolio.aggregateStats(state);
5732
+ console.log();
5733
+ console.log(G5.bold(" Grid Portfolio"));
5734
+ SEP5();
5735
+ console.log(W5(` Allocation: $${state.totalAllocation.toLocaleString()}`));
5736
+ console.log(W5(` Status: ${state.paused ? chalk14.red("PAUSED \u2014 " + state.pauseReason) : G5("Active")}`));
5737
+ console.log(W5(` Initialized: ${new Date(state.initializedAt).toLocaleString()}`));
5738
+ SEP5();
5739
+ console.log();
5740
+ console.log(BOLD5(" Token Alloc RTs PnL Today PnL Fills"));
5741
+ console.log(DIM5(" " + "\u2500".repeat(72)));
5742
+ for (const g of state.grids) {
5743
+ const name = g.token.padEnd(13);
5744
+ const alloc = `$${g.allocation.toFixed(0)}`.padStart(8);
5745
+ const rts = String(g.stats.totalRoundTrips).padStart(6);
5746
+ const pnl = `$${g.stats.totalPnlUsd.toFixed(2)}`.padStart(10);
5747
+ const todayPnl = `$${g.stats.todayPnlUsd.toFixed(2)}`.padStart(10);
5748
+ const fills = String(g.stats.totalFills).padStart(6);
5749
+ const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 : chalk14.red;
5750
+ const todayColor = g.stats.todayPnlUsd >= 0 ? G5 : chalk14.red;
5751
+ console.log(` ${W5(name)} ${alloc} ${rts} ${pnlColor(pnl)} ${todayColor(todayPnl)} ${fills}`);
5752
+ }
5753
+ console.log(DIM5(" " + "\u2500".repeat(72)));
5754
+ const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 : chalk14.red;
5755
+ const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 : chalk14.red;
5756
+ console.log(` ${BOLD5("TOTAL".padEnd(13))} ${`$${state.totalAllocation.toFixed(0)}`.padStart(8)} ${String(agg.totalRoundTrips).padStart(6)} ${totalPnlColor(`$${agg.totalPnlUsd.toFixed(2)}`.padStart(10))} ${todayTotalColor(`$${agg.todayPnlUsd.toFixed(2)}`.padStart(10))} ${String(agg.todayFills).padStart(6)}`);
5757
+ console.log();
5758
+ });
5759
+ }
5760
+
4613
5761
  // src/index.ts
4614
5762
  try {
4615
5763
  loadDotenv();
@@ -4618,20 +5766,20 @@ try {
4618
5766
  var require2 = createRequire(import.meta.url);
4619
5767
  var { version: CLI_VERSION } = require2("../package.json");
4620
5768
  async function loadXmtp() {
4621
- return import("./xmtp-JO7W6RFK.js");
5769
+ return import("./xmtp-ACB2W573.js");
4622
5770
  }
4623
5771
  async function loadCron() {
4624
5772
  return import("./cron-EOULYO3R.js");
4625
5773
  }
4626
- var G5 = chalk9.green;
4627
- var W5 = chalk9.white;
4628
- var DIM5 = chalk9.gray;
4629
- var BOLD5 = chalk9.white.bold;
4630
- var LABEL5 = chalk9.green.bold;
4631
- var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
5774
+ var G6 = chalk15.green;
5775
+ var W6 = chalk15.white;
5776
+ var DIM6 = chalk15.gray;
5777
+ var BOLD6 = chalk15.white.bold;
5778
+ var LABEL5 = chalk15.green.bold;
5779
+ var SEP6 = () => console.log(DIM6("\u2500".repeat(60)));
4632
5780
  function validateAddress(value, name) {
4633
5781
  if (!isAddress6(value)) {
4634
- console.error(chalk9.red(`Invalid ${name} address: ${value}`));
5782
+ console.error(chalk15.red(`Invalid ${name} address: ${value}`));
4635
5783
  process.exit(1);
4636
5784
  }
4637
5785
  return value;
@@ -4651,7 +5799,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
4651
5799
  process.env.ENABLE_TESTNET = "true";
4652
5800
  if (network !== "base") {
4653
5801
  console.warn(
4654
- chalk9.yellow("[warn] --testnet ignored, --chain takes precedence")
5802
+ chalk15.yellow("[warn] --testnet ignored, --chain takes precedence")
4655
5803
  );
4656
5804
  } else {
4657
5805
  network = "base-sepolia";
@@ -4659,7 +5807,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
4659
5807
  }
4660
5808
  setNetwork(network);
4661
5809
  if (getNetwork() !== "base") {
4662
- console.log(chalk9.yellow(`[${getNetwork()}]`));
5810
+ console.log(chalk15.yellow(`[${getNetwork()}]`));
4663
5811
  }
4664
5812
  });
4665
5813
  var syndicate = program.command("syndicate");
@@ -4667,31 +5815,31 @@ syndicate.command("create").description("Create a new syndicate via the factory
4667
5815
  try {
4668
5816
  console.log();
4669
5817
  console.log(LABEL5(" \u25C6 Create Syndicate"));
4670
- SEP5();
5818
+ SEP6();
4671
5819
  const wallet = getAccount();
4672
- console.log(DIM5(` Wallet: ${wallet.address}`));
4673
- console.log(DIM5(` Network: ${getChain().name}`));
4674
- SEP5();
5820
+ console.log(DIM6(` Wallet: ${wallet.address}`));
5821
+ console.log(DIM6(` Network: ${getChain().name}`));
5822
+ SEP6();
4675
5823
  const savedAgentId = getAgentId();
4676
5824
  const nonInteractive = opts.yes;
4677
5825
  const name = opts.name || (nonInteractive ? (() => {
4678
5826
  throw new Error("--name is required in non-interactive mode (-y)");
4679
5827
  })() : await input({
4680
- message: G5("Syndicate name"),
5828
+ message: G6("Syndicate name"),
4681
5829
  validate: (v) => v.length > 0 || "Name is required"
4682
5830
  }));
4683
5831
  const subdomain = opts.subdomain || (nonInteractive ? name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") : await input({
4684
- message: G5("ENS subdomain"),
5832
+ message: G6("ENS subdomain"),
4685
5833
  default: name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""),
4686
5834
  validate: (v) => v.length >= 3 || "Must be at least 3 characters"
4687
5835
  }));
4688
5836
  const description = opts.description || (nonInteractive ? `${name} \u2014 a Sherwood syndicate` : await input({
4689
- message: G5("Description"),
5837
+ message: G6("Description"),
4690
5838
  default: `${name} \u2014 a Sherwood syndicate`
4691
5839
  }));
4692
- const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message: G5("Agent ID (ERC-8004)"), default: String(savedAgentId) }) : nonInteractive ? "0" : await input({ message: G5("Agent ID (ERC-8004)"), validate: (v) => /^\d+$/.test(v) || "Must be a number" }));
5840
+ const agentIdStr = opts.agentId || (savedAgentId ? nonInteractive ? String(savedAgentId) : await input({ message: G6("Agent ID (ERC-8004)"), default: String(savedAgentId) }) : nonInteractive ? "0" : await input({ message: G6("Agent ID (ERC-8004)"), validate: (v) => /^\d+$/.test(v) || "Must be a number" }));
4693
5841
  const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : nonInteractive ? true : await confirm({
4694
- message: G5("Open deposits? (anyone can deposit)"),
5842
+ message: G6("Open deposits? (anyone can deposit)"),
4695
5843
  default: true
4696
5844
  });
4697
5845
  const ASSET_SYMBOLS = {
@@ -4707,14 +5855,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
4707
5855
  asset = opts.asset;
4708
5856
  } else {
4709
5857
  const supported = Object.keys(ASSET_SYMBOLS).join(", ");
4710
- console.error(chalk9.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
5858
+ console.error(chalk15.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
4711
5859
  process.exit(1);
4712
5860
  }
4713
5861
  } else if (nonInteractive) {
4714
5862
  asset = TOKENS().USDC !== "0x0000000000000000000000000000000000000000" ? ASSET_SYMBOLS.USDC : ASSET_SYMBOLS.WETH;
4715
5863
  } else {
4716
5864
  const assetChoice = await select({
4717
- message: G5("Vault asset (what token do depositors provide?)"),
5865
+ message: G6("Vault asset (what token do depositors provide?)"),
4718
5866
  choices: [
4719
5867
  { name: "USDC", value: "USDC", description: "USD Coin (6 decimals)" },
4720
5868
  { name: "WETH", value: "WETH", description: "Wrapped Ether (18 decimals)" }
@@ -4730,25 +5878,25 @@ syndicate.command("create").description("Create a new syndicate via the factory
4730
5878
  const symbol = `sw${assetSymbol}`;
4731
5879
  console.log();
4732
5880
  console.log(LABEL5(" \u25C6 Review"));
4733
- SEP5();
4734
- console.log(W5(` Name: ${BOLD5(name)}`));
4735
- console.log(W5(` ENS: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
4736
- console.log(W5(` Description: ${DIM5(description)}`));
4737
- console.log(W5(` Agent ID: #${agentIdStr}`));
4738
- console.log(W5(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
4739
- console.log(W5(` Share token: ${symbol}`));
4740
- console.log(W5(` Open deposits: ${openDeposits ? G5("yes") : chalk9.red("no (whitelist)")}`));
4741
- SEP5();
5881
+ SEP6();
5882
+ console.log(W6(` Name: ${BOLD6(name)}`));
5883
+ console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
5884
+ console.log(W6(` Description: ${DIM6(description)}`));
5885
+ console.log(W6(` Agent ID: #${agentIdStr}`));
5886
+ console.log(W6(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
5887
+ console.log(W6(` Share token: ${symbol}`));
5888
+ console.log(W6(` Open deposits: ${openDeposits ? G6("yes") : chalk15.red("no (whitelist)")}`));
5889
+ SEP6();
4742
5890
  if (!nonInteractive) {
4743
- const go = await confirm({ message: G5("Deploy syndicate?"), default: true });
5891
+ const go = await confirm({ message: G6("Deploy syndicate?"), default: true });
4744
5892
  if (!go) {
4745
- console.log(DIM5(" Cancelled."));
5893
+ console.log(DIM6(" Cancelled."));
4746
5894
  return;
4747
5895
  }
4748
5896
  }
4749
5897
  let metadataURI = opts.metadataUri || "";
4750
5898
  if (!metadataURI) {
4751
- const spinner2 = ora8({ text: W5("Uploading metadata to IPFS..."), color: "green" }).start();
5899
+ const spinner2 = ora8({ text: W6("Uploading metadata to IPFS..."), color: "green" }).start();
4752
5900
  try {
4753
5901
  const metadata = {
4754
5902
  schema: "sherwood/syndicate/v1",
@@ -4762,9 +5910,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
4762
5910
  links: {}
4763
5911
  };
4764
5912
  metadataURI = await uploadMetadata(metadata);
4765
- spinner2.succeed(G5(`Metadata pinned: ${DIM5(metadataURI)}`));
5913
+ spinner2.succeed(G6(`Metadata pinned: ${DIM6(metadataURI)}`));
4766
5914
  } catch (err) {
4767
- spinner2.warn(chalk9.yellow(`IPFS upload failed \u2014 using inline metadata`));
5915
+ spinner2.warn(chalk15.yellow(`IPFS upload failed \u2014 using inline metadata`));
4768
5916
  const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
4769
5917
  metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
4770
5918
  }
@@ -4774,43 +5922,43 @@ syndicate.command("create").description("Create a new syndicate via the factory
4774
5922
  const existingInfo = await getSyndicate(existingId);
4775
5923
  const callerAddress = getAccount().address.toLowerCase();
4776
5924
  if (existingInfo.creator.toLowerCase() !== callerAddress) {
4777
- console.error(chalk9.red(`
5925
+ console.error(chalk15.red(`
4778
5926
  \u2716 Subdomain '${subdomain}' is already taken by another syndicate.
4779
5927
  `));
4780
5928
  process.exit(1);
4781
5929
  }
4782
- console.log(chalk9.yellow(`
5930
+ console.log(chalk15.yellow(`
4783
5931
  \u26A0 Syndicate '${subdomain}' already exists \u2014 resuming setup...
4784
5932
  `));
4785
- const spinner2 = ora8({ text: W5("Resuming setup..."), color: "green" }).start();
5933
+ const spinner2 = ora8({ text: W6("Resuming setup..."), color: "green" }).start();
4786
5934
  const chainId2 = getChain().id;
4787
5935
  setChainContract(chainId2, "vault", existingInfo.vault);
4788
5936
  addSyndicate(chainId2, { subdomain, vault: existingInfo.vault, role: "creator" });
4789
5937
  setPrimarySyndicate(chainId2, subdomain);
4790
- spinner2.text = W5("Checking agent registration...");
5938
+ spinner2.text = W6("Checking agent registration...");
4791
5939
  try {
4792
5940
  setVaultAddress(existingInfo.vault);
4793
5941
  const isAlreadyAgent = await isAgent(getAccount().address);
4794
5942
  if (!isAlreadyAgent) {
4795
- spinner2.text = W5("Registering creator as agent...");
5943
+ spinner2.text = W6("Registering creator as agent...");
4796
5944
  await registerAgent(BigInt(agentIdStr), getAccount().address);
4797
5945
  }
4798
5946
  } catch {
4799
5947
  }
4800
- spinner2.text = W5("Checking chat group...");
5948
+ spinner2.text = W6("Checking chat group...");
4801
5949
  try {
4802
5950
  const xmtp = await loadXmtp();
4803
5951
  const xmtpClient = await xmtp.getXmtpClient();
4804
5952
  try {
4805
5953
  await xmtp.getGroup(xmtpClient, subdomain);
4806
5954
  } catch {
4807
- spinner2.text = W5("Creating chat group...");
5955
+ spinner2.text = W6("Creating chat group...");
4808
5956
  const groupId = await xmtp.createSyndicateGroup(xmtpClient, subdomain, opts.publicChat);
4809
5957
  await setTextRecord(subdomain, "xmtpGroupId", groupId, existingInfo.vault);
4810
5958
  cacheGroupId(subdomain, groupId);
4811
5959
  }
4812
5960
  } catch {
4813
- console.warn(chalk9.yellow("\n \u26A0 Could not set up XMTP chat group"));
5961
+ console.warn(chalk15.yellow("\n \u26A0 Could not set up XMTP chat group"));
4814
5962
  }
4815
5963
  try {
4816
5964
  const cron = await loadCron();
@@ -4820,19 +5968,19 @@ syndicate.command("create").description("Create a new syndicate via the factory
4820
5968
  spinner2.stop();
4821
5969
  console.log();
4822
5970
  console.log(LABEL5(" \u25C6 Syndicate Setup Resumed"));
4823
- SEP5();
4824
- console.log(W5(` ID: ${G5(`#${existingId}`)}`));
4825
- console.log(W5(` Vault: ${G5(existingInfo.vault)}`));
4826
- console.log(W5(` ENS: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
4827
- console.log(W5(` Chat: ${DIM5(`sherwood chat ${subdomain}`)}`));
5971
+ SEP6();
5972
+ console.log(W6(` ID: ${G6(`#${existingId}`)}`));
5973
+ console.log(W6(` Vault: ${G6(existingInfo.vault)}`));
5974
+ console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
5975
+ console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
4828
5976
  const refParam2 = getAgentId() ? `?ref=${getAgentId()}` : "";
4829
- console.log(W5(` Share: ${G5(`https://sherwood.sh/syndicate/${subdomain}${refParam2}`)}`));
4830
- SEP5();
4831
- console.log(G5(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
5977
+ console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam2}`)}`));
5978
+ SEP6();
5979
+ console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
4832
5980
  console.log();
4833
5981
  return;
4834
5982
  }
4835
- const spinner = ora8({ text: W5("Deploying vault via factory..."), color: "green" }).start();
5983
+ const spinner = ora8({ text: W6("Deploying vault via factory..."), color: "green" }).start();
4836
5984
  const result = await createSyndicate({
4837
5985
  creatorAgentId: BigInt(agentIdStr),
4838
5986
  metadataURI,
@@ -4847,7 +5995,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
4847
5995
  addSyndicate(chainId, { subdomain, vault: result.vault, role: "creator" });
4848
5996
  setPrimarySyndicate(chainId, subdomain);
4849
5997
  await new Promise((r) => setTimeout(r, 2e3));
4850
- spinner.text = W5("Registering creator as agent...");
5998
+ spinner.text = W6("Registering creator as agent...");
4851
5999
  try {
4852
6000
  setVaultAddress(result.vault);
4853
6001
  const creatorAddress = getAccount().address;
@@ -4857,9 +6005,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
4857
6005
  // agentAddress = creator EOA (direct execution)
4858
6006
  );
4859
6007
  } catch (regErr) {
4860
- console.warn(chalk9.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
6008
+ console.warn(chalk15.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
4861
6009
  }
4862
- spinner.text = W5("Setting up chat...");
6010
+ spinner.text = W6("Setting up chat...");
4863
6011
  try {
4864
6012
  const xmtp = await loadXmtp();
4865
6013
  const xmtpClient = await xmtp.getXmtpClient();
@@ -4870,36 +6018,36 @@ syndicate.command("create").description("Create a new syndicate via the factory
4870
6018
  } catch {
4871
6019
  }
4872
6020
  } catch {
4873
- console.warn(chalk9.yellow("\n \u26A0 Could not create XMTP chat group"));
4874
- console.warn(chalk9.dim(` Recover later with: sherwood chat ${subdomain} init`));
6021
+ console.warn(chalk15.yellow("\n \u26A0 Could not create XMTP chat group"));
6022
+ console.warn(chalk15.dim(` Recover later with: sherwood chat ${subdomain} init`));
4875
6023
  }
4876
6024
  try {
4877
6025
  const cron = await loadCron();
4878
6026
  const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
4879
6027
  if (cronResult.isOpenClaw && cronResult.registered) {
4880
- console.log(G5(" \u2713 Participation crons registered (15m check + hourly summary)"));
6028
+ console.log(G6(" \u2713 Participation crons registered (15m check + hourly summary)"));
4881
6029
  } else if (!cronResult.isOpenClaw) {
4882
- console.log(DIM5(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
6030
+ console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
4883
6031
  }
4884
6032
  } catch {
4885
6033
  }
4886
6034
  spinner.stop();
4887
6035
  console.log();
4888
6036
  console.log(LABEL5(" \u25C6 Syndicate Created"));
4889
- SEP5();
4890
- console.log(W5(` ID: ${G5(`#${result.syndicateId}`)}`));
4891
- console.log(W5(` Vault: ${G5(result.vault)}`));
4892
- console.log(W5(` ENS: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
4893
- console.log(W5(` Metadata: ${DIM5(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
4894
- console.log(W5(` Explorer: ${DIM5(getExplorerUrl(result.hash))}`));
4895
- console.log(W5(` Chat: ${DIM5(`sherwood chat ${subdomain}`)}`));
6037
+ SEP6();
6038
+ console.log(W6(` ID: ${G6(`#${result.syndicateId}`)}`));
6039
+ console.log(W6(` Vault: ${G6(result.vault)}`));
6040
+ console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
6041
+ console.log(W6(` Metadata: ${DIM6(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
6042
+ console.log(W6(` Explorer: ${DIM6(getExplorerUrl(result.hash))}`));
6043
+ console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
4896
6044
  const refParam = getAgentId() ? `?ref=${getAgentId()}` : "";
4897
- console.log(W5(` Share: ${G5(`https://sherwood.sh/syndicate/${subdomain}${refParam}`)}`));
4898
- SEP5();
4899
- console.log(G5(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
6045
+ console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam}`)}`));
6046
+ SEP6();
6047
+ console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
4900
6048
  console.log();
4901
6049
  } catch (err) {
4902
- console.error(chalk9.red(`
6050
+ console.error(chalk15.red(`
4903
6051
  \u2716 ${formatContractError(err)}`));
4904
6052
  process.exit(1);
4905
6053
  }
@@ -4949,38 +6097,38 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
4949
6097
  }
4950
6098
  spinner.stop();
4951
6099
  if (syndicates.length === 0) {
4952
- console.log(chalk9.dim("No active syndicates found."));
6100
+ console.log(chalk15.dim("No active syndicates found."));
4953
6101
  return;
4954
6102
  }
4955
6103
  console.log();
4956
- console.log(chalk9.bold(`Active Syndicates (${syndicates.length})`));
6104
+ console.log(chalk15.bold(`Active Syndicates (${syndicates.length})`));
4957
6105
  if (!process.env.SUBGRAPH_URL && !opts.allChains) {
4958
- console.log(chalk9.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
6106
+ console.log(chalk15.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
4959
6107
  }
4960
6108
  if (opts.allChains) {
4961
- console.log(chalk9.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
6109
+ console.log(chalk15.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
4962
6110
  }
4963
- console.log(chalk9.dim("\u2500".repeat(70)));
6111
+ console.log(chalk15.dim("\u2500".repeat(70)));
4964
6112
  for (const s of syndicates) {
4965
6113
  const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
4966
6114
  const date = new Date(ts * 1e3).toLocaleDateString();
4967
6115
  const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
4968
- const chainTag = s.chain ? chalk9.dim(`[${s.chain}] `) : "";
4969
- console.log(` ${chainTag}#${s.id} ${chalk9.bold(ensName || String(s.vault))}`);
4970
- if (ensName) console.log(` Vault: ${chalk9.cyan(String(s.vault))}`);
6116
+ const chainTag = s.chain ? chalk15.dim(`[${s.chain}] `) : "";
6117
+ console.log(` ${chainTag}#${s.id} ${chalk15.bold(ensName || String(s.vault))}`);
6118
+ if (ensName) console.log(` Vault: ${chalk15.cyan(String(s.vault))}`);
4971
6119
  console.log(` Creator: ${s.creator}`);
4972
6120
  console.log(` Created: ${date}`);
4973
6121
  if (s.totalDeposits) {
4974
6122
  console.log(` Deposits: ${s.totalDeposits} USDC`);
4975
6123
  }
4976
6124
  if (s.metadataURI) {
4977
- console.log(` Metadata: ${chalk9.dim(s.metadataURI)}`);
6125
+ console.log(` Metadata: ${chalk15.dim(s.metadataURI)}`);
4978
6126
  }
4979
6127
  console.log();
4980
6128
  }
4981
6129
  } catch (err) {
4982
6130
  spinner.fail("Failed to load syndicates");
4983
- console.error(chalk9.red(formatContractError(err)));
6131
+ console.error(chalk15.red(formatContractError(err)));
4984
6132
  process.exit(1);
4985
6133
  }
4986
6134
  });
@@ -5020,38 +6168,38 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
5020
6168
  spinner.stop();
5021
6169
  if (crossChainHint) {
5022
6170
  const others = crossChainHint.others.length > 0 ? ` (also on: ${crossChainHint.others.join(", ")})` : "";
5023
- console.log(chalk9.yellow(` Found on chain: ${chalk9.bold(crossChainHint.network)}${others}`));
5024
- console.log(chalk9.dim(` Re-run with --chain ${crossChainHint.network} to interact with this syndicate.`));
6171
+ console.log(chalk15.yellow(` Found on chain: ${chalk15.bold(crossChainHint.network)}${others}`));
6172
+ console.log(chalk15.dim(` Re-run with --chain ${crossChainHint.network} to interact with this syndicate.`));
5025
6173
  console.log();
5026
6174
  }
5027
6175
  if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
5028
- console.log(chalk9.red(`Syndicate #${id} not found.`));
6176
+ console.log(chalk15.red(`Syndicate #${id} not found.`));
5029
6177
  process.exit(1);
5030
6178
  }
5031
6179
  const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
5032
6180
  console.log();
5033
- console.log(chalk9.bold(`Syndicate #${info.id}`));
5034
- console.log(chalk9.dim("\u2500".repeat(40)));
6181
+ console.log(chalk15.bold(`Syndicate #${info.id}`));
6182
+ console.log(chalk15.dim("\u2500".repeat(40)));
5035
6183
  if (info.subdomain) {
5036
- console.log(` ENS: ${chalk9.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
6184
+ console.log(` ENS: ${chalk15.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
5037
6185
  }
5038
- console.log(` Vault: ${chalk9.cyan(info.vault)}`);
6186
+ console.log(` Vault: ${chalk15.cyan(info.vault)}`);
5039
6187
  console.log(` Creator: ${info.creator}`);
5040
6188
  console.log(` Created: ${date}`);
5041
- console.log(` Active: ${info.active ? chalk9.green("yes") : chalk9.red("no")}`);
6189
+ console.log(` Active: ${info.active ? chalk15.green("yes") : chalk15.red("no")}`);
5042
6190
  if (info.metadataURI) {
5043
- console.log(` Metadata: ${chalk9.dim(info.metadataURI)}`);
6191
+ console.log(` Metadata: ${chalk15.dim(info.metadataURI)}`);
5044
6192
  }
5045
6193
  if (info.subdomain) {
5046
6194
  const xmtpGroupId = getCachedGroupId(info.subdomain);
5047
6195
  if (xmtpGroupId) {
5048
- console.log(` XMTP Group: ${chalk9.cyan(xmtpGroupId)}`);
6196
+ console.log(` XMTP Group: ${chalk15.cyan(xmtpGroupId)}`);
5049
6197
  }
5050
6198
  }
5051
6199
  setVaultAddress(info.vault);
5052
6200
  const vaultInfo = crossChainHint ? await withNetwork(crossChainHint.network, () => getVaultInfo()) : await getVaultInfo();
5053
6201
  console.log();
5054
- console.log(chalk9.bold(" Vault Stats"));
6202
+ console.log(chalk15.bold(" Vault Stats"));
5055
6203
  console.log(` Total Assets: ${vaultInfo.totalAssets}`);
5056
6204
  console.log(` Agent Count: ${vaultInfo.agentCount}`);
5057
6205
  console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
@@ -5059,12 +6207,12 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
5059
6207
  console.log();
5060
6208
  } catch (err) {
5061
6209
  spinner.fail("Failed to load syndicate info");
5062
- console.error(chalk9.red(formatContractError(err)));
6210
+ console.error(chalk15.red(formatContractError(err)));
5063
6211
  process.exit(1);
5064
6212
  }
5065
6213
  });
5066
6214
  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) => {
5067
- const spinner = ora8({ text: W5("Loading syndicate..."), color: "green" }).start();
6215
+ const spinner = ora8({ text: W6("Loading syndicate..."), color: "green" }).start();
5068
6216
  try {
5069
6217
  const syndicateId = BigInt(opts.id);
5070
6218
  let metadataURI = opts.uri;
@@ -5076,7 +6224,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
5076
6224
  }
5077
6225
  const name = opts.name || info.subdomain;
5078
6226
  const description = opts.description || `${name} \u2014 a Sherwood syndicate on ${info.subdomain}.sherwoodagent.eth`;
5079
- spinner.text = W5("Uploading metadata to IPFS...");
6227
+ spinner.text = W6("Uploading metadata to IPFS...");
5080
6228
  const metadata = {
5081
6229
  schema: "sherwood/syndicate/v1",
5082
6230
  name,
@@ -5087,15 +6235,15 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
5087
6235
  links: {}
5088
6236
  };
5089
6237
  metadataURI = await uploadMetadata(metadata);
5090
- spinner.text = W5("Updating on-chain metadata...");
6238
+ spinner.text = W6("Updating on-chain metadata...");
5091
6239
  }
5092
6240
  const hash = await updateMetadata(syndicateId, metadataURI);
5093
- spinner.succeed(G5(`Metadata updated`));
5094
- console.log(DIM5(` IPFS: ${metadataURI}`));
5095
- console.log(DIM5(` ${getExplorerUrl(hash)}`));
6241
+ spinner.succeed(G6(`Metadata updated`));
6242
+ console.log(DIM6(` IPFS: ${metadataURI}`));
6243
+ console.log(DIM6(` ${getExplorerUrl(hash)}`));
5096
6244
  } catch (err) {
5097
6245
  spinner.fail("Metadata update failed");
5098
- console.error(chalk9.red(formatContractError(err)));
6246
+ console.error(chalk15.red(formatContractError(err)));
5099
6247
  process.exit(1);
5100
6248
  }
5101
6249
  });
@@ -5106,10 +6254,10 @@ syndicate.command("approve-depositor").description("Approve an address to deposi
5106
6254
  const depositor = validateAddress(opts.depositor, "depositor");
5107
6255
  const hash = await approveDepositor(depositor);
5108
6256
  spinner.succeed(`Depositor approved: ${hash}`);
5109
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
6257
+ console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
5110
6258
  } catch (err) {
5111
6259
  spinner.fail("Approval failed");
5112
- console.error(chalk9.red(formatContractError(err)));
6260
+ console.error(chalk15.red(formatContractError(err)));
5113
6261
  process.exit(1);
5114
6262
  }
5115
6263
  });
@@ -5120,10 +6268,10 @@ syndicate.command("remove-depositor").description("Remove an address from the de
5120
6268
  const depositor = validateAddress(opts.depositor, "depositor");
5121
6269
  const hash = await removeDepositor(depositor);
5122
6270
  spinner.succeed(`Depositor removed: ${hash}`);
5123
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
6271
+ console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
5124
6272
  } catch (err) {
5125
6273
  spinner.fail("Removal failed");
5126
- console.error(chalk9.red(formatContractError(err)));
6274
+ console.error(chalk15.red(formatContractError(err)));
5127
6275
  process.exit(1);
5128
6276
  }
5129
6277
  });
@@ -5143,7 +6291,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5143
6291
  if (opts.agentId) {
5144
6292
  agentId = BigInt(opts.agentId);
5145
6293
  } else {
5146
- const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-DCQMJJFI.js");
6294
+ const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-DOWLGXGE.js");
5147
6295
  const registry2 = AGENT_REGISTRY2().IDENTITY_REGISTRY;
5148
6296
  if (registry2 === "0x0000000000000000000000000000000000000000") {
5149
6297
  agentId = 0n;
@@ -5164,7 +6312,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5164
6312
  });
5165
6313
  if (balance === 0n) {
5166
6314
  spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
5167
- console.error(chalk9.dim(" Mint one first: sherwood identity mint --name <name>"));
6315
+ console.error(chalk15.dim(" Mint one first: sherwood identity mint --name <name>"));
5168
6316
  process.exit(1);
5169
6317
  }
5170
6318
  const tokenId = await client.readContract({
@@ -5180,13 +6328,13 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5180
6328
  args: [agentWallet, 0n]
5181
6329
  });
5182
6330
  agentId = tokenId;
5183
- console.log(chalk9.dim(` Resolved agent ID: #${agentId}`));
6331
+ console.log(chalk15.dim(` Resolved agent ID: #${agentId}`));
5184
6332
  }
5185
6333
  }
5186
6334
  spinner.text = "Registering agent...";
5187
6335
  const hash = await registerAgent(agentId, agentWallet);
5188
6336
  spinner.succeed(`Agent registered: ${hash}`);
5189
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
6337
+ console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
5190
6338
  try {
5191
6339
  const xmtp = await loadXmtp();
5192
6340
  const xmtpClient = await xmtp.getXmtpClient();
@@ -5198,14 +6346,14 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5198
6346
  syndicate: subdomain,
5199
6347
  timestamp: Math.floor(Date.now() / 1e3)
5200
6348
  });
5201
- console.log(chalk9.dim(` Added to chat: ${subdomain}`));
6349
+ console.log(chalk15.dim(` Added to chat: ${subdomain}`));
5202
6350
  } catch {
5203
- console.warn(chalk9.yellow(" \u26A0 Could not add agent to chat group"));
5204
- console.warn(chalk9.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
6351
+ console.warn(chalk15.yellow(" \u26A0 Could not add agent to chat group"));
6352
+ console.warn(chalk15.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
5205
6353
  }
5206
6354
  } catch (err) {
5207
6355
  spinner.fail("Registration failed");
5208
- console.error(chalk9.red(formatContractError(err)));
6356
+ console.error(chalk15.red(formatContractError(err)));
5209
6357
  process.exit(1);
5210
6358
  }
5211
6359
  });
@@ -5213,7 +6361,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
5213
6361
  try {
5214
6362
  const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
5215
6363
  if (!subdomain) {
5216
- console.error(chalk9.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
6364
+ console.error(chalk15.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
5217
6365
  process.exit(1);
5218
6366
  }
5219
6367
  const syndicateInfo = await resolveSyndicate(subdomain);
@@ -5222,12 +6370,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
5222
6370
  const dashboardUrl = `https://sherwood.sh/syndicate/${subdomain}${refParam}`;
5223
6371
  console.log();
5224
6372
  console.log(LABEL5(" \u25C6 Share Your Syndicate"));
5225
- SEP5();
5226
- console.log(W5(` Dashboard: ${G5(dashboardUrl)}`));
5227
- console.log(W5(` ENS: ${DIM5(`${subdomain}.sherwoodagent.eth`)}`));
5228
- console.log(W5(` Vault: ${DIM5(syndicateInfo.vault)}`));
5229
- console.log(W5(` Join CLI: ${DIM5(`sherwood syndicate join --subdomain ${subdomain}`)}`));
5230
- SEP5();
6373
+ SEP6();
6374
+ console.log(W6(` Dashboard: ${G6(dashboardUrl)}`));
6375
+ console.log(W6(` ENS: ${DIM6(`${subdomain}.sherwoodagent.eth`)}`));
6376
+ console.log(W6(` Vault: ${DIM6(syndicateInfo.vault)}`));
6377
+ console.log(W6(` Join CLI: ${DIM6(`sherwood syndicate join --subdomain ${subdomain}`)}`));
6378
+ SEP6();
5231
6379
  try {
5232
6380
  const { exec } = await import("child_process");
5233
6381
  const platform = (await import("os")).platform();
@@ -5235,12 +6383,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
5235
6383
  const proc = exec(cmd);
5236
6384
  proc.stdin?.write(dashboardUrl);
5237
6385
  proc.stdin?.end();
5238
- console.log(G5(" \u2713 Dashboard URL copied to clipboard"));
6386
+ console.log(G6(" \u2713 Dashboard URL copied to clipboard"));
5239
6387
  } catch {
5240
6388
  }
5241
6389
  console.log();
5242
6390
  } catch (err) {
5243
- console.error(chalk9.red(` \u2716 ${formatContractError(err)}`));
6391
+ console.error(chalk15.red(` \u2716 ${formatContractError(err)}`));
5244
6392
  process.exit(1);
5245
6393
  }
5246
6394
  });
@@ -5265,14 +6413,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5265
6413
  const hits = await searchSyndicateAcrossChains(subdomain);
5266
6414
  spinner.fail(`Syndicate "${subdomain}" not found on ${getNetwork()}`);
5267
6415
  if (hits.length === 0) {
5268
- console.error(chalk9.red(` Not found on any supported chain.`));
6416
+ console.error(chalk15.red(` Not found on any supported chain.`));
5269
6417
  } else if (hits.length === 1) {
5270
- console.error(chalk9.yellow(` Found on chain: ${chalk9.bold(hits[0].network)}`));
5271
- console.error(chalk9.dim(` Re-run: sherwood --chain ${hits[0].network} syndicate join --subdomain ${subdomain}`));
6418
+ console.error(chalk15.yellow(` Found on chain: ${chalk15.bold(hits[0].network)}`));
6419
+ console.error(chalk15.dim(` Re-run: sherwood --chain ${hits[0].network} syndicate join --subdomain ${subdomain}`));
5272
6420
  } else {
5273
6421
  const chains = hits.map((h) => h.network).join(", ");
5274
- console.error(chalk9.yellow(` Found on chains: ${chalk9.bold(chains)}`));
5275
- console.error(chalk9.dim(` Specify which chain to join with --chain <network>.`));
6422
+ console.error(chalk15.yellow(` Found on chains: ${chalk15.bold(chains)}`));
6423
+ console.error(chalk15.dim(` Specify which chain to join with --chain <network>.`));
5276
6424
  }
5277
6425
  process.exit(1);
5278
6426
  }
@@ -5285,17 +6433,17 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5285
6433
  try {
5286
6434
  const xmtp = await loadXmtp();
5287
6435
  await xmtp.getXmtpClient();
5288
- console.log(chalk9.dim(" XMTP identity ready"));
6436
+ console.log(chalk15.dim(" XMTP identity ready"));
5289
6437
  } catch {
5290
- console.warn(chalk9.yellow(" \u26A0 Could not initialize XMTP identity"));
6438
+ console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity"));
5291
6439
  }
5292
6440
  try {
5293
6441
  const cron = await loadCron();
5294
6442
  const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
5295
6443
  if (cronResult.isOpenClaw && cronResult.registered) {
5296
- console.log(chalk9.green(" \u2713 Participation crons registered"));
6444
+ console.log(chalk15.green(" \u2713 Participation crons registered"));
5297
6445
  } else if (!cronResult.isOpenClaw) {
5298
- console.log(chalk9.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
6446
+ console.log(chalk15.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
5299
6447
  }
5300
6448
  } catch {
5301
6449
  }
@@ -5308,14 +6456,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5308
6456
  );
5309
6457
  if (existingRequest) {
5310
6458
  spinner.succeed("You already have a pending join request for this syndicate");
5311
- console.log(chalk9.dim(` Attestation: ${existingRequest.uid}`));
5312
- console.log(chalk9.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
6459
+ console.log(chalk15.dim(` Attestation: ${existingRequest.uid}`));
6460
+ console.log(chalk15.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
5313
6461
  try {
5314
6462
  const xmtp = await loadXmtp();
5315
6463
  await xmtp.getXmtpClient();
5316
- console.log(chalk9.dim(" XMTP identity ready"));
6464
+ console.log(chalk15.dim(" XMTP identity ready"));
5317
6465
  } catch {
5318
- console.warn(chalk9.yellow(" \u26A0 Could not initialize XMTP identity"));
6466
+ console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity"));
5319
6467
  }
5320
6468
  return;
5321
6469
  }
@@ -5336,7 +6484,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5336
6484
  spinner.succeed("Join request created (XMTP identity ready)");
5337
6485
  } catch {
5338
6486
  spinner.succeed("Join request created");
5339
- console.warn(chalk9.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
6487
+ console.warn(chalk15.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
5340
6488
  }
5341
6489
  addSyndicate(getChain().id, { subdomain, vault: syndicate2.vault, role: "agent" });
5342
6490
  setPrimarySyndicate(getChain().id, subdomain);
@@ -5344,29 +6492,29 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5344
6492
  const cron = await loadCron();
5345
6493
  const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
5346
6494
  if (cronResult.isOpenClaw && cronResult.registered) {
5347
- console.log(G5(" \u2713 Participation crons registered (will activate after approval)"));
6495
+ console.log(G6(" \u2713 Participation crons registered (will activate after approval)"));
5348
6496
  } else if (!cronResult.isOpenClaw) {
5349
- console.log(DIM5(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
6497
+ console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
5350
6498
  }
5351
6499
  } catch {
5352
6500
  }
5353
6501
  console.log();
5354
6502
  console.log(LABEL5(" \u25C6 Join Request Submitted"));
5355
- SEP5();
5356
- console.log(W5(` Syndicate: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
5357
- console.log(W5(` Agent ID: #${agentId}`));
5358
- console.log(W5(` Creator: ${DIM5(syndicate2.creator)}`));
5359
- console.log(W5(` Attestation: ${DIM5(uid)}`));
5360
- console.log(W5(` EAS Scan: ${DIM5(getEasScanUrl(uid))}`));
5361
- console.log(W5(` Explorer: ${DIM5(getEasExplorerUrl(hash))}`));
5362
- SEP5();
5363
- console.log(G5(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
5364
- console.log(G5(" \u2713 The creator can review with:"));
5365
- console.log(DIM5(` sherwood syndicate requests --subdomain ${subdomain}`));
6503
+ SEP6();
6504
+ console.log(W6(` Syndicate: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
6505
+ console.log(W6(` Agent ID: #${agentId}`));
6506
+ console.log(W6(` Creator: ${DIM6(syndicate2.creator)}`));
6507
+ console.log(W6(` Attestation: ${DIM6(uid)}`));
6508
+ console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(uid))}`));
6509
+ console.log(W6(` Explorer: ${DIM6(getEasExplorerUrl(hash))}`));
6510
+ SEP6();
6511
+ console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
6512
+ console.log(G6(" \u2713 The creator can review with:"));
6513
+ console.log(DIM6(` sherwood syndicate requests --subdomain ${subdomain}`));
5366
6514
  console.log();
5367
6515
  } catch (err) {
5368
6516
  spinner.fail("Join request failed");
5369
- console.error(chalk9.red(formatContractError(err)));
6517
+ console.error(chalk15.red(formatContractError(err)));
5370
6518
  process.exit(1);
5371
6519
  }
5372
6520
  });
@@ -5407,34 +6555,34 @@ syndicate.command("requests").description("View pending join requests for a synd
5407
6555
  );
5408
6556
  spinner.stop();
5409
6557
  if (requests.length === 0) {
5410
- console.log(DIM5("\n No pending join requests.\n"));
6558
+ console.log(DIM6("\n No pending join requests.\n"));
5411
6559
  return;
5412
6560
  }
5413
6561
  console.log();
5414
6562
  console.log(LABEL5(` \u25C6 Pending Join Requests (${requests.length})`));
5415
- SEP5();
6563
+ SEP6();
5416
6564
  for (let i = 0; i < requests.length; i++) {
5417
6565
  const req = requests[i];
5418
6566
  const date = new Date(req.time * 1e3).toLocaleString();
5419
6567
  const referrer = parseReferrer(req.decoded.message);
5420
6568
  const cleanMessage = stripReferrerPrefix(req.decoded.message);
5421
- console.log(W5(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM5(`(${req.attester})`)}`));
5422
- console.log(DIM5(` Message: "${cleanMessage}"`));
6569
+ console.log(W6(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM6(`(${req.attester})`)}`));
6570
+ console.log(DIM6(` Message: "${cleanMessage}"`));
5423
6571
  if (referrer !== null) {
5424
- console.log(G5(` Referred by: Agent #${referrer}`));
6572
+ console.log(G6(` Referred by: Agent #${referrer}`));
5425
6573
  }
5426
- console.log(DIM5(` Requested: ${date}`));
5427
- console.log(DIM5(` Attestation: ${req.uid}`));
6574
+ console.log(DIM6(` Requested: ${date}`));
6575
+ console.log(DIM6(` Attestation: ${req.uid}`));
5428
6576
  console.log();
5429
6577
  }
5430
- console.log(G5(" To approve:"));
5431
- console.log(DIM5(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
5432
- console.log(G5(" To reject:"));
5433
- console.log(DIM5(` sherwood syndicate reject --attestation <uid>`));
6578
+ console.log(G6(" To approve:"));
6579
+ console.log(DIM6(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
6580
+ console.log(G6(" To reject:"));
6581
+ console.log(DIM6(` sherwood syndicate reject --attestation <uid>`));
5434
6582
  console.log();
5435
6583
  } catch (err) {
5436
6584
  spinner.fail("Failed to load requests");
5437
- console.error(chalk9.red(formatContractError(err)));
6585
+ console.error(chalk15.red(formatContractError(err)));
5438
6586
  process.exit(1);
5439
6587
  }
5440
6588
  });
@@ -5464,11 +6612,11 @@ syndicate.command("approve").description("Approve an agent join request (registe
5464
6612
  agentWallet
5465
6613
  );
5466
6614
  agentWasRegistered = true;
5467
- console.log(DIM5(` Agent registered: ${getExplorerUrl(regHash)}`));
6615
+ console.log(DIM6(` Agent registered: ${getExplorerUrl(regHash)}`));
5468
6616
  } catch (regErr) {
5469
6617
  const msg = regErr instanceof Error ? regErr.message : String(regErr);
5470
6618
  if (msg.includes("0xe098d3ee") || msg.includes("AgentAlreadyRegistered")) {
5471
- console.log(DIM5(" Agent already registered on vault \u2014 skipping"));
6619
+ console.log(DIM6(" Agent already registered on vault \u2014 skipping"));
5472
6620
  } else {
5473
6621
  throw regErr;
5474
6622
  }
@@ -5484,7 +6632,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
5484
6632
  let approvalUid;
5485
6633
  if (alreadyApproved) {
5486
6634
  approvalUid = alreadyApproved.uid;
5487
- console.log(DIM5(` Approval attestation already exists \u2014 skipping`));
6635
+ console.log(DIM6(` Approval attestation already exists \u2014 skipping`));
5488
6636
  } else {
5489
6637
  spinner.text = "Creating approval attestation...";
5490
6638
  const result = await createApproval(
@@ -5507,23 +6655,23 @@ syndicate.command("approve").description("Approve an agent join request (registe
5507
6655
  syndicate: subdomain,
5508
6656
  timestamp: Math.floor(Date.now() / 1e3)
5509
6657
  });
5510
- console.log(DIM5(` Added to chat: ${subdomain}`));
6658
+ console.log(DIM6(` Added to chat: ${subdomain}`));
5511
6659
  } catch {
5512
- console.warn(chalk9.yellow(" \u26A0 Could not add agent to chat group"));
5513
- console.warn(chalk9.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
6660
+ console.warn(chalk15.yellow(" \u26A0 Could not add agent to chat group"));
6661
+ console.warn(chalk15.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
5514
6662
  }
5515
6663
  spinner.succeed("Agent approved and registered");
5516
6664
  console.log();
5517
6665
  console.log(LABEL5(" \u25C6 Agent Approved"));
5518
- SEP5();
5519
- console.log(W5(` Agent ID: #${opts.agentId}`));
5520
- console.log(W5(` Wallet: ${G5(opts.wallet)}`));
5521
- console.log(W5(` Approval: ${DIM5(approvalUid)}`));
5522
- console.log(W5(` EAS Scan: ${DIM5(getEasScanUrl(approvalUid))}`));
5523
- SEP5();
6666
+ SEP6();
6667
+ console.log(W6(` Agent ID: #${opts.agentId}`));
6668
+ console.log(W6(` Wallet: ${G6(opts.wallet)}`));
6669
+ console.log(W6(` Approval: ${DIM6(approvalUid)}`));
6670
+ console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(approvalUid))}`));
6671
+ SEP6();
5524
6672
  } catch (err) {
5525
6673
  spinner.fail("Approval failed");
5526
- console.error(chalk9.red(formatContractError(err)));
6674
+ console.error(chalk15.red(formatContractError(err)));
5527
6675
  process.exit(1);
5528
6676
  }
5529
6677
  });
@@ -5532,17 +6680,17 @@ syndicate.command("reject").description("Reject a join request by revoking its a
5532
6680
  try {
5533
6681
  const hash = await revokeJoinRequest(opts.attestation);
5534
6682
  spinner.succeed("Join request rejected");
5535
- console.log(DIM5(` ${getEasExplorerUrl(hash)}`));
6683
+ console.log(DIM6(` ${getEasExplorerUrl(hash)}`));
5536
6684
  } catch (err) {
5537
6685
  spinner.fail("Rejection failed");
5538
- console.error(chalk9.red(formatContractError(err)));
6686
+ console.error(chalk15.red(formatContractError(err)));
5539
6687
  process.exit(1);
5540
6688
  }
5541
6689
  });
5542
6690
  syndicate.command("leave").description("Leave a syndicate \u2014 removes participation crons and session state").option("--subdomain <name>", "Syndicate subdomain to leave").action(async (opts) => {
5543
6691
  const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
5544
6692
  if (!subdomain) {
5545
- console.error(chalk9.red(" No syndicate configured. Pass --subdomain <name>."));
6693
+ console.error(chalk15.red(" No syndicate configured. Pass --subdomain <name>."));
5546
6694
  process.exit(1);
5547
6695
  }
5548
6696
  const spinner = ora8("Cleaning up...").start();
@@ -5558,17 +6706,17 @@ syndicate.command("leave").description("Leave a syndicate \u2014 removes partici
5558
6706
  resetSession(subdomain);
5559
6707
  spinner.succeed("Left syndicate");
5560
6708
  if (cronsRemoved) {
5561
- console.log(G5(" \u2713 Participation crons removed"));
6709
+ console.log(G6(" \u2713 Participation crons removed"));
5562
6710
  }
5563
- console.log(G5(" \u2713 Session state cleared"));
6711
+ console.log(G6(" \u2713 Session state cleared"));
5564
6712
  console.log();
5565
- console.log(chalk9.dim(" Note: This does not remove you on-chain. To exit your position:"));
5566
- console.log(chalk9.dim(" sherwood vault balance \u2014 check your LP share balance"));
5567
- console.log(chalk9.dim(" Redeem shares via the vault contract or dashboard"));
6713
+ console.log(chalk15.dim(" Note: This does not remove you on-chain. To exit your position:"));
6714
+ console.log(chalk15.dim(" sherwood vault balance \u2014 check your LP share balance"));
6715
+ console.log(chalk15.dim(" Redeem shares via the vault contract or dashboard"));
5568
6716
  console.log();
5569
6717
  } catch (err) {
5570
6718
  spinner.fail("Leave failed");
5571
- console.error(chalk9.red(err instanceof Error ? err.message : String(err)));
6719
+ console.error(chalk15.red(err instanceof Error ? err.message : String(err)));
5572
6720
  process.exit(1);
5573
6721
  }
5574
6722
  });
@@ -5579,21 +6727,21 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
5579
6727
  if (!opts.subdomain && !opts.vault) {
5580
6728
  const primary = getPrimarySyndicate(chainId);
5581
6729
  if (memberships.length === 0) {
5582
- console.log(DIM5("\n No syndicates saved. Create or join one first.\n"));
6730
+ console.log(DIM6("\n No syndicates saved. Create or join one first.\n"));
5583
6731
  return;
5584
6732
  }
5585
6733
  console.log();
5586
6734
  console.log(LABEL5(" \u25C6 Your Syndicates"));
5587
- SEP5();
6735
+ SEP6();
5588
6736
  for (const m of memberships) {
5589
6737
  const isPrimary = primary?.subdomain === m.subdomain;
5590
- const marker = isPrimary ? G5(" \u2605") : "";
5591
- console.log(W5(` ${m.subdomain}.sherwoodagent.eth${marker} ${DIM5(`(${m.role})`)}`));
5592
- console.log(DIM5(` vault: ${m.vault}`));
6738
+ const marker = isPrimary ? G6(" \u2605") : "";
6739
+ console.log(W6(` ${m.subdomain}.sherwoodagent.eth${marker} ${DIM6(`(${m.role})`)}`));
6740
+ console.log(DIM6(` vault: ${m.vault}`));
5593
6741
  }
5594
- SEP5();
6742
+ SEP6();
5595
6743
  if (memberships.length > 1) {
5596
- console.log(DIM5(" Switch with: sherwood syndicate set-primary --subdomain <name>"));
6744
+ console.log(DIM6(" Switch with: sherwood syndicate set-primary --subdomain <name>"));
5597
6745
  }
5598
6746
  console.log();
5599
6747
  return;
@@ -5619,15 +6767,15 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
5619
6767
  }
5620
6768
  }
5621
6769
  if (!target) {
5622
- console.error(chalk9.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
6770
+ console.error(chalk15.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
5623
6771
  process.exit(1);
5624
6772
  }
5625
6773
  setPrimarySyndicate(chainId, target);
5626
- console.log(G5(`
6774
+ console.log(G6(`
5627
6775
  \u2713 Primary syndicate set to ${target}.sherwoodagent.eth
5628
6776
  `));
5629
6777
  } catch (err) {
5630
- console.error(chalk9.red(formatContractError(err)));
6778
+ console.error(chalk15.red(formatContractError(err)));
5631
6779
  process.exit(1);
5632
6780
  }
5633
6781
  });
@@ -5635,13 +6783,13 @@ var vaultCmd = program.command("vault");
5635
6783
  vaultCmd.command("deposit").description("Deposit into a vault").option("--vault <address>", "Vault address (default: from config)").requiredOption("--amount <amount>", "Amount to deposit (in asset units)").option("--use-eth", "Auto-wrap ETH \u2192 WETH before depositing (for WETH vaults)").action(async (opts) => {
5636
6784
  resolveVault(opts);
5637
6785
  const decimals = await getAssetDecimals();
5638
- const amount = parseUnits6(opts.amount, decimals);
6786
+ const amount = parseUnits7(opts.amount, decimals);
5639
6787
  try {
5640
6788
  if (!opts.useEth) {
5641
6789
  await preflightDeposit(amount);
5642
6790
  }
5643
6791
  } catch (err) {
5644
- console.error(chalk9.red(`
6792
+ console.error(chalk15.red(`
5645
6793
  \u2716 ${err instanceof Error ? err.message : String(err)}
5646
6794
  `));
5647
6795
  process.exit(1);
@@ -5656,10 +6804,10 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
5656
6804
  hash = await deposit(amount);
5657
6805
  }
5658
6806
  spinner.succeed(`Deposited: ${hash}`);
5659
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
6807
+ console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
5660
6808
  } catch (err) {
5661
6809
  spinner.fail("Deposit failed");
5662
- console.error(chalk9.red(formatContractError(err)));
6810
+ console.error(chalk15.red(formatContractError(err)));
5663
6811
  process.exit(1);
5664
6812
  }
5665
6813
  });
@@ -5670,8 +6818,8 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
5670
6818
  const info = await getVaultInfo();
5671
6819
  spinner.stop();
5672
6820
  console.log();
5673
- console.log(chalk9.bold("Vault Info"));
5674
- console.log(chalk9.dim("\u2500".repeat(40)));
6821
+ console.log(chalk15.bold("Vault Info"));
6822
+ console.log(chalk15.dim("\u2500".repeat(40)));
5675
6823
  console.log(` Address: ${info.address}`);
5676
6824
  console.log(` Total Assets: ${info.totalAssets}`);
5677
6825
  console.log(` Agent Count: ${info.agentCount}`);
@@ -5680,7 +6828,7 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
5680
6828
  console.log();
5681
6829
  } catch (err) {
5682
6830
  spinner.fail("Failed to load vault info");
5683
- console.error(chalk9.red(formatContractError(err)));
6831
+ console.error(chalk15.red(formatContractError(err)));
5684
6832
  process.exit(1);
5685
6833
  }
5686
6834
  });
@@ -5691,15 +6839,15 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
5691
6839
  const balance = await getBalance(opts.address);
5692
6840
  spinner.stop();
5693
6841
  console.log();
5694
- console.log(chalk9.bold("LP Position"));
5695
- console.log(chalk9.dim("\u2500".repeat(40)));
6842
+ console.log(chalk15.bold("LP Position"));
6843
+ console.log(chalk15.dim("\u2500".repeat(40)));
5696
6844
  console.log(` Shares: ${balance.shares.toString()}`);
5697
6845
  console.log(` Asset Value: ${balance.assetsValue}`);
5698
6846
  console.log(` % of Vault: ${balance.percentOfVault}`);
5699
6847
  console.log();
5700
6848
  } catch (err) {
5701
6849
  spinner.fail("Failed to load balance");
5702
- console.error(chalk9.red(formatContractError(err)));
6850
+ console.error(chalk15.red(formatContractError(err)));
5703
6851
  process.exit(1);
5704
6852
  }
5705
6853
  });
@@ -5710,22 +6858,22 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
5710
6858
  let shares;
5711
6859
  try {
5712
6860
  if (opts.shares) {
5713
- shares = parseUnits6(opts.shares, shareDecimals);
6861
+ shares = parseUnits7(opts.shares, shareDecimals);
5714
6862
  } else {
5715
6863
  shares = await getShareBalance();
5716
6864
  if (shares === 0n) {
5717
- console.error(chalk9.red("\n \u2716 No shares to redeem.\n"));
6865
+ console.error(chalk15.red("\n \u2716 No shares to redeem.\n"));
5718
6866
  process.exit(1);
5719
6867
  }
5720
6868
  }
5721
6869
  } catch (err) {
5722
- console.error(chalk9.red(`
6870
+ console.error(chalk15.red(`
5723
6871
  \u2716 ${err instanceof Error ? err.message : String(err)}
5724
6872
  `));
5725
6873
  process.exit(1);
5726
6874
  }
5727
6875
  if (opts.receiver && !isAddress6(opts.receiver)) {
5728
- console.error(chalk9.red(`
6876
+ console.error(chalk15.red(`
5729
6877
  \u2716 Invalid receiver address: ${opts.receiver}
5730
6878
  `));
5731
6879
  process.exit(1);
@@ -5733,7 +6881,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
5733
6881
  try {
5734
6882
  await preflightRedeem(shares);
5735
6883
  } catch (err) {
5736
- console.error(chalk9.red(`
6884
+ console.error(chalk15.red(`
5737
6885
  \u2716 ${err instanceof Error ? err.message : String(err)}
5738
6886
  `));
5739
6887
  process.exit(1);
@@ -5746,18 +6894,18 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
5746
6894
  opts.receiver
5747
6895
  );
5748
6896
  spinner.succeed(`Redeemed: ${hash}`);
5749
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
6897
+ console.log(chalk15.dim(` ${getExplorerUrl(hash)}`));
5750
6898
  console.log(` Assets received: ${formatUnits5(assetsOut, assetDecimals)}`);
5751
6899
  } catch (err) {
5752
6900
  spinner.fail("Redeem failed");
5753
- console.error(chalk9.red(formatContractError(err)));
6901
+ console.error(chalk15.red(formatContractError(err)));
5754
6902
  process.exit(1);
5755
6903
  }
5756
6904
  });
5757
6905
  var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
5758
6906
  registerStrategyTemplateCommands(strategy);
5759
6907
  program.command("providers").description("List available DeFi providers").action(async () => {
5760
- const { MessariProvider, NansenProvider } = await import("./research-NR4VC5LN.js");
6908
+ const { MessariProvider, NansenProvider } = await import("./research-YJWSQS2A.js");
5761
6909
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
5762
6910
  for (const p of providers) {
5763
6911
  const info = p.info();
@@ -5768,17 +6916,17 @@ ${info.name} (${info.type})`);
5768
6916
  }
5769
6917
  });
5770
6918
  try {
5771
- const { registerChatCommands } = await import("./chat-UWJ6JBFE.js");
6919
+ const { registerChatCommands } = await import("./chat-MRTPUZ3X.js");
5772
6920
  registerChatCommands(program);
5773
6921
  } catch {
5774
6922
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
5775
- console.error(chalk9.red("XMTP CLI not available."));
5776
- console.error(chalk9.dim("Install with: npm install -g @xmtp/cli"));
5777
- console.error(chalk9.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
6923
+ console.error(chalk15.red("XMTP CLI not available."));
6924
+ console.error(chalk15.dim("Install with: npm install -g @xmtp/cli"));
6925
+ console.error(chalk15.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
5778
6926
  process.exit(1);
5779
6927
  });
5780
6928
  }
5781
- var { registerSessionCommands } = await import("./session-NUPHC6ZN.js");
6929
+ var { registerSessionCommands } = await import("./session-FPCPCVQX.js");
5782
6930
  registerSessionCommands(program);
5783
6931
  registerVeniceCommands(program);
5784
6932
  registerAllowanceCommands(program);
@@ -5786,65 +6934,71 @@ registerIdentityCommands(program);
5786
6934
  registerProposalCommands(program);
5787
6935
  registerGovernorCommands(program);
5788
6936
  registerGuardianCommands(program);
5789
- var { registerResearchCommands } = await import("./research-J3HYU3N6.js");
6937
+ var { registerResearchCommands } = await import("./research-QBKISW2M.js");
5790
6938
  registerResearchCommands(program);
5791
- var { registerAgentCommands } = await import("./agent-5YEJ6W6W.js");
6939
+ var { registerAgentCommands } = await import("./agent-ZPAZTJG3.js");
5792
6940
  registerAgentCommands(program);
5793
- var { registerTradeCommands } = await import("./trade-VGECC4QZ.js");
6941
+ var { registerTradeCommands } = await import("./trade-NO6CNWCA.js");
5794
6942
  registerTradeCommands(program);
6943
+ registerGridCommand(program);
5795
6944
  var configCmd = program.command("config");
5796
- 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").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").option("--xmtp-group <subdomain:groupId>", "Cache an XMTP group ID for a syndicate (e.g. my-fund:abc123)").action((opts) => {
6945
+ 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").option("--notify-to <id>", "Destination for cron summaries (Telegram chat ID, phone, etc.)").option("--uniswap-api-key <key>", "Uniswap Trading API key (from developers.uniswap.org)").option("--venice-api-key <key>", "Venice AI inference API key").option("--anthropic-api-key <key>", "Anthropic Claude API key (for LLM judge)").option("--xmtp-group <subdomain:groupId>", "Cache an XMTP group ID for a syndicate (e.g. my-fund:abc123)").action((opts) => {
5797
6946
  let saved = false;
5798
6947
  if (opts.privateKey) {
5799
6948
  setPrivateKey(opts.privateKey);
5800
6949
  const account = getAccount();
5801
- console.log(chalk9.green("Private key saved to ~/.sherwood/config.json"));
5802
- console.log(chalk9.dim(` Wallet: ${account.address}`));
6950
+ console.log(chalk15.green("Private key saved to ~/.sherwood/config.json"));
6951
+ console.log(chalk15.dim(` Wallet: ${account.address}`));
5803
6952
  saved = true;
5804
6953
  }
5805
6954
  if (opts.vault) {
5806
6955
  const chainId = getChain().id;
5807
6956
  setChainContract(chainId, "vault", opts.vault);
5808
- console.log(chalk9.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
5809
- console.log(chalk9.dim(` Vault: ${opts.vault}`));
6957
+ console.log(chalk15.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
6958
+ console.log(chalk15.dim(` Vault: ${opts.vault}`));
5810
6959
  saved = true;
5811
6960
  }
5812
6961
  if (opts.rpc) {
5813
6962
  const network = getNetwork();
5814
6963
  setConfigRpcUrl(network, opts.rpc);
5815
- console.log(chalk9.green(`RPC URL saved for ${network}`));
5816
- console.log(chalk9.dim(` RPC: ${opts.rpc}`));
6964
+ console.log(chalk15.green(`RPC URL saved for ${network}`));
6965
+ console.log(chalk15.dim(` RPC: ${opts.rpc}`));
5817
6966
  saved = true;
5818
6967
  }
5819
6968
  if (opts.notifyTo) {
5820
6969
  setNotifyTo(opts.notifyTo);
5821
- console.log(chalk9.green("Notify destination saved to ~/.sherwood/config.json"));
5822
- console.log(chalk9.dim(` Notify to: ${opts.notifyTo}`));
6970
+ console.log(chalk15.green("Notify destination saved to ~/.sherwood/config.json"));
6971
+ console.log(chalk15.dim(` Notify to: ${opts.notifyTo}`));
5823
6972
  saved = true;
5824
6973
  }
5825
6974
  if (opts.uniswapApiKey) {
5826
6975
  setUniswapApiKey(opts.uniswapApiKey);
5827
- console.log(chalk9.green("Uniswap API key saved to ~/.sherwood/config.json"));
6976
+ console.log(chalk15.green("Uniswap API key saved to ~/.sherwood/config.json"));
5828
6977
  saved = true;
5829
6978
  }
5830
6979
  if (opts.veniceApiKey) {
5831
6980
  setVeniceApiKey(opts.veniceApiKey);
5832
- console.log(chalk9.green("Venice API key saved to ~/.sherwood/config.json"));
6981
+ console.log(chalk15.green("Venice API key saved to ~/.sherwood/config.json"));
6982
+ saved = true;
6983
+ }
6984
+ if (opts.anthropicApiKey) {
6985
+ setAnthropicApiKey(opts.anthropicApiKey);
6986
+ console.log(chalk15.green("Anthropic API key saved to ~/.sherwood/config.json"));
5833
6987
  saved = true;
5834
6988
  }
5835
6989
  if (opts.xmtpGroup) {
5836
6990
  const parts = opts.xmtpGroup.split(":");
5837
6991
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
5838
- console.log(chalk9.red("Format: --xmtp-group <subdomain>:<groupId>"));
6992
+ console.log(chalk15.red("Format: --xmtp-group <subdomain>:<groupId>"));
5839
6993
  process.exit(1);
5840
6994
  }
5841
6995
  cacheGroupId(parts[0], parts[1]);
5842
- console.log(chalk9.green(`XMTP group ID cached for ${parts[0]}`));
5843
- console.log(chalk9.dim(` Group ID: ${parts[1]}`));
6996
+ console.log(chalk15.green(`XMTP group ID cached for ${parts[0]}`));
6997
+ console.log(chalk15.dim(` Group ID: ${parts[1]}`));
5844
6998
  saved = true;
5845
6999
  }
5846
7000
  if (!saved) {
5847
- console.log(chalk9.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --xmtp-group"));
7001
+ console.log(chalk15.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --anthropic-api-key, --xmtp-group"));
5848
7002
  process.exit(1);
5849
7003
  }
5850
7004
  });
@@ -5855,25 +7009,25 @@ configCmd.command("show").description("Display current config for the active net
5855
7009
  const config = loadConfig();
5856
7010
  const customRpc = config.rpc?.[network];
5857
7011
  console.log();
5858
- console.log(chalk9.bold(`Sherwood Config`));
5859
- console.log(chalk9.dim("\u2500".repeat(50)));
5860
- console.log(` Network: ${chalk9.cyan(network)} (chain ${chainId})`);
5861
- console.log(` RPC: ${customRpc ? chalk9.green(customRpc) : chalk9.dim("default")}`);
5862
- console.log(` Wallet: ${config.privateKey ? chalk9.green("configured") : chalk9.dim("not set")}`);
5863
- console.log(` Agent ID: ${config.agentId ?? chalk9.dim("not set")}`);
5864
- console.log(` Vault: ${contracts.vault ?? chalk9.dim("not set")}`);
5865
- console.log(` Uniswap: ${getUniswapApiKey() ? chalk9.green("API key configured") : chalk9.dim("not set")}`);
5866
- console.log(` Venice: ${getVeniceApiKey() ? chalk9.green("API key configured") : chalk9.dim("not set")}`);
7012
+ console.log(chalk15.bold(`Sherwood Config`));
7013
+ console.log(chalk15.dim("\u2500".repeat(50)));
7014
+ console.log(` Network: ${chalk15.cyan(network)} (chain ${chainId})`);
7015
+ console.log(` RPC: ${customRpc ? chalk15.green(customRpc) : chalk15.dim("default")}`);
7016
+ console.log(` Wallet: ${config.privateKey ? chalk15.green("configured") : chalk15.dim("not set")}`);
7017
+ console.log(` Agent ID: ${config.agentId ?? chalk15.dim("not set")}`);
7018
+ console.log(` Vault: ${contracts.vault ?? chalk15.dim("not set")}`);
7019
+ console.log(` Uniswap: ${getUniswapApiKey() ? chalk15.green("API key configured") : chalk15.dim("not set")}`);
7020
+ console.log(` Venice: ${getVeniceApiKey() ? chalk15.green("API key configured") : chalk15.dim("not set")}`);
5867
7021
  const groupEntries = Object.entries(config.groupCache || {}).filter(([, v]) => v);
5868
7022
  if (groupEntries.length > 0) {
5869
7023
  console.log();
5870
- console.log(chalk9.bold(" XMTP Groups"));
7024
+ console.log(chalk15.bold(" XMTP Groups"));
5871
7025
  for (const [sub, gid] of groupEntries) {
5872
- console.log(` ${sub}: ${chalk9.cyan(gid)}`);
7026
+ console.log(` ${sub}: ${chalk15.cyan(gid)}`);
5873
7027
  }
5874
7028
  }
5875
7029
  console.log();
5876
- console.log(chalk9.dim(" Config file: ~/.sherwood/config.json"));
7030
+ console.log(chalk15.dim(" Config file: ~/.sherwood/config.json"));
5877
7031
  console.log();
5878
7032
  });
5879
7033
  program.parse();