@sherwoodagent/cli 0.41.5 → 0.47.1

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 (69) hide show
  1. package/dist/{addresses-RQ7DTIBS.js → addresses-DOWLGXGE.js} +4 -4
  2. package/dist/{agent-SXEV65ME.js → agent-4HKHLQEJ.js} +3035 -2029
  3. package/dist/agent-4HKHLQEJ.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-D5OF4OTL.js → chat-MRTPUZ3X.js} +8 -8
  7. package/dist/{chunk-2YOXRATG.js → chunk-2LRC7I33.js} +12 -2
  8. package/dist/{chunk-2YOXRATG.js.map → chunk-2LRC7I33.js.map} +1 -1
  9. package/dist/{chunk-4EHI4SKM.js → chunk-2W4KGGPU.js} +11 -1
  10. package/dist/chunk-2W4KGGPU.js.map +1 -0
  11. package/dist/chunk-7HJXZ4NM.js +189 -0
  12. package/dist/chunk-7HJXZ4NM.js.map +1 -0
  13. package/dist/{chunk-DG2K6CPY.js → chunk-E6LH2FJ6.js} +110 -68
  14. package/dist/chunk-E6LH2FJ6.js.map +1 -0
  15. package/dist/{chunk-MT3VS3GZ.js → chunk-FWC57PT7.js} +2 -2
  16. package/dist/{chunk-YRCJ3NUX.js → chunk-IS2CLLUT.js} +4 -4
  17. package/dist/{chunk-YYB4TF6D.js → chunk-KJ35EF7N.js} +4 -4
  18. package/dist/{chunk-D2UYGUV2.js → chunk-OHQOTSFG.js} +39 -3
  19. package/dist/chunk-OHQOTSFG.js.map +1 -0
  20. package/dist/chunk-R2P4C2I7.js +560 -0
  21. package/dist/chunk-R2P4C2I7.js.map +1 -0
  22. package/dist/{chunk-4QNPS5LI.js → chunk-R7X33D2S.js} +25 -6
  23. package/dist/{chunk-4QNPS5LI.js.map → chunk-R7X33D2S.js.map} +1 -1
  24. package/dist/{chunk-XZHIZVJ3.js → chunk-WMZEOR5W.js} +6 -127
  25. package/dist/chunk-WMZEOR5W.js.map +1 -0
  26. package/dist/{chunk-M2NJPATE.js → chunk-XDBRKW7I.js} +3 -3
  27. package/dist/chunk-YTYXBX7B.js +495 -0
  28. package/dist/chunk-YTYXBX7B.js.map +1 -0
  29. package/dist/{client-VSBQM2LU.js → client-UHHCB6EZ.js} +4 -4
  30. package/dist/{config-YRERDZSL.js → config-REASKDIK.js} +6 -2
  31. package/dist/{eas-QNZKCEP7.js → eas-PEEWGSAO.js} +16 -6
  32. package/dist/{governor-RE6IJMUJ.js → governor-MG5VVSU4.js} +6 -6
  33. package/dist/index.js +1137 -272
  34. package/dist/index.js.map +1 -1
  35. package/dist/{network-R5NFPIAF.js → network-3ZU7UBDU.js} +7 -5
  36. package/dist/{price-GY3JQ2OK.js → price-WCCXWPDM.js} +5 -5
  37. package/dist/{research-SVWNPHIN.js → research-QBKISW2M.js} +9 -9
  38. package/dist/research-YJWSQS2A.js +14 -0
  39. package/dist/serve-6MYBOAX6.js +222 -0
  40. package/dist/serve-6MYBOAX6.js.map +1 -0
  41. package/dist/{session-3Z6I2IM3.js → session-FPCPCVQX.js} +10 -10
  42. package/dist/summary-formatter-JIGWN4QS.js +180 -0
  43. package/dist/summary-formatter-JIGWN4QS.js.map +1 -0
  44. package/dist/{trade-E5HSZE44.js → trade-NO6CNWCA.js} +19 -17
  45. package/dist/{trade-E5HSZE44.js.map → trade-NO6CNWCA.js.map} +1 -1
  46. package/dist/{xmtp-VPGQE4IP.js → xmtp-ACB2W573.js} +6 -6
  47. package/package.json +5 -2
  48. package/dist/agent-SXEV65ME.js.map +0 -1
  49. package/dist/chunk-4EHI4SKM.js.map +0 -1
  50. package/dist/chunk-D2UYGUV2.js.map +0 -1
  51. package/dist/chunk-DG2K6CPY.js.map +0 -1
  52. package/dist/chunk-XZHIZVJ3.js.map +0 -1
  53. package/dist/research-PB22GPXC.js +0 -14
  54. /package/dist/{addresses-RQ7DTIBS.js.map → addresses-DOWLGXGE.js.map} +0 -0
  55. /package/dist/{chat-D5OF4OTL.js.map → chat-MRTPUZ3X.js.map} +0 -0
  56. /package/dist/{chunk-MT3VS3GZ.js.map → chunk-FWC57PT7.js.map} +0 -0
  57. /package/dist/{chunk-YRCJ3NUX.js.map → chunk-IS2CLLUT.js.map} +0 -0
  58. /package/dist/{chunk-YYB4TF6D.js.map → chunk-KJ35EF7N.js.map} +0 -0
  59. /package/dist/{chunk-M2NJPATE.js.map → chunk-XDBRKW7I.js.map} +0 -0
  60. /package/dist/{client-VSBQM2LU.js.map → client-UHHCB6EZ.js.map} +0 -0
  61. /package/dist/{config-YRERDZSL.js.map → config-REASKDIK.js.map} +0 -0
  62. /package/dist/{eas-QNZKCEP7.js.map → eas-PEEWGSAO.js.map} +0 -0
  63. /package/dist/{governor-RE6IJMUJ.js.map → governor-MG5VVSU4.js.map} +0 -0
  64. /package/dist/{network-R5NFPIAF.js.map → network-3ZU7UBDU.js.map} +0 -0
  65. /package/dist/{price-GY3JQ2OK.js.map → price-WCCXWPDM.js.map} +0 -0
  66. /package/dist/{research-SVWNPHIN.js.map → research-QBKISW2M.js.map} +0 -0
  67. /package/dist/{research-PB22GPXC.js.map → research-YJWSQS2A.js.map} +0 -0
  68. /package/dist/{session-3Z6I2IM3.js.map → session-FPCPCVQX.js.map} +0 -0
  69. /package/dist/{xmtp-VPGQE4IP.js.map → xmtp-ACB2W573.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,32 +1,39 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ HyperliquidProvider,
4
+ getLatestSignals
5
+ } from "./chunk-R2P4C2I7.js";
2
6
  import {
3
7
  UniswapProvider,
4
8
  applySlippage,
5
- chatCompletion,
6
- checkApiKeyValid,
7
9
  encodeSwapPath,
8
10
  getMultiHopQuote,
9
- getQuote,
11
+ getQuote
12
+ } from "./chunk-WMZEOR5W.js";
13
+ import {
14
+ chatCompletion,
15
+ checkApiKeyValid,
10
16
  listModels,
11
17
  provisionApiKey
12
- } from "./chunk-XZHIZVJ3.js";
18
+ } from "./chunk-7HJXZ4NM.js";
13
19
  import {
14
20
  createApproval,
15
21
  createJoinRequest,
22
+ getEasExplorerUrl,
16
23
  getEasScanUrl,
17
24
  parseReferrer,
18
25
  queryApprovals,
19
26
  queryJoinRequests,
20
- revokeAttestation,
27
+ revokeJoinRequest,
21
28
  stripReferrerPrefix
22
- } from "./chunk-DG2K6CPY.js";
29
+ } from "./chunk-E6LH2FJ6.js";
23
30
  import {
24
31
  createSyndicate,
25
32
  getActiveSyndicates,
26
33
  getSyndicate,
27
34
  subdomainExists,
28
35
  updateMetadata
29
- } from "./chunk-YRCJ3NUX.js";
36
+ } from "./chunk-IS2CLLUT.js";
30
37
  import {
31
38
  approveDepositor,
32
39
  deposit,
@@ -47,9 +54,10 @@ import {
47
54
  removeDepositor,
48
55
  resolveSyndicate,
49
56
  resolveVaultSyndicate,
57
+ searchSyndicateAcrossChains,
50
58
  setTextRecord,
51
59
  setVaultAddress
52
- } from "./chunk-4QNPS5LI.js";
60
+ } from "./chunk-R7X33D2S.js";
53
61
  import {
54
62
  PROPOSAL_STATE,
55
63
  PROPOSAL_STATES,
@@ -81,12 +89,11 @@ import {
81
89
  setVotingPeriod,
82
90
  settleProposal,
83
91
  vote
84
- } from "./chunk-YYB4TF6D.js";
92
+ } from "./chunk-KJ35EF7N.js";
85
93
  import {
86
94
  AERODROME,
87
95
  AGENT_REGISTRY,
88
96
  CHAINLINK,
89
- EAS_SCHEMAS,
90
97
  MOONWELL,
91
98
  SHERWOOD,
92
99
  STRATEGY_TEMPLATES,
@@ -94,7 +101,7 @@ import {
94
101
  TOKENS,
95
102
  UNISWAP,
96
103
  VENICE
97
- } from "./chunk-MT3VS3GZ.js";
104
+ } from "./chunk-FWC57PT7.js";
98
105
  import {
99
106
  formatContractError,
100
107
  getAccount,
@@ -103,7 +110,7 @@ import {
103
110
  sendTxWithRetry,
104
111
  waitForReceipt,
105
112
  writeContractWithRetry
106
- } from "./chunk-M2NJPATE.js";
113
+ } from "./chunk-XDBRKW7I.js";
107
114
  import {
108
115
  CHAIN_REGISTRY,
109
116
  VALID_NETWORKS,
@@ -112,8 +119,9 @@ import {
112
119
  getNetwork,
113
120
  getRpcUrl,
114
121
  isTestnet,
115
- setNetwork
116
- } from "./chunk-2YOXRATG.js";
122
+ setNetwork,
123
+ withNetwork
124
+ } from "./chunk-2LRC7I33.js";
117
125
  import {
118
126
  addSyndicate,
119
127
  cacheGroupId,
@@ -129,6 +137,7 @@ import {
129
137
  getVeniceApiKey,
130
138
  loadConfig,
131
139
  setAgentId,
140
+ setAnthropicApiKey,
132
141
  setChainContract,
133
142
  setConfigRpcUrl,
134
143
  setNotifyTo,
@@ -136,7 +145,7 @@ import {
136
145
  setPrivateKey,
137
146
  setUniswapApiKey,
138
147
  setVeniceApiKey
139
- } from "./chunk-4EHI4SKM.js";
148
+ } from "./chunk-2W4KGGPU.js";
140
149
  import {
141
150
  BASE_STRATEGY_ABI,
142
151
  ERC20_ABI,
@@ -156,7 +165,7 @@ import { config as loadDotenv } from "dotenv";
156
165
  import { createRequire } from "module";
157
166
  import { Command, Option } from "commander";
158
167
  import { parseUnits as parseUnits6, formatUnits as formatUnits5, isAddress as isAddress6 } from "viem";
159
- import chalk9 from "chalk";
168
+ import chalk13 from "chalk";
160
169
  import ora8 from "ora";
161
170
  import { input, confirm, select } from "@inquirer/prompts";
162
171
 
@@ -1445,9 +1454,9 @@ function registerStrategyTemplateCommands(strategy2) {
1445
1454
  console.error(chalk.red("Missing --name, --performance-fee, or --duration. Use --write-calls to skip proposal submission."));
1446
1455
  process.exit(1);
1447
1456
  }
1448
- const { propose: propose2 } = await import("./governor-RE6IJMUJ.js");
1457
+ const { propose: propose2 } = await import("./governor-MG5VVSU4.js");
1449
1458
  const { pinJSON } = await import("./ipfs-IGXLLJCF.js");
1450
- const { parseDuration: parseDuration2 } = await import("./governor-RE6IJMUJ.js");
1459
+ const { parseDuration: parseDuration2 } = await import("./governor-MG5VVSU4.js");
1451
1460
  const performanceFeeBps = BigInt(opts.performanceFee);
1452
1461
  if (performanceFeeBps < 0n || performanceFeeBps > 10000n) {
1453
1462
  console.error(chalk.red("--performance-fee must be 0-10000 (basis points)"));
@@ -1554,7 +1563,7 @@ function registerStrategyTemplateCommands(strategy2) {
1554
1563
  let prices = [];
1555
1564
  let priceSource = null;
1556
1565
  try {
1557
- const { getTokenPricesInAsset } = await import("./price-GY3JQ2OK.js");
1566
+ const { getTokenPricesInAsset } = await import("./price-WCCXWPDM.js");
1558
1567
  prices = await getTokenPricesInAsset({
1559
1568
  tokens: allocations.map((a, i) => ({
1560
1569
  token: a.token,
@@ -1984,7 +1993,7 @@ function registerVeniceCommands(program2) {
1984
1993
  console.log(chalk2.dim(` Key: ${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`));
1985
1994
  console.log(chalk2.dim(" Saved to ~/.sherwood/config.json"));
1986
1995
  try {
1987
- const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-QNZKCEP7.js");
1996
+ const { createVeniceProvisionAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
1988
1997
  const { uid } = await createVeniceProvisionAttestation(account.address);
1989
1998
  if (uid !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
1990
1999
  console.log(chalk2.dim(` Attested: ${getEasScanUrl2(uid)}`));
@@ -2340,10 +2349,10 @@ ${opts.prompt}`;
2340
2349
  console.log(chalk2.dim(`Model: ${result.model} | Tokens: ${result.usage.promptTokens} in, ${result.usage.completionTokens} out, ${result.usage.totalTokens} total`));
2341
2350
  }
2342
2351
  try {
2343
- const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-QNZKCEP7.js");
2352
+ const { createVeniceInferenceAttestation, getEasScanUrl: getEasScanUrl2 } = await import("./eas-PEEWGSAO.js");
2344
2353
  const { keccak256, toHex, isAddress: isAddr } = await import("viem");
2345
- const { getChainContracts: getChainContracts2 } = await import("./config-YRERDZSL.js");
2346
- const { getChain: getActiveChain } = await import("./network-R5NFPIAF.js");
2354
+ const { getChainContracts: getChainContracts2 } = await import("./config-REASKDIK.js");
2355
+ const { getChain: getActiveChain } = await import("./network-3ZU7UBDU.js");
2347
2356
  const vaultRecipient = opts.vault && isAddr(opts.vault) ? opts.vault : getChainContracts2(getActiveChain().id).vault;
2348
2357
  const promptHash = keccak256(toHex(userContent)).slice(0, 18);
2349
2358
  const { uid } = await createVeniceInferenceAttestation(
@@ -2738,6 +2747,50 @@ function registerIdentityCommands(program2) {
2738
2747
  process.exit(1);
2739
2748
  }
2740
2749
  });
2750
+ identity.command("find").description("Search ERC-8004 agents by wallet address or name (recover a lost agent ID)").option("--wallet <address>", "Filter by owner / wallet address").option("--name <text>", "Filter by agent name (exact or substring match, depending on registry)").option("--all-chains", "Search across all chains, not just the active one").action(async (opts) => {
2751
+ if (!opts.wallet && !opts.name) {
2752
+ console.error(chalk4.red("Pass --wallet <address> or --name <text> (or both)."));
2753
+ process.exit(1);
2754
+ }
2755
+ const spinner = ora4("Searching ERC-8004 registry...").start();
2756
+ try {
2757
+ const sdk = getAgent0SDK();
2758
+ const filters = {};
2759
+ if (opts.wallet) filters.walletAddress = opts.wallet;
2760
+ if (opts.name) filters.name = opts.name;
2761
+ filters.chains = opts.allChains ? "all" : [getChain().id];
2762
+ const results = await sdk.searchAgents(filters);
2763
+ spinner.stop();
2764
+ if (results.length === 0) {
2765
+ console.log(chalk4.dim("No agents found matching those filters."));
2766
+ console.log(chalk4.dim("If you minted on a different chain, retry with --all-chains."));
2767
+ return;
2768
+ }
2769
+ console.log();
2770
+ console.log(chalk4.bold(`Found ${results.length} agent${results.length === 1 ? "" : "s"}`));
2771
+ console.log(chalk4.dim("\u2500".repeat(50)));
2772
+ for (const a of results) {
2773
+ const idStr = String(a.agentId);
2774
+ const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
2775
+ console.log(` ${chalk4.bold(`#${tokenId}`)} ${a.name || chalk4.dim("(no name)")}`);
2776
+ console.log(chalk4.dim(` Chain: ${a.chainId}`));
2777
+ console.log(chalk4.dim(` Full: ${idStr}`));
2778
+ if (a.owners?.length) {
2779
+ console.log(chalk4.dim(` Owner: ${a.owners[0]}`));
2780
+ }
2781
+ if (a.walletAddress) {
2782
+ console.log(chalk4.dim(` Wallet: ${a.walletAddress}`));
2783
+ }
2784
+ }
2785
+ console.log();
2786
+ console.log(chalk4.green("To bind one of these to this machine:"));
2787
+ console.log(chalk4.dim(` sherwood identity load --id <tokenId>`));
2788
+ } catch (err) {
2789
+ spinner.fail("Search failed");
2790
+ console.error(chalk4.red(formatContractError(err)));
2791
+ process.exit(1);
2792
+ }
2793
+ });
2741
2794
  identity.command("status").description("Show your agent identity status").action(async () => {
2742
2795
  const account = getAccount();
2743
2796
  const registry2 = AGENT_REGISTRY().IDENTITY_REGISTRY;
@@ -3472,8 +3525,8 @@ function printSimulationResult(result, callType) {
3472
3525
  }
3473
3526
  async function sendSimulationAlert(subdomain, proposalId, vault, execResult, settleResult) {
3474
3527
  try {
3475
- const xmtp = await import("./xmtp-VPGQE4IP.js");
3476
- const { getAccount: getAccount2 } = await import("./client-VSBQM2LU.js");
3528
+ const xmtp = await import("./xmtp-ACB2W573.js");
3529
+ const { getAccount: getAccount2 } = await import("./client-UHHCB6EZ.js");
3477
3530
  const group = await xmtp.getGroup("", subdomain);
3478
3531
  const allRisks = [...execResult.risks, ...settleResult?.risks ?? []];
3479
3532
  const criticals = allRisks.filter((r) => r.level === "critical");
@@ -4564,6 +4617,740 @@ function registerGuardianCommands(program2) {
4564
4617
  });
4565
4618
  }
4566
4619
 
4620
+ // src/commands/grid.ts
4621
+ import chalk12 from "chalk";
4622
+
4623
+ // src/grid/loop.ts
4624
+ import chalk11 from "chalk";
4625
+ import { appendFile, mkdir as mkdir3 } from "fs/promises";
4626
+ import { join as join3 } from "path";
4627
+ import { homedir as homedir3 } from "os";
4628
+
4629
+ // src/grid/manager.ts
4630
+ import chalk9 from "chalk";
4631
+
4632
+ // src/grid/portfolio.ts
4633
+ import { readFile, writeFile, rename, mkdir } from "fs/promises";
4634
+ import { join, dirname } from "path";
4635
+ import { homedir } from "os";
4636
+ var GRID_STATE_PATH = join(homedir(), ".sherwood", "grid", "portfolio.json");
4637
+ function emptyStats() {
4638
+ return {
4639
+ totalRoundTrips: 0,
4640
+ totalPnlUsd: 0,
4641
+ todayPnlUsd: 0,
4642
+ totalFills: 0,
4643
+ todayFills: 0,
4644
+ lastDailyReset: 0,
4645
+ lastRebalanceAt: 0
4646
+ };
4647
+ }
4648
+ var GridPortfolio = class {
4649
+ state = null;
4650
+ /** Load grid state from disk. Returns null if no grid initialized yet. */
4651
+ async load() {
4652
+ try {
4653
+ const raw = await readFile(GRID_STATE_PATH, "utf-8");
4654
+ this.state = JSON.parse(raw);
4655
+ return this.state;
4656
+ } catch {
4657
+ return null;
4658
+ }
4659
+ }
4660
+ /** Save grid state to disk (atomic write). */
4661
+ async save(state) {
4662
+ this.state = state;
4663
+ await mkdir(dirname(GRID_STATE_PATH), { recursive: true });
4664
+ const tmp = `${GRID_STATE_PATH}.tmp.${process.pid}`;
4665
+ await writeFile(tmp, JSON.stringify(state, null, 2), "utf-8");
4666
+ await rename(tmp, GRID_STATE_PATH);
4667
+ }
4668
+ /**
4669
+ * Initialize the grid by carving capital from the directional portfolio.
4670
+ * Called once on first startup when grid-portfolio.json doesn't exist.
4671
+ *
4672
+ * Returns the USD amount carved out (caller must deduct from directional portfolio).
4673
+ */
4674
+ async initialize(capital, config) {
4675
+ const allocation = capital;
4676
+ const grids = config.tokens.map((token) => ({
4677
+ token,
4678
+ levels: [],
4679
+ openFills: [],
4680
+ allocation: allocation * (config.tokenSplit[token] ?? 1 / config.tokens.length),
4681
+ stats: emptyStats(),
4682
+ centerPrice: 0,
4683
+ atr: 0
4684
+ }));
4685
+ const state = {
4686
+ totalAllocation: allocation,
4687
+ grids,
4688
+ paused: false,
4689
+ pauseReason: "",
4690
+ initializedAt: Date.now()
4691
+ };
4692
+ await this.save(state);
4693
+ this.state = state;
4694
+ }
4695
+ /** Get current state (must load() first). */
4696
+ getState() {
4697
+ return this.state;
4698
+ }
4699
+ /** Reset daily counters if UTC day boundary crossed. */
4700
+ resetDailyStats(state) {
4701
+ const now = Date.now();
4702
+ const todayMidnight = /* @__PURE__ */ new Date();
4703
+ todayMidnight.setUTCHours(0, 0, 0, 0);
4704
+ const todayMs = todayMidnight.getTime();
4705
+ let changed = false;
4706
+ for (const grid of state.grids) {
4707
+ if (!grid.stats.lastDailyReset || grid.stats.lastDailyReset < todayMs) {
4708
+ grid.stats.todayPnlUsd = 0;
4709
+ grid.stats.todayFills = 0;
4710
+ grid.stats.lastDailyReset = now;
4711
+ changed = true;
4712
+ }
4713
+ }
4714
+ return changed;
4715
+ }
4716
+ /** Check if grid should be paused (pool dropped below threshold). */
4717
+ checkPauseThreshold(state, config) {
4718
+ const currentValue = state.grids.reduce((sum, g) => {
4719
+ const openFillValue = g.openFills.filter((f) => !f.closed).reduce((s, f) => s + f.quantity * f.buyPrice, 0);
4720
+ return sum + g.allocation + openFillValue;
4721
+ }, 0);
4722
+ const dropPct = 1 - currentValue / state.totalAllocation;
4723
+ if (dropPct >= config.pauseThresholdPct) {
4724
+ state.paused = true;
4725
+ state.pauseReason = `Grid pool dropped ${(dropPct * 100).toFixed(1)}% (threshold: ${(config.pauseThresholdPct * 100).toFixed(0)}%)`;
4726
+ return true;
4727
+ }
4728
+ return false;
4729
+ }
4730
+ /** Aggregate stats across all token grids. */
4731
+ aggregateStats(state) {
4732
+ return {
4733
+ totalPnlUsd: state.grids.reduce((s, g) => s + g.stats.totalPnlUsd, 0),
4734
+ todayPnlUsd: state.grids.reduce((s, g) => s + g.stats.todayPnlUsd, 0),
4735
+ todayFills: state.grids.reduce((s, g) => s + g.stats.todayFills, 0),
4736
+ totalRoundTrips: state.grids.reduce((s, g) => s + g.stats.totalRoundTrips, 0)
4737
+ };
4738
+ }
4739
+ };
4740
+
4741
+ // src/grid/manager.ts
4742
+ var GridManager = class {
4743
+ config;
4744
+ portfolio;
4745
+ hl;
4746
+ constructor(config) {
4747
+ this.config = config;
4748
+ this.portfolio = new GridPortfolio();
4749
+ this.hl = new HyperliquidProvider();
4750
+ }
4751
+ /** Load or initialize grid state.
4752
+ * Also syncs new tokens added to config after initial grid setup. */
4753
+ async init(totalPortfolioValue) {
4754
+ const existing = await this.portfolio.load();
4755
+ if (!existing) {
4756
+ await this.portfolio.initialize(totalPortfolioValue, this.config);
4757
+ return;
4758
+ }
4759
+ let addedAllocation = 0;
4760
+ for (const token of this.config.tokens) {
4761
+ if (!existing.grids.find((g) => g.token === token)) {
4762
+ const split = this.config.tokenSplit[token] ?? 1 / this.config.tokens.length;
4763
+ const alloc = existing.totalAllocation * split;
4764
+ existing.grids.push({
4765
+ token,
4766
+ levels: [],
4767
+ openFills: [],
4768
+ allocation: alloc,
4769
+ stats: { totalRoundTrips: 0, totalPnlUsd: 0, todayPnlUsd: 0, totalFills: 0, todayFills: 0, lastDailyReset: 0, lastRebalanceAt: 0 },
4770
+ centerPrice: 0,
4771
+ atr: 0
4772
+ });
4773
+ addedAllocation += alloc;
4774
+ console.error(` [grid] Added new token grid: ${token} ($${alloc.toFixed(0)} allocation)`);
4775
+ }
4776
+ }
4777
+ if (addedAllocation > 0) {
4778
+ await this.portfolio.save(existing);
4779
+ }
4780
+ }
4781
+ /**
4782
+ * Run one grid tick — called every cycle from the agent loop.
4783
+ *
4784
+ * 1. Load state + reset daily counters
4785
+ * 2. For each token grid: fetch price, simulate fills, check rebalance
4786
+ * 3. Persist state
4787
+ *
4788
+ * Returns a summary for the cycle log.
4789
+ */
4790
+ async tick(prices, regime) {
4791
+ const state = await this.portfolio.load();
4792
+ if (!state || state.paused || !this.config.enabled) {
4793
+ return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: state?.paused ?? false };
4794
+ }
4795
+ if (regime === "high-volatility") {
4796
+ console.error(chalk9.dim(` [grid] Paused: high-volatility regime \u2014 too risky for grid`));
4797
+ return { fills: 0, roundTrips: 0, pnlUsd: 0, paused: false };
4798
+ }
4799
+ this.portfolio.resetDailyStats(state);
4800
+ let totalFills = 0;
4801
+ let totalRoundTrips = 0;
4802
+ let totalPnl = 0;
4803
+ for (const grid of state.grids) {
4804
+ const price = prices[grid.token];
4805
+ if (!price || price <= 0) continue;
4806
+ if (grid.levels.length === 0 || this.needsFullRebuild(grid)) {
4807
+ await this.buildGrid(grid, price);
4808
+ }
4809
+ const { fills, roundTrips, pnlUsd } = this.simulateFills(grid, price);
4810
+ totalFills += fills;
4811
+ totalRoundTrips += roundTrips;
4812
+ totalPnl += pnlUsd;
4813
+ if (this.needsShift(grid, price)) {
4814
+ await this.shiftGrid(grid, price);
4815
+ }
4816
+ }
4817
+ this.portfolio.checkPauseThreshold(state, this.config);
4818
+ await this.portfolio.save(state);
4819
+ return { fills: totalFills, roundTrips: totalRoundTrips, pnlUsd: totalPnl, paused: false };
4820
+ }
4821
+ /** Build a fresh grid centered on the current price using ATR. */
4822
+ async buildGrid(grid, currentPrice) {
4823
+ const candles = await this.hl.getCandles(grid.token, "4h", 14 * 24 * 60 * 60 * 1e3);
4824
+ if (!candles || candles.length < this.config.atrPeriod) {
4825
+ console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: insufficient candle data`));
4826
+ return;
4827
+ }
4828
+ const signals = getLatestSignals(candles);
4829
+ const atr = signals.atr;
4830
+ if (!Number.isFinite(atr) || atr <= 0) {
4831
+ console.error(chalk9.dim(` [grid] Cannot build grid for ${grid.token}: invalid ATR (${atr})`));
4832
+ return;
4833
+ }
4834
+ const range = atr * this.config.atrMultiplier;
4835
+ const spacing = range / this.config.levelsPerSide;
4836
+ const effectiveCapital = grid.allocation * this.config.leverage;
4837
+ const quantity = effectiveCapital / (this.config.levelsPerSide * currentPrice);
4838
+ const levels = [];
4839
+ for (let i = 1; i <= this.config.levelsPerSide; i++) {
4840
+ levels.push({
4841
+ price: currentPrice - spacing * i,
4842
+ side: "buy",
4843
+ quantity,
4844
+ filled: false,
4845
+ filledAt: 0
4846
+ });
4847
+ }
4848
+ for (let i = 1; i <= this.config.levelsPerSide; i++) {
4849
+ levels.push({
4850
+ price: currentPrice + spacing * i,
4851
+ side: "sell",
4852
+ quantity,
4853
+ filled: false,
4854
+ filledAt: 0
4855
+ });
4856
+ }
4857
+ grid.levels = levels;
4858
+ grid.centerPrice = currentPrice;
4859
+ grid.atr = atr;
4860
+ grid.stats.lastRebalanceAt = Date.now();
4861
+ console.error(chalk9.dim(
4862
+ ` [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)}`
4863
+ ));
4864
+ }
4865
+ /** Simulate order fills against the current price. */
4866
+ simulateFills(grid, currentPrice) {
4867
+ let fills = 0;
4868
+ let roundTrips = 0;
4869
+ let pnlUsd = 0;
4870
+ const now = Date.now();
4871
+ const spacing = grid.atr > 0 ? grid.atr * this.config.atrMultiplier / this.config.levelsPerSide : 0;
4872
+ if (spacing <= 0) return { fills: 0, roundTrips: 0, pnlUsd: 0 };
4873
+ for (const level of grid.levels) {
4874
+ if (level.filled) continue;
4875
+ if (level.side === "buy" && currentPrice <= level.price) {
4876
+ level.filled = true;
4877
+ level.filledAt = now;
4878
+ fills++;
4879
+ grid.stats.totalFills++;
4880
+ grid.stats.todayFills++;
4881
+ grid.openFills.push({
4882
+ token: grid.token,
4883
+ buyPrice: level.price,
4884
+ targetSellPrice: level.price + spacing,
4885
+ quantity: level.quantity,
4886
+ filledAt: now,
4887
+ closed: false,
4888
+ pnlUsd: 0,
4889
+ closedAt: 0
4890
+ });
4891
+ console.error(chalk9.dim(
4892
+ ` [grid] BUY fill ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
4893
+ ));
4894
+ }
4895
+ if (level.side === "sell" && currentPrice >= level.price) {
4896
+ level.filled = true;
4897
+ level.filledAt = now;
4898
+ fills++;
4899
+ grid.stats.totalFills++;
4900
+ grid.stats.todayFills++;
4901
+ console.error(chalk9.dim(
4902
+ ` [grid] SELL level ${grid.token} @ $${level.price.toFixed(2)} qty=${level.quantity.toFixed(6)}`
4903
+ ));
4904
+ }
4905
+ }
4906
+ for (const openFill of grid.openFills) {
4907
+ if (openFill.closed) continue;
4908
+ if (currentPrice >= openFill.targetSellPrice) {
4909
+ const profit = (currentPrice - openFill.buyPrice) * openFill.quantity * this.config.leverage;
4910
+ if (profit >= this.config.minProfitPerFillUsd) {
4911
+ openFill.closed = true;
4912
+ openFill.pnlUsd = profit;
4913
+ openFill.closedAt = now;
4914
+ pnlUsd += profit;
4915
+ roundTrips++;
4916
+ grid.stats.totalRoundTrips++;
4917
+ grid.stats.totalPnlUsd += profit;
4918
+ grid.stats.todayPnlUsd += profit;
4919
+ grid.allocation += profit;
4920
+ console.error(chalk9.green(
4921
+ ` [grid] ROUND-TRIP ${grid.token}: buy $${openFill.buyPrice.toFixed(2)} \u2192 sell $${currentPrice.toFixed(2)} = +$${profit.toFixed(2)}`
4922
+ ));
4923
+ }
4924
+ }
4925
+ }
4926
+ const pruneThreshold = now - 24 * 60 * 60 * 1e3;
4927
+ grid.openFills = grid.openFills.filter((f) => !f.closed || f.closedAt > pruneThreshold);
4928
+ return { fills, roundTrips, pnlUsd };
4929
+ }
4930
+ /** Check if the grid needs a full rebuild. */
4931
+ needsFullRebuild(grid) {
4932
+ if (grid.levels.length === 0) return true;
4933
+ const elapsed = Date.now() - grid.stats.lastRebalanceAt;
4934
+ return elapsed >= this.config.fullRebuildIntervalMs;
4935
+ }
4936
+ /** Check if price has drifted enough to warrant shifting the grid. */
4937
+ needsShift(grid, currentPrice) {
4938
+ if (grid.centerPrice <= 0 || grid.atr <= 0) return false;
4939
+ const range = grid.atr * this.config.atrMultiplier;
4940
+ const distFromCenter = Math.abs(currentPrice - grid.centerPrice);
4941
+ return distFromCenter / range >= this.config.rebalanceDriftPct;
4942
+ }
4943
+ /** Shift the grid to re-center on the current price. */
4944
+ async shiftGrid(grid, currentPrice) {
4945
+ console.error(chalk9.dim(
4946
+ ` [grid] Shifting ${grid.token} grid: center $${grid.centerPrice.toFixed(2)} \u2192 $${currentPrice.toFixed(2)}`
4947
+ ));
4948
+ await this.buildGrid(grid, currentPrice);
4949
+ }
4950
+ /** Get open fill exposure per token — used by the hedge manager. */
4951
+ getOpenFillExposure() {
4952
+ const state = this.portfolio.getState();
4953
+ if (!state) return [];
4954
+ return state.grids.map((g) => {
4955
+ const opens = g.openFills.filter((f) => !f.closed);
4956
+ const totalQty = opens.reduce((s, f) => s + f.quantity, 0);
4957
+ const totalNotional = opens.reduce((s, f) => s + f.quantity * f.buyPrice, 0);
4958
+ return {
4959
+ token: g.token,
4960
+ totalQuantity: totalQty,
4961
+ totalNotional,
4962
+ avgEntryPrice: totalQty > 0 ? totalNotional / totalQty : 0,
4963
+ fillCount: opens.length
4964
+ };
4965
+ }).filter((e) => e.fillCount > 0);
4966
+ }
4967
+ /** Get aggregate stats for display. */
4968
+ getStats() {
4969
+ const state = this.portfolio.getState();
4970
+ if (!state) return null;
4971
+ const agg = this.portfolio.aggregateStats(state);
4972
+ return {
4973
+ ...agg,
4974
+ allocation: state.totalAllocation,
4975
+ paused: state.paused
4976
+ };
4977
+ }
4978
+ };
4979
+
4980
+ // src/grid/config.ts
4981
+ var DEFAULT_GRID_CONFIG = {
4982
+ enabled: true,
4983
+ tokens: ["bitcoin", "ethereum", "solana"],
4984
+ allocationPct: 0.5,
4985
+ leverage: 5,
4986
+ // was 4 — +25% profit per RT
4987
+ levelsPerSide: 15,
4988
+ atrMultiplier: 2,
4989
+ atrPeriod: 14,
4990
+ rebalanceDriftPct: 0.4,
4991
+ // was 0.55 — rebalance faster, keep levels near price
4992
+ fullRebuildIntervalMs: 12 * 60 * 60 * 1e3,
4993
+ // 12h
4994
+ tokenSplit: { bitcoin: 0.45, ethereum: 0.3, solana: 0.25 },
4995
+ minProfitPerFillUsd: 0.5,
4996
+ pauseThresholdPct: 0.2
4997
+ };
4998
+
4999
+ // src/grid/hedge.ts
5000
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
5001
+ import { join as join2, dirname as dirname2 } from "path";
5002
+ import { homedir as homedir2 } from "os";
5003
+ import chalk10 from "chalk";
5004
+ var HEDGE_RATIO = 0.3;
5005
+ var MIN_FILLS_TO_HEDGE = 3;
5006
+ var ADJUSTMENT_THRESHOLD = 0.1;
5007
+ var HEDGE_STATE_PATH = join2(homedir2(), ".sherwood", "grid", "hedge.json");
5008
+ var GridHedgeManager = class {
5009
+ state = null;
5010
+ async load() {
5011
+ if (this.state) return this.state;
5012
+ try {
5013
+ const raw = await readFile2(HEDGE_STATE_PATH, "utf-8");
5014
+ this.state = JSON.parse(raw);
5015
+ } catch {
5016
+ this.state = {
5017
+ positions: [],
5018
+ totalRealizedPnl: 0,
5019
+ todayRealizedPnl: 0,
5020
+ lastDailyReset: Date.now()
5021
+ };
5022
+ }
5023
+ const todayMidnight = /* @__PURE__ */ new Date();
5024
+ todayMidnight.setUTCHours(0, 0, 0, 0);
5025
+ if (this.state.lastDailyReset < todayMidnight.getTime()) {
5026
+ this.state.todayRealizedPnl = 0;
5027
+ this.state.lastDailyReset = Date.now();
5028
+ }
5029
+ return this.state;
5030
+ }
5031
+ async save() {
5032
+ if (!this.state) return;
5033
+ await mkdir2(dirname2(HEDGE_STATE_PATH), { recursive: true });
5034
+ const tmp = `${HEDGE_STATE_PATH}.tmp.${process.pid}`;
5035
+ await writeFile2(tmp, JSON.stringify(this.state, null, 2), "utf-8");
5036
+ const { rename: rename2 } = await import("fs/promises");
5037
+ await rename2(tmp, HEDGE_STATE_PATH);
5038
+ }
5039
+ /**
5040
+ * Run one hedge tick. Call after grid tick with current prices and open fill data.
5041
+ *
5042
+ * @param openFills - Per-token open fill exposure from the grid portfolio
5043
+ * @param prices - Current mark prices per token
5044
+ */
5045
+ async tick(openFills, prices) {
5046
+ const state = await this.load();
5047
+ let adjustments = 0;
5048
+ let unrealizedPnl = 0;
5049
+ for (const fill of openFills) {
5050
+ const price = prices[fill.token];
5051
+ if (!price || price <= 0) continue;
5052
+ const desiredShortQty = fill.fillCount >= MIN_FILLS_TO_HEDGE ? fill.totalQuantity * HEDGE_RATIO : 0;
5053
+ const existing = state.positions.find((p) => p.token === fill.token);
5054
+ if (desiredShortQty <= 0) {
5055
+ if (existing && existing.quantity > 0) {
5056
+ const closePnl = (existing.entryPrice - price) * existing.quantity;
5057
+ state.totalRealizedPnl += closePnl;
5058
+ state.todayRealizedPnl += closePnl;
5059
+ existing.realizedPnl += closePnl;
5060
+ console.error(chalk10.yellow(
5061
+ ` [hedge] CLOSE ${fill.token} short ${existing.quantity.toFixed(6)} @ $${price.toFixed(2)} PnL: $${closePnl.toFixed(2)}`
5062
+ ));
5063
+ existing.quantity = 0;
5064
+ existing.entryPrice = 0;
5065
+ adjustments++;
5066
+ }
5067
+ continue;
5068
+ }
5069
+ if (!existing) {
5070
+ state.positions.push({
5071
+ token: fill.token,
5072
+ entryPrice: price,
5073
+ quantity: desiredShortQty,
5074
+ lastAdjustedAt: Date.now(),
5075
+ realizedPnl: 0
5076
+ });
5077
+ console.error(chalk10.magenta(
5078
+ ` [hedge] OPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)} (${(HEDGE_RATIO * 100).toFixed(0)}% of ${fill.fillCount} open fills)`
5079
+ ));
5080
+ adjustments++;
5081
+ } else if (existing.quantity > 0) {
5082
+ const diff = Math.abs(desiredShortQty - existing.quantity) / existing.quantity;
5083
+ if (diff > ADJUSTMENT_THRESHOLD) {
5084
+ if (desiredShortQty > existing.quantity) {
5085
+ const addQty = desiredShortQty - existing.quantity;
5086
+ const newAvgEntry = (existing.entryPrice * existing.quantity + price * addQty) / desiredShortQty;
5087
+ existing.entryPrice = newAvgEntry;
5088
+ existing.quantity = desiredShortQty;
5089
+ existing.lastAdjustedAt = Date.now();
5090
+ console.error(chalk10.magenta(
5091
+ ` [hedge] INCREASE ${fill.token} short to ${desiredShortQty.toFixed(6)} @ avg $${newAvgEntry.toFixed(2)}`
5092
+ ));
5093
+ adjustments++;
5094
+ } else {
5095
+ const closeQty = existing.quantity - desiredShortQty;
5096
+ const closePnl = (existing.entryPrice - price) * closeQty;
5097
+ state.totalRealizedPnl += closePnl;
5098
+ state.todayRealizedPnl += closePnl;
5099
+ existing.realizedPnl += closePnl;
5100
+ existing.quantity = desiredShortQty;
5101
+ existing.lastAdjustedAt = Date.now();
5102
+ console.error(chalk10.magenta(
5103
+ ` [hedge] DECREASE ${fill.token} short to ${desiredShortQty.toFixed(6)}, closed ${closeQty.toFixed(6)} PnL: $${closePnl.toFixed(2)}`
5104
+ ));
5105
+ adjustments++;
5106
+ }
5107
+ }
5108
+ const uPnl = (existing.entryPrice - price) * existing.quantity;
5109
+ unrealizedPnl += uPnl;
5110
+ } else {
5111
+ existing.entryPrice = price;
5112
+ existing.quantity = desiredShortQty;
5113
+ existing.lastAdjustedAt = Date.now();
5114
+ console.error(chalk10.magenta(
5115
+ ` [hedge] REOPEN ${fill.token} short ${desiredShortQty.toFixed(6)} @ $${price.toFixed(2)}`
5116
+ ));
5117
+ adjustments++;
5118
+ }
5119
+ }
5120
+ for (const pos of state.positions) {
5121
+ if (pos.quantity <= 0) continue;
5122
+ if (!openFills.find((f) => f.token === pos.token)) {
5123
+ const price = prices[pos.token];
5124
+ if (price) {
5125
+ const closePnl = (pos.entryPrice - price) * pos.quantity;
5126
+ state.totalRealizedPnl += closePnl;
5127
+ state.todayRealizedPnl += closePnl;
5128
+ pos.realizedPnl += closePnl;
5129
+ console.error(chalk10.yellow(
5130
+ ` [hedge] CLOSE orphaned ${pos.token} short, PnL: $${closePnl.toFixed(2)}`
5131
+ ));
5132
+ pos.quantity = 0;
5133
+ adjustments++;
5134
+ }
5135
+ }
5136
+ }
5137
+ await this.save();
5138
+ return {
5139
+ adjustments,
5140
+ unrealizedPnl,
5141
+ totalRealizedPnl: state.totalRealizedPnl
5142
+ };
5143
+ }
5144
+ /** Get current hedge status for display. */
5145
+ getStatus() {
5146
+ if (!this.state) return null;
5147
+ return {
5148
+ positions: this.state.positions.filter((p) => p.quantity > 0),
5149
+ totalRealizedPnl: this.state.totalRealizedPnl,
5150
+ todayRealizedPnl: this.state.todayRealizedPnl
5151
+ };
5152
+ }
5153
+ };
5154
+
5155
+ // src/grid/loop.ts
5156
+ var GRID_CYCLES_PATH = join3(homedir3(), ".sherwood", "grid", "cycles.jsonl");
5157
+ var GridLoop = class {
5158
+ cfg;
5159
+ gridConfig;
5160
+ manager;
5161
+ hedge;
5162
+ hl;
5163
+ running = false;
5164
+ cycleCount = 0;
5165
+ timer = null;
5166
+ constructor(cfg) {
5167
+ this.cfg = cfg;
5168
+ this.gridConfig = { ...DEFAULT_GRID_CONFIG, ...cfg.config };
5169
+ this.manager = new GridManager(this.gridConfig);
5170
+ this.hedge = new GridHedgeManager();
5171
+ this.hl = new HyperliquidProvider();
5172
+ }
5173
+ /** Start the grid loop. Resolves when shut down via SIGINT/SIGTERM. */
5174
+ async start() {
5175
+ this.running = true;
5176
+ const shutdown = () => {
5177
+ console.error(chalk11.yellow("\n [grid-loop] Shutting down\u2026"));
5178
+ this.running = false;
5179
+ if (this.timer) {
5180
+ clearTimeout(this.timer);
5181
+ this.timer = null;
5182
+ }
5183
+ };
5184
+ process.on("SIGINT", shutdown);
5185
+ process.on("SIGTERM", shutdown);
5186
+ await this.manager.init(this.cfg.capital);
5187
+ console.error(chalk11.cyan(
5188
+ `
5189
+ [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
5190
+ `
5191
+ ));
5192
+ while (this.running) {
5193
+ try {
5194
+ await this.tick();
5195
+ } catch (err) {
5196
+ console.error(chalk11.red(` [grid-loop] Tick error: ${err.message}`));
5197
+ }
5198
+ if (this.running) {
5199
+ await new Promise((resolve2) => {
5200
+ this.timer = setTimeout(resolve2, this.cfg.cycle);
5201
+ });
5202
+ }
5203
+ }
5204
+ process.off("SIGINT", shutdown);
5205
+ process.off("SIGTERM", shutdown);
5206
+ console.error(chalk11.yellow(" [grid-loop] Stopped."));
5207
+ }
5208
+ /** Execute one grid cycle. */
5209
+ async tick() {
5210
+ this.cycleCount++;
5211
+ const start = Date.now();
5212
+ const prices = {};
5213
+ for (const token of this.gridConfig.tokens) {
5214
+ const data = await this.hl.getHyperliquidData(token);
5215
+ if (data?.markPrice && data.markPrice > 0) {
5216
+ prices[token] = data.markPrice;
5217
+ }
5218
+ }
5219
+ if (Object.keys(prices).length === 0) {
5220
+ console.error(chalk11.dim(` [grid-loop] #${this.cycleCount} \u2014 no prices, skipping`));
5221
+ return;
5222
+ }
5223
+ const result = await this.manager.tick(prices);
5224
+ const elapsed = Date.now() - start;
5225
+ if (result.roundTrips > 0) {
5226
+ console.error(chalk11.green(
5227
+ ` [grid-loop] #${this.cycleCount} \u2014 ${result.roundTrips} RT(s), +$${result.pnlUsd.toFixed(2)} PnL, ${result.fills} fill(s) [${elapsed}ms]`
5228
+ ));
5229
+ }
5230
+ if (result.fills > 0 && result.roundTrips === 0) {
5231
+ console.error(chalk11.dim(
5232
+ ` [grid-loop] #${this.cycleCount} \u2014 ${result.fills} fill(s), 0 RTs [${elapsed}ms]`
5233
+ ));
5234
+ }
5235
+ const openExposure = this.manager.getOpenFillExposure();
5236
+ const hedgeResult = await this.hedge.tick(openExposure, prices);
5237
+ if (hedgeResult.adjustments > 0) {
5238
+ console.error(chalk11.magenta(
5239
+ ` [hedge] ${hedgeResult.adjustments} adjustment(s), unrealized: $${hedgeResult.unrealizedPnl.toFixed(2)}, total realized: $${hedgeResult.totalRealizedPnl.toFixed(2)}`
5240
+ ));
5241
+ }
5242
+ const stats = this.manager.getStats();
5243
+ const cycleEntry = {
5244
+ cycleNumber: this.cycleCount,
5245
+ timestamp: Date.now(),
5246
+ gridFills: result.fills,
5247
+ gridRoundTrips: result.roundTrips,
5248
+ gridPnlUsd: result.pnlUsd,
5249
+ totalPnlUsd: stats?.totalPnlUsd ?? 0,
5250
+ todayPnlUsd: stats?.todayPnlUsd ?? 0,
5251
+ totalRoundTrips: stats?.totalRoundTrips ?? 0,
5252
+ allocation: stats?.allocation ?? 0,
5253
+ paused: stats?.paused ?? false,
5254
+ hedgeUnrealizedPnl: hedgeResult.unrealizedPnl,
5255
+ hedgeTotalRealizedPnl: hedgeResult.totalRealizedPnl
5256
+ };
5257
+ try {
5258
+ await mkdir3(join3(homedir3(), ".sherwood", "grid"), { recursive: true });
5259
+ await appendFile(GRID_CYCLES_PATH, JSON.stringify(cycleEntry) + "\n");
5260
+ } catch {
5261
+ }
5262
+ if (this.cycleCount % 60 === 0) {
5263
+ const stats2 = this.manager.getStats();
5264
+ if (stats2) {
5265
+ const hedgeStatus = this.hedge.getStatus();
5266
+ const hedgeInfo = hedgeStatus && hedgeStatus.positions.length > 0 ? ` hedge=$${hedgeResult.unrealizedPnl.toFixed(2)}unr/$${hedgeStatus.totalRealizedPnl.toFixed(2)}real` : "";
5267
+ console.error(chalk11.cyan(
5268
+ ` [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)" : ""}`
5269
+ ));
5270
+ }
5271
+ }
5272
+ }
5273
+ };
5274
+
5275
+ // src/commands/grid.ts
5276
+ var DIM5 = chalk12.gray;
5277
+ var G5 = chalk12.green;
5278
+ var BOLD5 = chalk12.white.bold;
5279
+ var W5 = chalk12.white;
5280
+ var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
5281
+ function registerGridCommand(program2) {
5282
+ const grid = program2.command("grid").description("Grid trading strategy \u2014 ATR-based grid on BTC/ETH/SOL");
5283
+ 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").action(async (opts) => {
5284
+ const capital = parseFloat(opts.capital);
5285
+ const cycleMs = parseInt(opts.cycle, 10) * 1e3;
5286
+ const tokens = opts.tokens.split(",").map((t) => t.trim());
5287
+ const leverage = parseFloat(opts.leverage);
5288
+ const levels = parseInt(opts.levels, 10);
5289
+ const weight = 1 / tokens.length;
5290
+ const tokenSplit = {};
5291
+ for (const t of tokens) {
5292
+ tokenSplit[t] = weight;
5293
+ }
5294
+ console.log();
5295
+ console.log(G5.bold(" Grid Strategy"));
5296
+ SEP5();
5297
+ console.log(W5(` Capital: $${capital.toLocaleString()}`));
5298
+ console.log(W5(` Cycle: ${opts.cycle}s`));
5299
+ console.log(W5(` Tokens: ${tokens.join(", ")}`));
5300
+ console.log(W5(` Leverage: ${leverage}x`));
5301
+ console.log(W5(` Levels: ${levels}/side`));
5302
+ SEP5();
5303
+ const loop = new GridLoop({
5304
+ capital,
5305
+ cycle: cycleMs,
5306
+ config: {
5307
+ ...DEFAULT_GRID_CONFIG,
5308
+ tokens,
5309
+ leverage,
5310
+ levelsPerSide: levels,
5311
+ tokenSplit
5312
+ }
5313
+ });
5314
+ await loop.start();
5315
+ });
5316
+ grid.command("status").description("Show current grid portfolio status").action(async () => {
5317
+ const portfolio = new GridPortfolio();
5318
+ const state = await portfolio.load();
5319
+ if (!state) {
5320
+ console.log(DIM5("\n No grid portfolio found. Run `sherwood grid start` first.\n"));
5321
+ return;
5322
+ }
5323
+ portfolio.resetDailyStats(state);
5324
+ const agg = portfolio.aggregateStats(state);
5325
+ console.log();
5326
+ console.log(G5.bold(" Grid Portfolio"));
5327
+ SEP5();
5328
+ console.log(W5(` Allocation: $${state.totalAllocation.toLocaleString()}`));
5329
+ console.log(W5(` Status: ${state.paused ? chalk12.red("PAUSED \u2014 " + state.pauseReason) : G5("Active")}`));
5330
+ console.log(W5(` Initialized: ${new Date(state.initializedAt).toLocaleString()}`));
5331
+ SEP5();
5332
+ console.log();
5333
+ console.log(BOLD5(" Token Alloc RTs PnL Today PnL Fills"));
5334
+ console.log(DIM5(" " + "\u2500".repeat(72)));
5335
+ for (const g of state.grids) {
5336
+ const name = g.token.padEnd(13);
5337
+ const alloc = `$${g.allocation.toFixed(0)}`.padStart(8);
5338
+ const rts = String(g.stats.totalRoundTrips).padStart(6);
5339
+ const pnl = `$${g.stats.totalPnlUsd.toFixed(2)}`.padStart(10);
5340
+ const todayPnl = `$${g.stats.todayPnlUsd.toFixed(2)}`.padStart(10);
5341
+ const fills = String(g.stats.totalFills).padStart(6);
5342
+ const pnlColor = g.stats.totalPnlUsd >= 0 ? G5 : chalk12.red;
5343
+ const todayColor = g.stats.todayPnlUsd >= 0 ? G5 : chalk12.red;
5344
+ console.log(` ${W5(name)} ${alloc} ${rts} ${pnlColor(pnl)} ${todayColor(todayPnl)} ${fills}`);
5345
+ }
5346
+ console.log(DIM5(" " + "\u2500".repeat(72)));
5347
+ const totalPnlColor = agg.totalPnlUsd >= 0 ? G5 : chalk12.red;
5348
+ const todayTotalColor = agg.todayPnlUsd >= 0 ? G5 : chalk12.red;
5349
+ 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)}`);
5350
+ console.log();
5351
+ });
5352
+ }
5353
+
4567
5354
  // src/index.ts
4568
5355
  try {
4569
5356
  loadDotenv();
@@ -4572,20 +5359,20 @@ try {
4572
5359
  var require2 = createRequire(import.meta.url);
4573
5360
  var { version: CLI_VERSION } = require2("../package.json");
4574
5361
  async function loadXmtp() {
4575
- return import("./xmtp-VPGQE4IP.js");
5362
+ return import("./xmtp-ACB2W573.js");
4576
5363
  }
4577
5364
  async function loadCron() {
4578
5365
  return import("./cron-EOULYO3R.js");
4579
5366
  }
4580
- var G5 = chalk9.green;
4581
- var W5 = chalk9.white;
4582
- var DIM5 = chalk9.gray;
4583
- var BOLD5 = chalk9.white.bold;
4584
- var LABEL5 = chalk9.green.bold;
4585
- var SEP5 = () => console.log(DIM5("\u2500".repeat(60)));
5367
+ var G6 = chalk13.green;
5368
+ var W6 = chalk13.white;
5369
+ var DIM6 = chalk13.gray;
5370
+ var BOLD6 = chalk13.white.bold;
5371
+ var LABEL5 = chalk13.green.bold;
5372
+ var SEP6 = () => console.log(DIM6("\u2500".repeat(60)));
4586
5373
  function validateAddress(value, name) {
4587
5374
  if (!isAddress6(value)) {
4588
- console.error(chalk9.red(`Invalid ${name} address: ${value}`));
5375
+ console.error(chalk13.red(`Invalid ${name} address: ${value}`));
4589
5376
  process.exit(1);
4590
5377
  }
4591
5378
  return value;
@@ -4605,7 +5392,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
4605
5392
  process.env.ENABLE_TESTNET = "true";
4606
5393
  if (network !== "base") {
4607
5394
  console.warn(
4608
- chalk9.yellow("[warn] --testnet ignored, --chain takes precedence")
5395
+ chalk13.yellow("[warn] --testnet ignored, --chain takes precedence")
4609
5396
  );
4610
5397
  } else {
4611
5398
  network = "base-sepolia";
@@ -4613,7 +5400,7 @@ program.name("sherwood").description("CLI for agent-managed investment syndicate
4613
5400
  }
4614
5401
  setNetwork(network);
4615
5402
  if (getNetwork() !== "base") {
4616
- console.log(chalk9.yellow(`[${getNetwork()}]`));
5403
+ console.log(chalk13.yellow(`[${getNetwork()}]`));
4617
5404
  }
4618
5405
  });
4619
5406
  var syndicate = program.command("syndicate");
@@ -4621,31 +5408,31 @@ syndicate.command("create").description("Create a new syndicate via the factory
4621
5408
  try {
4622
5409
  console.log();
4623
5410
  console.log(LABEL5(" \u25C6 Create Syndicate"));
4624
- SEP5();
5411
+ SEP6();
4625
5412
  const wallet = getAccount();
4626
- console.log(DIM5(` Wallet: ${wallet.address}`));
4627
- console.log(DIM5(` Network: ${getChain().name}`));
4628
- SEP5();
5413
+ console.log(DIM6(` Wallet: ${wallet.address}`));
5414
+ console.log(DIM6(` Network: ${getChain().name}`));
5415
+ SEP6();
4629
5416
  const savedAgentId = getAgentId();
4630
5417
  const nonInteractive = opts.yes;
4631
5418
  const name = opts.name || (nonInteractive ? (() => {
4632
5419
  throw new Error("--name is required in non-interactive mode (-y)");
4633
5420
  })() : await input({
4634
- message: G5("Syndicate name"),
5421
+ message: G6("Syndicate name"),
4635
5422
  validate: (v) => v.length > 0 || "Name is required"
4636
5423
  }));
4637
5424
  const subdomain = opts.subdomain || (nonInteractive ? name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") : await input({
4638
- message: G5("ENS subdomain"),
5425
+ message: G6("ENS subdomain"),
4639
5426
  default: name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, ""),
4640
5427
  validate: (v) => v.length >= 3 || "Must be at least 3 characters"
4641
5428
  }));
4642
5429
  const description = opts.description || (nonInteractive ? `${name} \u2014 a Sherwood syndicate` : await input({
4643
- message: G5("Description"),
5430
+ message: G6("Description"),
4644
5431
  default: `${name} \u2014 a Sherwood syndicate`
4645
5432
  }));
4646
- 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" }));
5433
+ 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" }));
4647
5434
  const openDeposits = opts.openDeposits !== void 0 ? opts.openDeposits : nonInteractive ? true : await confirm({
4648
- message: G5("Open deposits? (anyone can deposit)"),
5435
+ message: G6("Open deposits? (anyone can deposit)"),
4649
5436
  default: true
4650
5437
  });
4651
5438
  const ASSET_SYMBOLS = {
@@ -4661,14 +5448,14 @@ syndicate.command("create").description("Create a new syndicate via the factory
4661
5448
  asset = opts.asset;
4662
5449
  } else {
4663
5450
  const supported = Object.keys(ASSET_SYMBOLS).join(", ");
4664
- console.error(chalk9.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
5451
+ console.error(chalk13.red(` Unknown asset "${opts.asset}". Use a symbol (${supported}) or a 0x address.`));
4665
5452
  process.exit(1);
4666
5453
  }
4667
5454
  } else if (nonInteractive) {
4668
5455
  asset = TOKENS().USDC !== "0x0000000000000000000000000000000000000000" ? ASSET_SYMBOLS.USDC : ASSET_SYMBOLS.WETH;
4669
5456
  } else {
4670
5457
  const assetChoice = await select({
4671
- message: G5("Vault asset (what token do depositors provide?)"),
5458
+ message: G6("Vault asset (what token do depositors provide?)"),
4672
5459
  choices: [
4673
5460
  { name: "USDC", value: "USDC", description: "USD Coin (6 decimals)" },
4674
5461
  { name: "WETH", value: "WETH", description: "Wrapped Ether (18 decimals)" }
@@ -4684,25 +5471,25 @@ syndicate.command("create").description("Create a new syndicate via the factory
4684
5471
  const symbol = `sw${assetSymbol}`;
4685
5472
  console.log();
4686
5473
  console.log(LABEL5(" \u25C6 Review"));
4687
- SEP5();
4688
- console.log(W5(` Name: ${BOLD5(name)}`));
4689
- console.log(W5(` ENS: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
4690
- console.log(W5(` Description: ${DIM5(description)}`));
4691
- console.log(W5(` Agent ID: #${agentIdStr}`));
4692
- console.log(W5(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
4693
- console.log(W5(` Share token: ${symbol}`));
4694
- console.log(W5(` Open deposits: ${openDeposits ? G5("yes") : chalk9.red("no (whitelist)")}`));
4695
- SEP5();
5474
+ SEP6();
5475
+ console.log(W6(` Name: ${BOLD6(name)}`));
5476
+ console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
5477
+ console.log(W6(` Description: ${DIM6(description)}`));
5478
+ console.log(W6(` Agent ID: #${agentIdStr}`));
5479
+ console.log(W6(` Asset: ${assetSymbol} (${asset.slice(0, 10)}...)`));
5480
+ console.log(W6(` Share token: ${symbol}`));
5481
+ console.log(W6(` Open deposits: ${openDeposits ? G6("yes") : chalk13.red("no (whitelist)")}`));
5482
+ SEP6();
4696
5483
  if (!nonInteractive) {
4697
- const go = await confirm({ message: G5("Deploy syndicate?"), default: true });
5484
+ const go = await confirm({ message: G6("Deploy syndicate?"), default: true });
4698
5485
  if (!go) {
4699
- console.log(DIM5(" Cancelled."));
5486
+ console.log(DIM6(" Cancelled."));
4700
5487
  return;
4701
5488
  }
4702
5489
  }
4703
5490
  let metadataURI = opts.metadataUri || "";
4704
5491
  if (!metadataURI) {
4705
- const spinner2 = ora8({ text: W5("Uploading metadata to IPFS..."), color: "green" }).start();
5492
+ const spinner2 = ora8({ text: W6("Uploading metadata to IPFS..."), color: "green" }).start();
4706
5493
  try {
4707
5494
  const metadata = {
4708
5495
  schema: "sherwood/syndicate/v1",
@@ -4716,9 +5503,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
4716
5503
  links: {}
4717
5504
  };
4718
5505
  metadataURI = await uploadMetadata(metadata);
4719
- spinner2.succeed(G5(`Metadata pinned: ${DIM5(metadataURI)}`));
5506
+ spinner2.succeed(G6(`Metadata pinned: ${DIM6(metadataURI)}`));
4720
5507
  } catch (err) {
4721
- spinner2.warn(chalk9.yellow(`IPFS upload failed \u2014 using inline metadata`));
5508
+ spinner2.warn(chalk13.yellow(`IPFS upload failed \u2014 using inline metadata`));
4722
5509
  const json = JSON.stringify({ name, description, subdomain, asset: assetSymbol, openDeposits, createdBy: "@sherwoodagent/cli" });
4723
5510
  metadataURI = `data:application/json;base64,${Buffer.from(json).toString("base64")}`;
4724
5511
  }
@@ -4728,43 +5515,43 @@ syndicate.command("create").description("Create a new syndicate via the factory
4728
5515
  const existingInfo = await getSyndicate(existingId);
4729
5516
  const callerAddress = getAccount().address.toLowerCase();
4730
5517
  if (existingInfo.creator.toLowerCase() !== callerAddress) {
4731
- console.error(chalk9.red(`
5518
+ console.error(chalk13.red(`
4732
5519
  \u2716 Subdomain '${subdomain}' is already taken by another syndicate.
4733
5520
  `));
4734
5521
  process.exit(1);
4735
5522
  }
4736
- console.log(chalk9.yellow(`
5523
+ console.log(chalk13.yellow(`
4737
5524
  \u26A0 Syndicate '${subdomain}' already exists \u2014 resuming setup...
4738
5525
  `));
4739
- const spinner2 = ora8({ text: W5("Resuming setup..."), color: "green" }).start();
5526
+ const spinner2 = ora8({ text: W6("Resuming setup..."), color: "green" }).start();
4740
5527
  const chainId2 = getChain().id;
4741
5528
  setChainContract(chainId2, "vault", existingInfo.vault);
4742
5529
  addSyndicate(chainId2, { subdomain, vault: existingInfo.vault, role: "creator" });
4743
5530
  setPrimarySyndicate(chainId2, subdomain);
4744
- spinner2.text = W5("Checking agent registration...");
5531
+ spinner2.text = W6("Checking agent registration...");
4745
5532
  try {
4746
5533
  setVaultAddress(existingInfo.vault);
4747
5534
  const isAlreadyAgent = await isAgent(getAccount().address);
4748
5535
  if (!isAlreadyAgent) {
4749
- spinner2.text = W5("Registering creator as agent...");
5536
+ spinner2.text = W6("Registering creator as agent...");
4750
5537
  await registerAgent(BigInt(agentIdStr), getAccount().address);
4751
5538
  }
4752
5539
  } catch {
4753
5540
  }
4754
- spinner2.text = W5("Checking chat group...");
5541
+ spinner2.text = W6("Checking chat group...");
4755
5542
  try {
4756
5543
  const xmtp = await loadXmtp();
4757
5544
  const xmtpClient = await xmtp.getXmtpClient();
4758
5545
  try {
4759
5546
  await xmtp.getGroup(xmtpClient, subdomain);
4760
5547
  } catch {
4761
- spinner2.text = W5("Creating chat group...");
5548
+ spinner2.text = W6("Creating chat group...");
4762
5549
  const groupId = await xmtp.createSyndicateGroup(xmtpClient, subdomain, opts.publicChat);
4763
5550
  await setTextRecord(subdomain, "xmtpGroupId", groupId, existingInfo.vault);
4764
5551
  cacheGroupId(subdomain, groupId);
4765
5552
  }
4766
5553
  } catch {
4767
- console.warn(chalk9.yellow("\n \u26A0 Could not set up XMTP chat group"));
5554
+ console.warn(chalk13.yellow("\n \u26A0 Could not set up XMTP chat group"));
4768
5555
  }
4769
5556
  try {
4770
5557
  const cron = await loadCron();
@@ -4774,19 +5561,19 @@ syndicate.command("create").description("Create a new syndicate via the factory
4774
5561
  spinner2.stop();
4775
5562
  console.log();
4776
5563
  console.log(LABEL5(" \u25C6 Syndicate Setup Resumed"));
4777
- SEP5();
4778
- console.log(W5(` ID: ${G5(`#${existingId}`)}`));
4779
- console.log(W5(` Vault: ${G5(existingInfo.vault)}`));
4780
- console.log(W5(` ENS: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
4781
- console.log(W5(` Chat: ${DIM5(`sherwood chat ${subdomain}`)}`));
5564
+ SEP6();
5565
+ console.log(W6(` ID: ${G6(`#${existingId}`)}`));
5566
+ console.log(W6(` Vault: ${G6(existingInfo.vault)}`));
5567
+ console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
5568
+ console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
4782
5569
  const refParam2 = getAgentId() ? `?ref=${getAgentId()}` : "";
4783
- console.log(W5(` Share: ${G5(`https://sherwood.sh/syndicate/${subdomain}${refParam2}`)}`));
4784
- SEP5();
4785
- console.log(G5(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
5570
+ console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam2}`)}`));
5571
+ SEP6();
5572
+ console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
4786
5573
  console.log();
4787
5574
  return;
4788
5575
  }
4789
- const spinner = ora8({ text: W5("Deploying vault via factory..."), color: "green" }).start();
5576
+ const spinner = ora8({ text: W6("Deploying vault via factory..."), color: "green" }).start();
4790
5577
  const result = await createSyndicate({
4791
5578
  creatorAgentId: BigInt(agentIdStr),
4792
5579
  metadataURI,
@@ -4801,7 +5588,7 @@ syndicate.command("create").description("Create a new syndicate via the factory
4801
5588
  addSyndicate(chainId, { subdomain, vault: result.vault, role: "creator" });
4802
5589
  setPrimarySyndicate(chainId, subdomain);
4803
5590
  await new Promise((r) => setTimeout(r, 2e3));
4804
- spinner.text = W5("Registering creator as agent...");
5591
+ spinner.text = W6("Registering creator as agent...");
4805
5592
  try {
4806
5593
  setVaultAddress(result.vault);
4807
5594
  const creatorAddress = getAccount().address;
@@ -4811,9 +5598,9 @@ syndicate.command("create").description("Create a new syndicate via the factory
4811
5598
  // agentAddress = creator EOA (direct execution)
4812
5599
  );
4813
5600
  } catch (regErr) {
4814
- console.warn(chalk9.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
5601
+ console.warn(chalk13.yellow("\n \u26A0 Could not auto-register creator as agent \u2014 register manually with `syndicate add`"));
4815
5602
  }
4816
- spinner.text = W5("Setting up chat...");
5603
+ spinner.text = W6("Setting up chat...");
4817
5604
  try {
4818
5605
  const xmtp = await loadXmtp();
4819
5606
  const xmtpClient = await xmtp.getXmtpClient();
@@ -4824,45 +5611,70 @@ syndicate.command("create").description("Create a new syndicate via the factory
4824
5611
  } catch {
4825
5612
  }
4826
5613
  } catch {
4827
- console.warn(chalk9.yellow("\n \u26A0 Could not create XMTP chat group"));
4828
- console.warn(chalk9.dim(` Recover later with: sherwood chat ${subdomain} init`));
5614
+ console.warn(chalk13.yellow("\n \u26A0 Could not create XMTP chat group"));
5615
+ console.warn(chalk13.dim(` Recover later with: sherwood chat ${subdomain} init`));
4829
5616
  }
4830
5617
  try {
4831
5618
  const cron = await loadCron();
4832
5619
  const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
4833
5620
  if (cronResult.isOpenClaw && cronResult.registered) {
4834
- console.log(G5(" \u2713 Participation crons registered (15m check + hourly summary)"));
5621
+ console.log(G6(" \u2713 Participation crons registered (15m check + hourly summary)"));
4835
5622
  } else if (!cronResult.isOpenClaw) {
4836
- console.log(DIM5(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
5623
+ console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
4837
5624
  }
4838
5625
  } catch {
4839
5626
  }
4840
5627
  spinner.stop();
4841
5628
  console.log();
4842
5629
  console.log(LABEL5(" \u25C6 Syndicate Created"));
4843
- SEP5();
4844
- console.log(W5(` ID: ${G5(`#${result.syndicateId}`)}`));
4845
- console.log(W5(` Vault: ${G5(result.vault)}`));
4846
- console.log(W5(` ENS: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
4847
- console.log(W5(` Metadata: ${DIM5(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
4848
- console.log(W5(` Explorer: ${DIM5(getExplorerUrl(result.hash))}`));
4849
- console.log(W5(` Chat: ${DIM5(`sherwood chat ${subdomain}`)}`));
5630
+ SEP6();
5631
+ console.log(W6(` ID: ${G6(`#${result.syndicateId}`)}`));
5632
+ console.log(W6(` Vault: ${G6(result.vault)}`));
5633
+ console.log(W6(` ENS: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
5634
+ console.log(W6(` Metadata: ${DIM6(metadataURI.length > 50 ? metadataURI.slice(0, 50) + "..." : metadataURI)}`));
5635
+ console.log(W6(` Explorer: ${DIM6(getExplorerUrl(result.hash))}`));
5636
+ console.log(W6(` Chat: ${DIM6(`sherwood chat ${subdomain}`)}`));
4850
5637
  const refParam = getAgentId() ? `?ref=${getAgentId()}` : "";
4851
- console.log(W5(` Share: ${G5(`https://sherwood.sh/syndicate/${subdomain}${refParam}`)}`));
4852
- SEP5();
4853
- console.log(G5(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
5638
+ console.log(W6(` Share: ${G6(`https://sherwood.sh/syndicate/${subdomain}${refParam}`)}`));
5639
+ SEP6();
5640
+ console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
4854
5641
  console.log();
4855
5642
  } catch (err) {
4856
- console.error(chalk9.red(`
5643
+ console.error(chalk13.red(`
4857
5644
  \u2716 ${formatContractError(err)}`));
4858
5645
  process.exit(1);
4859
5646
  }
4860
5647
  });
4861
- syndicate.command("list").description("List active syndicates (queries subgraph, falls back to on-chain)").option("--creator <address>", "Filter by creator address").action(async (opts) => {
5648
+ syndicate.command("list").description("List active syndicates (queries subgraph, falls back to on-chain)").option("--creator <address>", "Filter by creator address").option("--all-chains", "Aggregate active syndicates across all supported chains (on-chain only)").action(async (opts) => {
4862
5649
  const spinner = ora8("Loading syndicates...").start();
4863
5650
  try {
4864
- let syndicates;
4865
- if (process.env.SUBGRAPH_URL) {
5651
+ let syndicates = [];
5652
+ if (opts.allChains) {
5653
+ const includeTestnets = process.env.ENABLE_TESTNET === "true";
5654
+ const targets = VALID_NETWORKS.filter((n) => includeTestnets || !["base-sepolia", "robinhood-testnet", "hyperevm-testnet"].includes(n));
5655
+ for (const net of targets) {
5656
+ spinner.text = `Loading from ${net}...`;
5657
+ try {
5658
+ const rows = await withNetwork(net, () => getActiveSyndicates());
5659
+ for (const s of rows) {
5660
+ syndicates.push({
5661
+ id: s.id.toString(),
5662
+ vault: s.vault,
5663
+ creator: s.creator,
5664
+ metadataURI: s.metadataURI,
5665
+ createdAt: s.createdAt.toString(),
5666
+ subdomain: s.subdomain,
5667
+ chain: net
5668
+ });
5669
+ }
5670
+ } catch {
5671
+ }
5672
+ }
5673
+ if (opts.creator) {
5674
+ const c = String(opts.creator).toLowerCase();
5675
+ syndicates = syndicates.filter((s) => s.creator.toLowerCase() === c);
5676
+ }
5677
+ } else if (process.env.SUBGRAPH_URL) {
4866
5678
  const result = await getActiveSyndicates2(opts.creator);
4867
5679
  syndicates = result;
4868
5680
  } else {
@@ -4878,34 +5690,38 @@ syndicate.command("list").description("List active syndicates (queries subgraph,
4878
5690
  }
4879
5691
  spinner.stop();
4880
5692
  if (syndicates.length === 0) {
4881
- console.log(chalk9.dim("No active syndicates found."));
5693
+ console.log(chalk13.dim("No active syndicates found."));
4882
5694
  return;
4883
5695
  }
4884
5696
  console.log();
4885
- console.log(chalk9.bold(`Active Syndicates (${syndicates.length})`));
4886
- if (!process.env.SUBGRAPH_URL) {
4887
- console.log(chalk9.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
5697
+ console.log(chalk13.bold(`Active Syndicates (${syndicates.length})`));
5698
+ if (!process.env.SUBGRAPH_URL && !opts.allChains) {
5699
+ console.log(chalk13.dim(" (Set SUBGRAPH_URL for faster indexed queries)"));
5700
+ }
5701
+ if (opts.allChains) {
5702
+ console.log(chalk13.dim(" (Aggregated across chains via on-chain factory calls; SUBGRAPH_URL is single-chain only)"));
4888
5703
  }
4889
- console.log(chalk9.dim("\u2500".repeat(70)));
5704
+ console.log(chalk13.dim("\u2500".repeat(70)));
4890
5705
  for (const s of syndicates) {
4891
5706
  const ts = typeof s.createdAt === "string" ? Number(s.createdAt) : Number(s.createdAt);
4892
5707
  const date = new Date(ts * 1e3).toLocaleDateString();
4893
5708
  const ensName = s.subdomain ? `${s.subdomain}.sherwoodagent.eth` : "";
4894
- console.log(` #${s.id} ${chalk9.bold(ensName || String(s.vault))}`);
4895
- if (ensName) console.log(` Vault: ${chalk9.cyan(String(s.vault))}`);
5709
+ const chainTag = s.chain ? chalk13.dim(`[${s.chain}] `) : "";
5710
+ console.log(` ${chainTag}#${s.id} ${chalk13.bold(ensName || String(s.vault))}`);
5711
+ if (ensName) console.log(` Vault: ${chalk13.cyan(String(s.vault))}`);
4896
5712
  console.log(` Creator: ${s.creator}`);
4897
5713
  console.log(` Created: ${date}`);
4898
5714
  if (s.totalDeposits) {
4899
5715
  console.log(` Deposits: ${s.totalDeposits} USDC`);
4900
5716
  }
4901
5717
  if (s.metadataURI) {
4902
- console.log(` Metadata: ${chalk9.dim(s.metadataURI)}`);
5718
+ console.log(` Metadata: ${chalk13.dim(s.metadataURI)}`);
4903
5719
  }
4904
5720
  console.log();
4905
5721
  }
4906
5722
  } catch (err) {
4907
5723
  spinner.fail("Failed to load syndicates");
4908
- console.error(chalk9.red(formatContractError(err)));
5724
+ console.error(chalk13.red(formatContractError(err)));
4909
5725
  process.exit(1);
4910
5726
  }
4911
5727
  });
@@ -4913,42 +5729,70 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
4913
5729
  const spinner = ora8("Loading syndicate info...").start();
4914
5730
  try {
4915
5731
  let id;
5732
+ let crossChainHint = null;
5733
+ let infoOverride = null;
4916
5734
  if (/^\d+$/.test(idStr)) {
4917
5735
  id = BigInt(idStr);
4918
5736
  } else {
4919
- const resolved = await resolveSyndicate(idStr);
4920
- id = resolved.id;
5737
+ try {
5738
+ const resolved = await resolveSyndicate(idStr);
5739
+ id = resolved.id;
5740
+ } catch {
5741
+ spinner.text = `Searching across chains...`;
5742
+ const hits = await searchSyndicateAcrossChains(idStr);
5743
+ if (hits.length === 0) {
5744
+ spinner.fail(`Syndicate "${idStr}" not found on any supported chain`);
5745
+ process.exit(1);
5746
+ }
5747
+ const active = getNetwork();
5748
+ const primary = hits.find((h) => h.network === active) ?? hits[0];
5749
+ id = primary.result.id;
5750
+ infoOverride = await withNetwork(
5751
+ primary.network,
5752
+ () => getSyndicate(primary.result.id)
5753
+ );
5754
+ crossChainHint = {
5755
+ network: primary.network,
5756
+ others: hits.filter((h) => h.network !== primary.network).map((h) => h.network)
5757
+ };
5758
+ }
4921
5759
  }
4922
- const info = await getSyndicate(id);
5760
+ const info = infoOverride ?? await getSyndicate(id);
4923
5761
  spinner.stop();
5762
+ if (crossChainHint) {
5763
+ const others = crossChainHint.others.length > 0 ? ` (also on: ${crossChainHint.others.join(", ")})` : "";
5764
+ console.log(chalk13.yellow(` Found on chain: ${chalk13.bold(crossChainHint.network)}${others}`));
5765
+ console.log(chalk13.dim(` Re-run with --chain ${crossChainHint.network} to interact with this syndicate.`));
5766
+ console.log();
5767
+ }
4924
5768
  if (!info.vault || info.vault === "0x0000000000000000000000000000000000000000") {
4925
- console.log(chalk9.red(`Syndicate #${id} not found.`));
5769
+ console.log(chalk13.red(`Syndicate #${id} not found.`));
4926
5770
  process.exit(1);
4927
5771
  }
4928
5772
  const date = new Date(Number(info.createdAt) * 1e3).toLocaleDateString();
4929
5773
  console.log();
4930
- console.log(chalk9.bold(`Syndicate #${info.id}`));
4931
- console.log(chalk9.dim("\u2500".repeat(40)));
5774
+ console.log(chalk13.bold(`Syndicate #${info.id}`));
5775
+ console.log(chalk13.dim("\u2500".repeat(40)));
4932
5776
  if (info.subdomain) {
4933
- console.log(` ENS: ${chalk9.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
5777
+ console.log(` ENS: ${chalk13.bold(`${info.subdomain}.sherwoodagent.eth`)}`);
4934
5778
  }
4935
- console.log(` Vault: ${chalk9.cyan(info.vault)}`);
5779
+ console.log(` Vault: ${chalk13.cyan(info.vault)}`);
4936
5780
  console.log(` Creator: ${info.creator}`);
4937
5781
  console.log(` Created: ${date}`);
4938
- console.log(` Active: ${info.active ? chalk9.green("yes") : chalk9.red("no")}`);
5782
+ console.log(` Active: ${info.active ? chalk13.green("yes") : chalk13.red("no")}`);
4939
5783
  if (info.metadataURI) {
4940
- console.log(` Metadata: ${chalk9.dim(info.metadataURI)}`);
5784
+ console.log(` Metadata: ${chalk13.dim(info.metadataURI)}`);
4941
5785
  }
4942
5786
  if (info.subdomain) {
4943
5787
  const xmtpGroupId = getCachedGroupId(info.subdomain);
4944
5788
  if (xmtpGroupId) {
4945
- console.log(` XMTP Group: ${chalk9.cyan(xmtpGroupId)}`);
5789
+ console.log(` XMTP Group: ${chalk13.cyan(xmtpGroupId)}`);
4946
5790
  }
4947
5791
  }
4948
5792
  setVaultAddress(info.vault);
4949
- const vaultInfo = await getVaultInfo();
5793
+ const vaultInfo = crossChainHint ? await withNetwork(crossChainHint.network, () => getVaultInfo()) : await getVaultInfo();
4950
5794
  console.log();
4951
- console.log(chalk9.bold(" Vault Stats"));
5795
+ console.log(chalk13.bold(" Vault Stats"));
4952
5796
  console.log(` Total Assets: ${vaultInfo.totalAssets}`);
4953
5797
  console.log(` Agent Count: ${vaultInfo.agentCount}`);
4954
5798
  console.log(` Redemptions Locked: ${vaultInfo.redemptionsLocked}`);
@@ -4956,12 +5800,12 @@ syndicate.command("info").description("Display syndicate details by ID or subdom
4956
5800
  console.log();
4957
5801
  } catch (err) {
4958
5802
  spinner.fail("Failed to load syndicate info");
4959
- console.error(chalk9.red(formatContractError(err)));
5803
+ console.error(chalk13.red(formatContractError(err)));
4960
5804
  process.exit(1);
4961
5805
  }
4962
5806
  });
4963
5807
  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) => {
4964
- const spinner = ora8({ text: W5("Loading syndicate..."), color: "green" }).start();
5808
+ const spinner = ora8({ text: W6("Loading syndicate..."), color: "green" }).start();
4965
5809
  try {
4966
5810
  const syndicateId = BigInt(opts.id);
4967
5811
  let metadataURI = opts.uri;
@@ -4973,7 +5817,7 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
4973
5817
  }
4974
5818
  const name = opts.name || info.subdomain;
4975
5819
  const description = opts.description || `${name} \u2014 a Sherwood syndicate on ${info.subdomain}.sherwoodagent.eth`;
4976
- spinner.text = W5("Uploading metadata to IPFS...");
5820
+ spinner.text = W6("Uploading metadata to IPFS...");
4977
5821
  const metadata = {
4978
5822
  schema: "sherwood/syndicate/v1",
4979
5823
  name,
@@ -4984,15 +5828,15 @@ syndicate.command("update-metadata").description("Update syndicate metadata (cre
4984
5828
  links: {}
4985
5829
  };
4986
5830
  metadataURI = await uploadMetadata(metadata);
4987
- spinner.text = W5("Updating on-chain metadata...");
5831
+ spinner.text = W6("Updating on-chain metadata...");
4988
5832
  }
4989
5833
  const hash = await updateMetadata(syndicateId, metadataURI);
4990
- spinner.succeed(G5(`Metadata updated`));
4991
- console.log(DIM5(` IPFS: ${metadataURI}`));
4992
- console.log(DIM5(` ${getExplorerUrl(hash)}`));
5834
+ spinner.succeed(G6(`Metadata updated`));
5835
+ console.log(DIM6(` IPFS: ${metadataURI}`));
5836
+ console.log(DIM6(` ${getExplorerUrl(hash)}`));
4993
5837
  } catch (err) {
4994
5838
  spinner.fail("Metadata update failed");
4995
- console.error(chalk9.red(formatContractError(err)));
5839
+ console.error(chalk13.red(formatContractError(err)));
4996
5840
  process.exit(1);
4997
5841
  }
4998
5842
  });
@@ -5003,10 +5847,10 @@ syndicate.command("approve-depositor").description("Approve an address to deposi
5003
5847
  const depositor = validateAddress(opts.depositor, "depositor");
5004
5848
  const hash = await approveDepositor(depositor);
5005
5849
  spinner.succeed(`Depositor approved: ${hash}`);
5006
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
5850
+ console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
5007
5851
  } catch (err) {
5008
5852
  spinner.fail("Approval failed");
5009
- console.error(chalk9.red(formatContractError(err)));
5853
+ console.error(chalk13.red(formatContractError(err)));
5010
5854
  process.exit(1);
5011
5855
  }
5012
5856
  });
@@ -5017,10 +5861,10 @@ syndicate.command("remove-depositor").description("Remove an address from the de
5017
5861
  const depositor = validateAddress(opts.depositor, "depositor");
5018
5862
  const hash = await removeDepositor(depositor);
5019
5863
  spinner.succeed(`Depositor removed: ${hash}`);
5020
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
5864
+ console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
5021
5865
  } catch (err) {
5022
5866
  spinner.fail("Removal failed");
5023
- console.error(chalk9.red(formatContractError(err)));
5867
+ console.error(chalk13.red(formatContractError(err)));
5024
5868
  process.exit(1);
5025
5869
  }
5026
5870
  });
@@ -5040,7 +5884,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5040
5884
  if (opts.agentId) {
5041
5885
  agentId = BigInt(opts.agentId);
5042
5886
  } else {
5043
- const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-RQ7DTIBS.js");
5887
+ const { AGENT_REGISTRY: AGENT_REGISTRY2 } = await import("./addresses-DOWLGXGE.js");
5044
5888
  const registry2 = AGENT_REGISTRY2().IDENTITY_REGISTRY;
5045
5889
  if (registry2 === "0x0000000000000000000000000000000000000000") {
5046
5890
  agentId = 0n;
@@ -5061,7 +5905,7 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5061
5905
  });
5062
5906
  if (balance === 0n) {
5063
5907
  spinner.fail("Agent wallet does not own an ERC-8004 identity NFT");
5064
- console.error(chalk9.dim(" Mint one first: sherwood identity mint --name <name>"));
5908
+ console.error(chalk13.dim(" Mint one first: sherwood identity mint --name <name>"));
5065
5909
  process.exit(1);
5066
5910
  }
5067
5911
  const tokenId = await client.readContract({
@@ -5077,13 +5921,13 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5077
5921
  args: [agentWallet, 0n]
5078
5922
  });
5079
5923
  agentId = tokenId;
5080
- console.log(chalk9.dim(` Resolved agent ID: #${agentId}`));
5924
+ console.log(chalk13.dim(` Resolved agent ID: #${agentId}`));
5081
5925
  }
5082
5926
  }
5083
5927
  spinner.text = "Registering agent...";
5084
5928
  const hash = await registerAgent(agentId, agentWallet);
5085
5929
  spinner.succeed(`Agent registered: ${hash}`);
5086
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
5930
+ console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
5087
5931
  try {
5088
5932
  const xmtp = await loadXmtp();
5089
5933
  const xmtpClient = await xmtp.getXmtpClient();
@@ -5095,14 +5939,14 @@ syndicate.command("add").description("Register an agent on a syndicate vault (cr
5095
5939
  syndicate: subdomain,
5096
5940
  timestamp: Math.floor(Date.now() / 1e3)
5097
5941
  });
5098
- console.log(chalk9.dim(` Added to chat: ${subdomain}`));
5942
+ console.log(chalk13.dim(` Added to chat: ${subdomain}`));
5099
5943
  } catch {
5100
- console.warn(chalk9.yellow(" \u26A0 Could not add agent to chat group"));
5101
- console.warn(chalk9.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
5944
+ console.warn(chalk13.yellow(" \u26A0 Could not add agent to chat group"));
5945
+ console.warn(chalk13.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
5102
5946
  }
5103
5947
  } catch (err) {
5104
5948
  spinner.fail("Registration failed");
5105
- console.error(chalk9.red(formatContractError(err)));
5949
+ console.error(chalk13.red(formatContractError(err)));
5106
5950
  process.exit(1);
5107
5951
  }
5108
5952
  });
@@ -5110,7 +5954,7 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
5110
5954
  try {
5111
5955
  const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
5112
5956
  if (!subdomain) {
5113
- console.error(chalk9.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
5957
+ console.error(chalk13.red(" No syndicate configured. Pass --subdomain <name> or run 'syndicate create/join' first."));
5114
5958
  process.exit(1);
5115
5959
  }
5116
5960
  const syndicateInfo = await resolveSyndicate(subdomain);
@@ -5119,12 +5963,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
5119
5963
  const dashboardUrl = `https://sherwood.sh/syndicate/${subdomain}${refParam}`;
5120
5964
  console.log();
5121
5965
  console.log(LABEL5(" \u25C6 Share Your Syndicate"));
5122
- SEP5();
5123
- console.log(W5(` Dashboard: ${G5(dashboardUrl)}`));
5124
- console.log(W5(` ENS: ${DIM5(`${subdomain}.sherwoodagent.eth`)}`));
5125
- console.log(W5(` Vault: ${DIM5(syndicateInfo.vault)}`));
5126
- console.log(W5(` Join CLI: ${DIM5(`sherwood syndicate join --subdomain ${subdomain}`)}`));
5127
- SEP5();
5966
+ SEP6();
5967
+ console.log(W6(` Dashboard: ${G6(dashboardUrl)}`));
5968
+ console.log(W6(` ENS: ${DIM6(`${subdomain}.sherwoodagent.eth`)}`));
5969
+ console.log(W6(` Vault: ${DIM6(syndicateInfo.vault)}`));
5970
+ console.log(W6(` Join CLI: ${DIM6(`sherwood syndicate join --subdomain ${subdomain}`)}`));
5971
+ SEP6();
5128
5972
  try {
5129
5973
  const { exec } = await import("child_process");
5130
5974
  const platform = (await import("os")).platform();
@@ -5132,12 +5976,12 @@ syndicate.command("share").description("Print the shareable dashboard URL for yo
5132
5976
  const proc = exec(cmd);
5133
5977
  proc.stdin?.write(dashboardUrl);
5134
5978
  proc.stdin?.end();
5135
- console.log(G5(" \u2713 Dashboard URL copied to clipboard"));
5979
+ console.log(G6(" \u2713 Dashboard URL copied to clipboard"));
5136
5980
  } catch {
5137
5981
  }
5138
5982
  console.log();
5139
5983
  } catch (err) {
5140
- console.error(chalk9.red(` \u2716 ${formatContractError(err)}`));
5984
+ console.error(chalk13.red(` \u2716 ${formatContractError(err)}`));
5141
5985
  process.exit(1);
5142
5986
  }
5143
5987
  });
@@ -5154,7 +5998,25 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5154
5998
  spinner.fail("No agent identity found. Run 'sherwood identity mint' first.");
5155
5999
  process.exit(1);
5156
6000
  }
5157
- const syndicate2 = await resolveSyndicate(subdomain);
6001
+ let syndicate2;
6002
+ try {
6003
+ syndicate2 = await resolveSyndicate(subdomain);
6004
+ } catch {
6005
+ spinner.text = "Searching across chains...";
6006
+ const hits = await searchSyndicateAcrossChains(subdomain);
6007
+ spinner.fail(`Syndicate "${subdomain}" not found on ${getNetwork()}`);
6008
+ if (hits.length === 0) {
6009
+ console.error(chalk13.red(` Not found on any supported chain.`));
6010
+ } else if (hits.length === 1) {
6011
+ console.error(chalk13.yellow(` Found on chain: ${chalk13.bold(hits[0].network)}`));
6012
+ console.error(chalk13.dim(` Re-run: sherwood --chain ${hits[0].network} syndicate join --subdomain ${subdomain}`));
6013
+ } else {
6014
+ const chains = hits.map((h) => h.network).join(", ");
6015
+ console.error(chalk13.yellow(` Found on chains: ${chalk13.bold(chains)}`));
6016
+ console.error(chalk13.dim(` Specify which chain to join with --chain <network>.`));
6017
+ }
6018
+ process.exit(1);
6019
+ }
5158
6020
  const callerAddress = getAccount().address;
5159
6021
  spinner.text = "Checking membership...";
5160
6022
  setVaultAddress(syndicate2.vault);
@@ -5164,17 +6026,17 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5164
6026
  try {
5165
6027
  const xmtp = await loadXmtp();
5166
6028
  await xmtp.getXmtpClient();
5167
- console.log(chalk9.dim(" XMTP identity ready"));
6029
+ console.log(chalk13.dim(" XMTP identity ready"));
5168
6030
  } catch {
5169
- console.warn(chalk9.yellow(" \u26A0 Could not initialize XMTP identity"));
6031
+ console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity"));
5170
6032
  }
5171
6033
  try {
5172
6034
  const cron = await loadCron();
5173
6035
  const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
5174
6036
  if (cronResult.isOpenClaw && cronResult.registered) {
5175
- console.log(chalk9.green(" \u2713 Participation crons registered"));
6037
+ console.log(chalk13.green(" \u2713 Participation crons registered"));
5176
6038
  } else if (!cronResult.isOpenClaw) {
5177
- console.log(chalk9.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
6039
+ console.log(chalk13.dim(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
5178
6040
  }
5179
6041
  } catch {
5180
6042
  }
@@ -5187,14 +6049,14 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5187
6049
  );
5188
6050
  if (existingRequest) {
5189
6051
  spinner.succeed("You already have a pending join request for this syndicate");
5190
- console.log(chalk9.dim(` Attestation: ${existingRequest.uid}`));
5191
- console.log(chalk9.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
6052
+ console.log(chalk13.dim(` Attestation: ${existingRequest.uid}`));
6053
+ console.log(chalk13.dim(` Submitted: ${new Date(existingRequest.time * 1e3).toLocaleString()}`));
5192
6054
  try {
5193
6055
  const xmtp = await loadXmtp();
5194
6056
  await xmtp.getXmtpClient();
5195
- console.log(chalk9.dim(" XMTP identity ready"));
6057
+ console.log(chalk13.dim(" XMTP identity ready"));
5196
6058
  } catch {
5197
- console.warn(chalk9.yellow(" \u26A0 Could not initialize XMTP identity"));
6059
+ console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity"));
5198
6060
  }
5199
6061
  return;
5200
6062
  }
@@ -5215,7 +6077,7 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5215
6077
  spinner.succeed("Join request created (XMTP identity ready)");
5216
6078
  } catch {
5217
6079
  spinner.succeed("Join request created");
5218
- console.warn(chalk9.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
6080
+ console.warn(chalk13.yellow(" \u26A0 Could not initialize XMTP identity \u2014 creator may not be able to auto-add you to chat"));
5219
6081
  }
5220
6082
  addSyndicate(getChain().id, { subdomain, vault: syndicate2.vault, role: "agent" });
5221
6083
  setPrimarySyndicate(getChain().id, subdomain);
@@ -5223,29 +6085,29 @@ syndicate.command("join").description("Request to join a syndicate (creates an E
5223
6085
  const cron = await loadCron();
5224
6086
  const cronResult = cron.registerSyndicateCrons(subdomain, isTestnet(), getNotifyTo());
5225
6087
  if (cronResult.isOpenClaw && cronResult.registered) {
5226
- console.log(G5(" \u2713 Participation crons registered (will activate after approval)"));
6088
+ console.log(G6(" \u2713 Participation crons registered (will activate after approval)"));
5227
6089
  } else if (!cronResult.isOpenClaw) {
5228
- console.log(DIM5(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
6090
+ console.log(DIM6(" Tip: Set up a scheduled process to run `sherwood session check " + subdomain + "` periodically"));
5229
6091
  }
5230
6092
  } catch {
5231
6093
  }
5232
6094
  console.log();
5233
6095
  console.log(LABEL5(" \u25C6 Join Request Submitted"));
5234
- SEP5();
5235
- console.log(W5(` Syndicate: ${G5(`${subdomain}.sherwoodagent.eth`)}`));
5236
- console.log(W5(` Agent ID: #${agentId}`));
5237
- console.log(W5(` Creator: ${DIM5(syndicate2.creator)}`));
5238
- console.log(W5(` Attestation: ${DIM5(uid)}`));
5239
- console.log(W5(` EAS Scan: ${DIM5(getEasScanUrl(uid))}`));
5240
- console.log(W5(` Explorer: ${DIM5(getExplorerUrl(hash))}`));
5241
- SEP5();
5242
- console.log(G5(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
5243
- console.log(G5(" \u2713 The creator can review with:"));
5244
- console.log(DIM5(` sherwood syndicate requests --subdomain ${subdomain}`));
6096
+ SEP6();
6097
+ console.log(W6(` Syndicate: ${G6(`${subdomain}.sherwoodagent.eth`)}`));
6098
+ console.log(W6(` Agent ID: #${agentId}`));
6099
+ console.log(W6(` Creator: ${DIM6(syndicate2.creator)}`));
6100
+ console.log(W6(` Attestation: ${DIM6(uid)}`));
6101
+ console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(uid))}`));
6102
+ console.log(W6(` Explorer: ${DIM6(getEasExplorerUrl(hash))}`));
6103
+ SEP6();
6104
+ console.log(G6(" \u2713 Syndicate saved to ~/.sherwood/config.json (set as primary)"));
6105
+ console.log(G6(" \u2713 The creator can review with:"));
6106
+ console.log(DIM6(` sherwood syndicate requests --subdomain ${subdomain}`));
5245
6107
  console.log();
5246
6108
  } catch (err) {
5247
6109
  spinner.fail("Join request failed");
5248
- console.error(chalk9.red(formatContractError(err)));
6110
+ console.error(chalk13.red(formatContractError(err)));
5249
6111
  process.exit(1);
5250
6112
  }
5251
6113
  });
@@ -5286,34 +6148,34 @@ syndicate.command("requests").description("View pending join requests for a synd
5286
6148
  );
5287
6149
  spinner.stop();
5288
6150
  if (requests.length === 0) {
5289
- console.log(DIM5("\n No pending join requests.\n"));
6151
+ console.log(DIM6("\n No pending join requests.\n"));
5290
6152
  return;
5291
6153
  }
5292
6154
  console.log();
5293
6155
  console.log(LABEL5(` \u25C6 Pending Join Requests (${requests.length})`));
5294
- SEP5();
6156
+ SEP6();
5295
6157
  for (let i = 0; i < requests.length; i++) {
5296
6158
  const req = requests[i];
5297
6159
  const date = new Date(req.time * 1e3).toLocaleString();
5298
6160
  const referrer = parseReferrer(req.decoded.message);
5299
6161
  const cleanMessage = stripReferrerPrefix(req.decoded.message);
5300
- console.log(W5(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM5(`(${req.attester})`)}`));
5301
- console.log(DIM5(` Message: "${cleanMessage}"`));
6162
+ console.log(W6(` ${i + 1}. Agent #${req.decoded.agentId} ${DIM6(`(${req.attester})`)}`));
6163
+ console.log(DIM6(` Message: "${cleanMessage}"`));
5302
6164
  if (referrer !== null) {
5303
- console.log(G5(` Referred by: Agent #${referrer}`));
6165
+ console.log(G6(` Referred by: Agent #${referrer}`));
5304
6166
  }
5305
- console.log(DIM5(` Requested: ${date}`));
5306
- console.log(DIM5(` Attestation: ${req.uid}`));
6167
+ console.log(DIM6(` Requested: ${date}`));
6168
+ console.log(DIM6(` Attestation: ${req.uid}`));
5307
6169
  console.log();
5308
6170
  }
5309
- console.log(G5(" To approve:"));
5310
- console.log(DIM5(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
5311
- console.log(G5(" To reject:"));
5312
- console.log(DIM5(` sherwood syndicate reject --attestation <uid>`));
6171
+ console.log(G6(" To approve:"));
6172
+ console.log(DIM6(` sherwood syndicate approve --agent-id <id> --wallet <addr>`));
6173
+ console.log(G6(" To reject:"));
6174
+ console.log(DIM6(` sherwood syndicate reject --attestation <uid>`));
5313
6175
  console.log();
5314
6176
  } catch (err) {
5315
6177
  spinner.fail("Failed to load requests");
5316
- console.error(chalk9.red(formatContractError(err)));
6178
+ console.error(chalk13.red(formatContractError(err)));
5317
6179
  process.exit(1);
5318
6180
  }
5319
6181
  });
@@ -5343,11 +6205,11 @@ syndicate.command("approve").description("Approve an agent join request (registe
5343
6205
  agentWallet
5344
6206
  );
5345
6207
  agentWasRegistered = true;
5346
- console.log(DIM5(` Agent registered: ${getExplorerUrl(regHash)}`));
6208
+ console.log(DIM6(` Agent registered: ${getExplorerUrl(regHash)}`));
5347
6209
  } catch (regErr) {
5348
6210
  const msg = regErr instanceof Error ? regErr.message : String(regErr);
5349
6211
  if (msg.includes("0xe098d3ee") || msg.includes("AgentAlreadyRegistered")) {
5350
- console.log(DIM5(" Agent already registered on vault \u2014 skipping"));
6212
+ console.log(DIM6(" Agent already registered on vault \u2014 skipping"));
5351
6213
  } else {
5352
6214
  throw regErr;
5353
6215
  }
@@ -5363,7 +6225,7 @@ syndicate.command("approve").description("Approve an agent join request (registe
5363
6225
  let approvalUid;
5364
6226
  if (alreadyApproved) {
5365
6227
  approvalUid = alreadyApproved.uid;
5366
- console.log(DIM5(` Approval attestation already exists \u2014 skipping`));
6228
+ console.log(DIM6(` Approval attestation already exists \u2014 skipping`));
5367
6229
  } else {
5368
6230
  spinner.text = "Creating approval attestation...";
5369
6231
  const result = await createApproval(
@@ -5386,45 +6248,42 @@ syndicate.command("approve").description("Approve an agent join request (registe
5386
6248
  syndicate: subdomain,
5387
6249
  timestamp: Math.floor(Date.now() / 1e3)
5388
6250
  });
5389
- console.log(DIM5(` Added to chat: ${subdomain}`));
6251
+ console.log(DIM6(` Added to chat: ${subdomain}`));
5390
6252
  } catch {
5391
- console.warn(chalk9.yellow(" \u26A0 Could not add agent to chat group"));
5392
- console.warn(chalk9.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
6253
+ console.warn(chalk13.yellow(" \u26A0 Could not add agent to chat group"));
6254
+ console.warn(chalk13.dim(` If no group exists, run: sherwood chat ${subdomain} init`));
5393
6255
  }
5394
6256
  spinner.succeed("Agent approved and registered");
5395
6257
  console.log();
5396
6258
  console.log(LABEL5(" \u25C6 Agent Approved"));
5397
- SEP5();
5398
- console.log(W5(` Agent ID: #${opts.agentId}`));
5399
- console.log(W5(` Wallet: ${G5(opts.wallet)}`));
5400
- console.log(W5(` Approval: ${DIM5(approvalUid)}`));
5401
- console.log(W5(` EAS Scan: ${DIM5(getEasScanUrl(approvalUid))}`));
5402
- SEP5();
6259
+ SEP6();
6260
+ console.log(W6(` Agent ID: #${opts.agentId}`));
6261
+ console.log(W6(` Wallet: ${G6(opts.wallet)}`));
6262
+ console.log(W6(` Approval: ${DIM6(approvalUid)}`));
6263
+ console.log(W6(` EAS Scan: ${DIM6(getEasScanUrl(approvalUid))}`));
6264
+ SEP6();
5403
6265
  } catch (err) {
5404
6266
  spinner.fail("Approval failed");
5405
- console.error(chalk9.red(formatContractError(err)));
6267
+ console.error(chalk13.red(formatContractError(err)));
5406
6268
  process.exit(1);
5407
6269
  }
5408
6270
  });
5409
6271
  syndicate.command("reject").description("Reject a join request by revoking its attestation").requiredOption("--attestation <uid>", "Join request attestation UID to revoke").action(async (opts) => {
5410
6272
  const spinner = ora8("Revoking attestation...").start();
5411
6273
  try {
5412
- const hash = await revokeAttestation(
5413
- EAS_SCHEMAS().SYNDICATE_JOIN_REQUEST,
5414
- opts.attestation
5415
- );
6274
+ const hash = await revokeJoinRequest(opts.attestation);
5416
6275
  spinner.succeed("Join request rejected");
5417
- console.log(DIM5(` ${getExplorerUrl(hash)}`));
6276
+ console.log(DIM6(` ${getEasExplorerUrl(hash)}`));
5418
6277
  } catch (err) {
5419
6278
  spinner.fail("Rejection failed");
5420
- console.error(chalk9.red(formatContractError(err)));
6279
+ console.error(chalk13.red(formatContractError(err)));
5421
6280
  process.exit(1);
5422
6281
  }
5423
6282
  });
5424
6283
  syndicate.command("leave").description("Leave a syndicate \u2014 removes participation crons and session state").option("--subdomain <name>", "Syndicate subdomain to leave").action(async (opts) => {
5425
6284
  const subdomain = opts.subdomain || getPrimarySyndicate(getChain().id)?.subdomain;
5426
6285
  if (!subdomain) {
5427
- console.error(chalk9.red(" No syndicate configured. Pass --subdomain <name>."));
6286
+ console.error(chalk13.red(" No syndicate configured. Pass --subdomain <name>."));
5428
6287
  process.exit(1);
5429
6288
  }
5430
6289
  const spinner = ora8("Cleaning up...").start();
@@ -5440,17 +6299,17 @@ syndicate.command("leave").description("Leave a syndicate \u2014 removes partici
5440
6299
  resetSession(subdomain);
5441
6300
  spinner.succeed("Left syndicate");
5442
6301
  if (cronsRemoved) {
5443
- console.log(G5(" \u2713 Participation crons removed"));
6302
+ console.log(G6(" \u2713 Participation crons removed"));
5444
6303
  }
5445
- console.log(G5(" \u2713 Session state cleared"));
6304
+ console.log(G6(" \u2713 Session state cleared"));
5446
6305
  console.log();
5447
- console.log(chalk9.dim(" Note: This does not remove you on-chain. To exit your position:"));
5448
- console.log(chalk9.dim(" sherwood vault balance \u2014 check your LP share balance"));
5449
- console.log(chalk9.dim(" Redeem shares via the vault contract or dashboard"));
6306
+ console.log(chalk13.dim(" Note: This does not remove you on-chain. To exit your position:"));
6307
+ console.log(chalk13.dim(" sherwood vault balance \u2014 check your LP share balance"));
6308
+ console.log(chalk13.dim(" Redeem shares via the vault contract or dashboard"));
5450
6309
  console.log();
5451
6310
  } catch (err) {
5452
6311
  spinner.fail("Leave failed");
5453
- console.error(chalk9.red(err instanceof Error ? err.message : String(err)));
6312
+ console.error(chalk13.red(err instanceof Error ? err.message : String(err)));
5454
6313
  process.exit(1);
5455
6314
  }
5456
6315
  });
@@ -5461,21 +6320,21 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
5461
6320
  if (!opts.subdomain && !opts.vault) {
5462
6321
  const primary = getPrimarySyndicate(chainId);
5463
6322
  if (memberships.length === 0) {
5464
- console.log(DIM5("\n No syndicates saved. Create or join one first.\n"));
6323
+ console.log(DIM6("\n No syndicates saved. Create or join one first.\n"));
5465
6324
  return;
5466
6325
  }
5467
6326
  console.log();
5468
6327
  console.log(LABEL5(" \u25C6 Your Syndicates"));
5469
- SEP5();
6328
+ SEP6();
5470
6329
  for (const m of memberships) {
5471
6330
  const isPrimary = primary?.subdomain === m.subdomain;
5472
- const marker = isPrimary ? G5(" \u2605") : "";
5473
- console.log(W5(` ${m.subdomain}.sherwoodagent.eth${marker} ${DIM5(`(${m.role})`)}`));
5474
- console.log(DIM5(` vault: ${m.vault}`));
6331
+ const marker = isPrimary ? G6(" \u2605") : "";
6332
+ console.log(W6(` ${m.subdomain}.sherwoodagent.eth${marker} ${DIM6(`(${m.role})`)}`));
6333
+ console.log(DIM6(` vault: ${m.vault}`));
5475
6334
  }
5476
- SEP5();
6335
+ SEP6();
5477
6336
  if (memberships.length > 1) {
5478
- console.log(DIM5(" Switch with: sherwood syndicate set-primary --subdomain <name>"));
6337
+ console.log(DIM6(" Switch with: sherwood syndicate set-primary --subdomain <name>"));
5479
6338
  }
5480
6339
  console.log();
5481
6340
  return;
@@ -5501,15 +6360,15 @@ syndicate.command("set-primary").description("Set the active syndicate for CLI c
5501
6360
  }
5502
6361
  }
5503
6362
  if (!target) {
5504
- console.error(chalk9.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
6363
+ console.error(chalk13.red(" Could not resolve syndicate. Pass --subdomain or --vault."));
5505
6364
  process.exit(1);
5506
6365
  }
5507
6366
  setPrimarySyndicate(chainId, target);
5508
- console.log(G5(`
6367
+ console.log(G6(`
5509
6368
  \u2713 Primary syndicate set to ${target}.sherwoodagent.eth
5510
6369
  `));
5511
6370
  } catch (err) {
5512
- console.error(chalk9.red(formatContractError(err)));
6371
+ console.error(chalk13.red(formatContractError(err)));
5513
6372
  process.exit(1);
5514
6373
  }
5515
6374
  });
@@ -5523,7 +6382,7 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
5523
6382
  await preflightDeposit(amount);
5524
6383
  }
5525
6384
  } catch (err) {
5526
- console.error(chalk9.red(`
6385
+ console.error(chalk13.red(`
5527
6386
  \u2716 ${err instanceof Error ? err.message : String(err)}
5528
6387
  `));
5529
6388
  process.exit(1);
@@ -5538,10 +6397,10 @@ vaultCmd.command("deposit").description("Deposit into a vault").option("--vault
5538
6397
  hash = await deposit(amount);
5539
6398
  }
5540
6399
  spinner.succeed(`Deposited: ${hash}`);
5541
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
6400
+ console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
5542
6401
  } catch (err) {
5543
6402
  spinner.fail("Deposit failed");
5544
- console.error(chalk9.red(formatContractError(err)));
6403
+ console.error(chalk13.red(formatContractError(err)));
5545
6404
  process.exit(1);
5546
6405
  }
5547
6406
  });
@@ -5552,8 +6411,8 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
5552
6411
  const info = await getVaultInfo();
5553
6412
  spinner.stop();
5554
6413
  console.log();
5555
- console.log(chalk9.bold("Vault Info"));
5556
- console.log(chalk9.dim("\u2500".repeat(40)));
6414
+ console.log(chalk13.bold("Vault Info"));
6415
+ console.log(chalk13.dim("\u2500".repeat(40)));
5557
6416
  console.log(` Address: ${info.address}`);
5558
6417
  console.log(` Total Assets: ${info.totalAssets}`);
5559
6418
  console.log(` Agent Count: ${info.agentCount}`);
@@ -5562,7 +6421,7 @@ vaultCmd.command("info").description("Display vault state").option("--vault <add
5562
6421
  console.log();
5563
6422
  } catch (err) {
5564
6423
  spinner.fail("Failed to load vault info");
5565
- console.error(chalk9.red(formatContractError(err)));
6424
+ console.error(chalk13.red(formatContractError(err)));
5566
6425
  process.exit(1);
5567
6426
  }
5568
6427
  });
@@ -5573,15 +6432,15 @@ vaultCmd.command("balance").description("Show LP share balance and asset value")
5573
6432
  const balance = await getBalance(opts.address);
5574
6433
  spinner.stop();
5575
6434
  console.log();
5576
- console.log(chalk9.bold("LP Position"));
5577
- console.log(chalk9.dim("\u2500".repeat(40)));
6435
+ console.log(chalk13.bold("LP Position"));
6436
+ console.log(chalk13.dim("\u2500".repeat(40)));
5578
6437
  console.log(` Shares: ${balance.shares.toString()}`);
5579
6438
  console.log(` Asset Value: ${balance.assetsValue}`);
5580
6439
  console.log(` % of Vault: ${balance.percentOfVault}`);
5581
6440
  console.log();
5582
6441
  } catch (err) {
5583
6442
  spinner.fail("Failed to load balance");
5584
- console.error(chalk9.red(formatContractError(err)));
6443
+ console.error(chalk13.red(formatContractError(err)));
5585
6444
  process.exit(1);
5586
6445
  }
5587
6446
  });
@@ -5596,18 +6455,18 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
5596
6455
  } else {
5597
6456
  shares = await getShareBalance();
5598
6457
  if (shares === 0n) {
5599
- console.error(chalk9.red("\n \u2716 No shares to redeem.\n"));
6458
+ console.error(chalk13.red("\n \u2716 No shares to redeem.\n"));
5600
6459
  process.exit(1);
5601
6460
  }
5602
6461
  }
5603
6462
  } catch (err) {
5604
- console.error(chalk9.red(`
6463
+ console.error(chalk13.red(`
5605
6464
  \u2716 ${err instanceof Error ? err.message : String(err)}
5606
6465
  `));
5607
6466
  process.exit(1);
5608
6467
  }
5609
6468
  if (opts.receiver && !isAddress6(opts.receiver)) {
5610
- console.error(chalk9.red(`
6469
+ console.error(chalk13.red(`
5611
6470
  \u2716 Invalid receiver address: ${opts.receiver}
5612
6471
  `));
5613
6472
  process.exit(1);
@@ -5615,7 +6474,7 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
5615
6474
  try {
5616
6475
  await preflightRedeem(shares);
5617
6476
  } catch (err) {
5618
- console.error(chalk9.red(`
6477
+ console.error(chalk13.red(`
5619
6478
  \u2716 ${err instanceof Error ? err.message : String(err)}
5620
6479
  `));
5621
6480
  process.exit(1);
@@ -5628,18 +6487,18 @@ vaultCmd.command("redeem").description("Redeem vault shares for the underlying a
5628
6487
  opts.receiver
5629
6488
  );
5630
6489
  spinner.succeed(`Redeemed: ${hash}`);
5631
- console.log(chalk9.dim(` ${getExplorerUrl(hash)}`));
6490
+ console.log(chalk13.dim(` ${getExplorerUrl(hash)}`));
5632
6491
  console.log(` Assets received: ${formatUnits5(assetsOut, assetDecimals)}`);
5633
6492
  } catch (err) {
5634
6493
  spinner.fail("Redeem failed");
5635
- console.error(chalk9.red(formatContractError(err)));
6494
+ console.error(chalk13.red(formatContractError(err)));
5636
6495
  process.exit(1);
5637
6496
  }
5638
6497
  });
5639
6498
  var strategy = program.command("strategy").description("Strategy templates \u2014 list, clone, propose");
5640
6499
  registerStrategyTemplateCommands(strategy);
5641
6500
  program.command("providers").description("List available DeFi providers").action(async () => {
5642
- const { MessariProvider, NansenProvider } = await import("./research-PB22GPXC.js");
6501
+ const { MessariProvider, NansenProvider } = await import("./research-YJWSQS2A.js");
5643
6502
  const providers = [new MoonwellProvider(), new UniswapProvider(), new MessariProvider(), new NansenProvider()];
5644
6503
  for (const p of providers) {
5645
6504
  const info = p.info();
@@ -5650,17 +6509,17 @@ ${info.name} (${info.type})`);
5650
6509
  }
5651
6510
  });
5652
6511
  try {
5653
- const { registerChatCommands } = await import("./chat-D5OF4OTL.js");
6512
+ const { registerChatCommands } = await import("./chat-MRTPUZ3X.js");
5654
6513
  registerChatCommands(program);
5655
6514
  } catch {
5656
6515
  program.command("chat <name> [action] [actionArgs...]").description("Syndicate chat (XMTP) \u2014 requires @xmtp/cli").action(() => {
5657
- console.error(chalk9.red("XMTP CLI not available."));
5658
- console.error(chalk9.dim("Install with: npm install -g @xmtp/cli"));
5659
- console.error(chalk9.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
6516
+ console.error(chalk13.red("XMTP CLI not available."));
6517
+ console.error(chalk13.dim("Install with: npm install -g @xmtp/cli"));
6518
+ console.error(chalk13.dim("Or reinstall: npm i -g @sherwoodagent/cli"));
5660
6519
  process.exit(1);
5661
6520
  });
5662
6521
  }
5663
- var { registerSessionCommands } = await import("./session-3Z6I2IM3.js");
6522
+ var { registerSessionCommands } = await import("./session-FPCPCVQX.js");
5664
6523
  registerSessionCommands(program);
5665
6524
  registerVeniceCommands(program);
5666
6525
  registerAllowanceCommands(program);
@@ -5668,65 +6527,71 @@ registerIdentityCommands(program);
5668
6527
  registerProposalCommands(program);
5669
6528
  registerGovernorCommands(program);
5670
6529
  registerGuardianCommands(program);
5671
- var { registerResearchCommands } = await import("./research-SVWNPHIN.js");
6530
+ var { registerResearchCommands } = await import("./research-QBKISW2M.js");
5672
6531
  registerResearchCommands(program);
5673
- var { registerAgentCommands } = await import("./agent-SXEV65ME.js");
6532
+ var { registerAgentCommands } = await import("./agent-4HKHLQEJ.js");
5674
6533
  registerAgentCommands(program);
5675
- var { registerTradeCommands } = await import("./trade-E5HSZE44.js");
6534
+ var { registerTradeCommands } = await import("./trade-NO6CNWCA.js");
5676
6535
  registerTradeCommands(program);
6536
+ registerGridCommand(program);
5677
6537
  var configCmd = program.command("config");
5678
- 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) => {
6538
+ 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) => {
5679
6539
  let saved = false;
5680
6540
  if (opts.privateKey) {
5681
6541
  setPrivateKey(opts.privateKey);
5682
6542
  const account = getAccount();
5683
- console.log(chalk9.green("Private key saved to ~/.sherwood/config.json"));
5684
- console.log(chalk9.dim(` Wallet: ${account.address}`));
6543
+ console.log(chalk13.green("Private key saved to ~/.sherwood/config.json"));
6544
+ console.log(chalk13.dim(` Wallet: ${account.address}`));
5685
6545
  saved = true;
5686
6546
  }
5687
6547
  if (opts.vault) {
5688
6548
  const chainId = getChain().id;
5689
6549
  setChainContract(chainId, "vault", opts.vault);
5690
- console.log(chalk9.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
5691
- console.log(chalk9.dim(` Vault: ${opts.vault}`));
6550
+ console.log(chalk13.green(`Vault saved to ~/.sherwood/config.json (chain ${chainId})`));
6551
+ console.log(chalk13.dim(` Vault: ${opts.vault}`));
5692
6552
  saved = true;
5693
6553
  }
5694
6554
  if (opts.rpc) {
5695
6555
  const network = getNetwork();
5696
6556
  setConfigRpcUrl(network, opts.rpc);
5697
- console.log(chalk9.green(`RPC URL saved for ${network}`));
5698
- console.log(chalk9.dim(` RPC: ${opts.rpc}`));
6557
+ console.log(chalk13.green(`RPC URL saved for ${network}`));
6558
+ console.log(chalk13.dim(` RPC: ${opts.rpc}`));
5699
6559
  saved = true;
5700
6560
  }
5701
6561
  if (opts.notifyTo) {
5702
6562
  setNotifyTo(opts.notifyTo);
5703
- console.log(chalk9.green("Notify destination saved to ~/.sherwood/config.json"));
5704
- console.log(chalk9.dim(` Notify to: ${opts.notifyTo}`));
6563
+ console.log(chalk13.green("Notify destination saved to ~/.sherwood/config.json"));
6564
+ console.log(chalk13.dim(` Notify to: ${opts.notifyTo}`));
5705
6565
  saved = true;
5706
6566
  }
5707
6567
  if (opts.uniswapApiKey) {
5708
6568
  setUniswapApiKey(opts.uniswapApiKey);
5709
- console.log(chalk9.green("Uniswap API key saved to ~/.sherwood/config.json"));
6569
+ console.log(chalk13.green("Uniswap API key saved to ~/.sherwood/config.json"));
5710
6570
  saved = true;
5711
6571
  }
5712
6572
  if (opts.veniceApiKey) {
5713
6573
  setVeniceApiKey(opts.veniceApiKey);
5714
- console.log(chalk9.green("Venice API key saved to ~/.sherwood/config.json"));
6574
+ console.log(chalk13.green("Venice API key saved to ~/.sherwood/config.json"));
6575
+ saved = true;
6576
+ }
6577
+ if (opts.anthropicApiKey) {
6578
+ setAnthropicApiKey(opts.anthropicApiKey);
6579
+ console.log(chalk13.green("Anthropic API key saved to ~/.sherwood/config.json"));
5715
6580
  saved = true;
5716
6581
  }
5717
6582
  if (opts.xmtpGroup) {
5718
6583
  const parts = opts.xmtpGroup.split(":");
5719
6584
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
5720
- console.log(chalk9.red("Format: --xmtp-group <subdomain>:<groupId>"));
6585
+ console.log(chalk13.red("Format: --xmtp-group <subdomain>:<groupId>"));
5721
6586
  process.exit(1);
5722
6587
  }
5723
6588
  cacheGroupId(parts[0], parts[1]);
5724
- console.log(chalk9.green(`XMTP group ID cached for ${parts[0]}`));
5725
- console.log(chalk9.dim(` Group ID: ${parts[1]}`));
6589
+ console.log(chalk13.green(`XMTP group ID cached for ${parts[0]}`));
6590
+ console.log(chalk13.dim(` Group ID: ${parts[1]}`));
5726
6591
  saved = true;
5727
6592
  }
5728
6593
  if (!saved) {
5729
- console.log(chalk9.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --xmtp-group"));
6594
+ console.log(chalk13.red("Provide at least one of: --private-key, --vault, --rpc, --notify-to, --uniswap-api-key, --venice-api-key, --anthropic-api-key, --xmtp-group"));
5730
6595
  process.exit(1);
5731
6596
  }
5732
6597
  });
@@ -5737,25 +6602,25 @@ configCmd.command("show").description("Display current config for the active net
5737
6602
  const config = loadConfig();
5738
6603
  const customRpc = config.rpc?.[network];
5739
6604
  console.log();
5740
- console.log(chalk9.bold(`Sherwood Config`));
5741
- console.log(chalk9.dim("\u2500".repeat(50)));
5742
- console.log(` Network: ${chalk9.cyan(network)} (chain ${chainId})`);
5743
- console.log(` RPC: ${customRpc ? chalk9.green(customRpc) : chalk9.dim("default")}`);
5744
- console.log(` Wallet: ${config.privateKey ? chalk9.green("configured") : chalk9.dim("not set")}`);
5745
- console.log(` Agent ID: ${config.agentId ?? chalk9.dim("not set")}`);
5746
- console.log(` Vault: ${contracts.vault ?? chalk9.dim("not set")}`);
5747
- console.log(` Uniswap: ${getUniswapApiKey() ? chalk9.green("API key configured") : chalk9.dim("not set")}`);
5748
- console.log(` Venice: ${getVeniceApiKey() ? chalk9.green("API key configured") : chalk9.dim("not set")}`);
6605
+ console.log(chalk13.bold(`Sherwood Config`));
6606
+ console.log(chalk13.dim("\u2500".repeat(50)));
6607
+ console.log(` Network: ${chalk13.cyan(network)} (chain ${chainId})`);
6608
+ console.log(` RPC: ${customRpc ? chalk13.green(customRpc) : chalk13.dim("default")}`);
6609
+ console.log(` Wallet: ${config.privateKey ? chalk13.green("configured") : chalk13.dim("not set")}`);
6610
+ console.log(` Agent ID: ${config.agentId ?? chalk13.dim("not set")}`);
6611
+ console.log(` Vault: ${contracts.vault ?? chalk13.dim("not set")}`);
6612
+ console.log(` Uniswap: ${getUniswapApiKey() ? chalk13.green("API key configured") : chalk13.dim("not set")}`);
6613
+ console.log(` Venice: ${getVeniceApiKey() ? chalk13.green("API key configured") : chalk13.dim("not set")}`);
5749
6614
  const groupEntries = Object.entries(config.groupCache || {}).filter(([, v]) => v);
5750
6615
  if (groupEntries.length > 0) {
5751
6616
  console.log();
5752
- console.log(chalk9.bold(" XMTP Groups"));
6617
+ console.log(chalk13.bold(" XMTP Groups"));
5753
6618
  for (const [sub, gid] of groupEntries) {
5754
- console.log(` ${sub}: ${chalk9.cyan(gid)}`);
6619
+ console.log(` ${sub}: ${chalk13.cyan(gid)}`);
5755
6620
  }
5756
6621
  }
5757
6622
  console.log();
5758
- console.log(chalk9.dim(" Config file: ~/.sherwood/config.json"));
6623
+ console.log(chalk13.dim(" Config file: ~/.sherwood/config.json"));
5759
6624
  console.log();
5760
6625
  });
5761
6626
  program.parse();