aavegotchi-cli 0.2.0 → 0.2.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,48 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 0.2.2 - 2026-02-27
6
+
7
+ ### Added
8
+
9
+ - Command-targeted help:
10
+ - `ag --help`
11
+ - `ag <command> --help`
12
+ - `ag help <command>`
13
+ - Mapped-write help now includes mapped function name, required flags, and dry-run pattern.
14
+ - ABI-derived signature/input introspection for mapped help when `--abi-file` is passed.
15
+ - Unknown command suggestion list in `UNKNOWN_COMMAND` error details.
16
+ - CLI UX audit report: `docs/ux/cli-ux-audit-2026-02-27.md`.
17
+ - Native `bankr` signer backend:
18
+ - signer spec: `bankr[:address|apiKeyEnv|apiUrl]`
19
+ - default auth env var: `BANKR_API_KEY`
20
+ - default API URL: `https://api.bankr.bot`
21
+ - address auto-resolution via `GET /agent/me` when address is not pinned
22
+ - transaction submit via `POST /agent/submit`
23
+ - Bootstrap overrides now support `bankr`:
24
+ - `--signer-address` to pin wallet address
25
+ - `--signer-auth-env-var` to customize Bankr API key env var
26
+
27
+ ### Changed
28
+
29
+ - Stub namespace errors now include mapped command options for the requested root.
30
+
31
+ ## 0.2.1 - 2026-02-27
32
+
33
+ ### Added
34
+
35
+ - `--dry-run` mode for write surfaces:
36
+ - `ag tx send --dry-run`
37
+ - `ag onchain send --dry-run`
38
+ - mapped writes (for example `ag token approve --dry-run`)
39
+ - `scripts/smoke-write-dryrun.sh` and npm script `smoke:write-dryrun` for automated write-path smoke checks without broadcasting.
40
+
41
+ ### Changed
42
+
43
+ - Dry-run execution now returns `status: "simulated"` with simulation details and skips journal mutation and transaction submission.
44
+ - `--dry-run` is explicitly blocked with `--wait` / `--confirm`.
45
+
3
46
  ## 0.2.0 - 2026-02-27
4
47
 
5
48
  ### Added
package/README.md CHANGED
@@ -55,6 +55,54 @@ Planned domain namespaces are stubbed for parity tracking:
55
55
  Many Base-era write flows are already executable as mapped aliases in those namespaces (internally routed through `onchain send`).
56
56
  Example: `ag lending create --abi-file ./abis/GotchiLendingFacet.json --address 0x... --args-json '[...]' --json`
57
57
 
58
+ ## Command help and discoverability
59
+
60
+ The CLI supports command-targeted help:
61
+
62
+ ```bash
63
+ ag --help
64
+ ag tx send --help
65
+ ag help baazaar buy-now
66
+ ```
67
+
68
+ Mapped write commands now expose their onchain function mapping and required flags:
69
+
70
+ ```bash
71
+ ag baazaar buy-now --help
72
+ ```
73
+
74
+ If you provide `--abi-file` with `--help`, the CLI prints ABI-derived function signature and input names for the mapped method:
75
+
76
+ ```bash
77
+ ag baazaar buy-now --help --abi-file ./abis/BaazaarFacet.json
78
+ ```
79
+
80
+ Unknown commands return suggestions:
81
+
82
+ ```bash
83
+ ag tx snd --json
84
+ ```
85
+
86
+ ## Dry-run writes
87
+
88
+ Use `--dry-run` on write commands to run full preflight without broadcasting:
89
+
90
+ - runs simulation (`eth_call`)
91
+ - runs gas + fee estimation
92
+ - enforces policy checks
93
+ - resolves nonce
94
+ - returns `status: \"simulated\"` with simulation details
95
+
96
+ Supported write surfaces:
97
+
98
+ - `tx send --dry-run`
99
+ - `onchain send --dry-run`
100
+ - mapped write aliases (for example: `token approve --dry-run`)
101
+
102
+ Safety rule:
103
+
104
+ - `--dry-run` cannot be combined with `--wait` / `--confirm`
105
+
58
106
  ## Subgraph sources and endpoint policy
59
107
 
60
108
  Canonical source aliases:
@@ -127,12 +175,26 @@ npm run ag -- auction active --first 5 --raw --json
127
175
  - `keychain:ACCOUNT_ID` (encrypted local key store; requires `AGCLI_KEYCHAIN_PASSPHRASE`)
128
176
  - `remote:URL|ADDRESS|AUTH_ENV` (HTTP signer service)
129
177
  - `ledger:DERIVATION_PATH|ADDRESS|BRIDGE_ENV` (external bridge command signer)
178
+ - `bankr[:ADDRESS|API_KEY_ENV|API_URL]` (Bankr-native signer via `/agent/me` + `/agent/submit`; defaults: `BANKR_API_KEY`, `https://api.bankr.bot`)
130
179
 
131
180
  Remote signer contract:
132
181
 
133
182
  - `GET /address` -> `{ "address": "0x..." }` (optional if address configured)
134
183
  - `POST /sign-transaction` -> `{ "rawTransaction": "0x..." }` or `{ "txHash": "0x..." }`
135
184
 
185
+ Bankr signer contract:
186
+
187
+ - `GET /agent/me` -> resolves wallet address when signer address is not pinned
188
+ - `POST /agent/submit` -> submits transaction and returns transaction hash
189
+ - auth header: `x-api-key: <BANKR_API_KEY>`
190
+
191
+ Bankr bootstrap example:
192
+
193
+ ```bash
194
+ BANKR_API_KEY=... \
195
+ npm run ag -- bootstrap --mode agent --profile bankr --chain base --signer bankr --json
196
+ ```
197
+
136
198
  Ledger bridge contract:
137
199
 
138
200
  - Set `AGCLI_LEDGER_BRIDGE_CMD` (or custom env var in signer config) to a command that reads tx payload JSON from stdin and outputs JSON containing either `rawTransaction` or `txHash`.
@@ -186,5 +248,12 @@ npm run typecheck
186
248
  npm test
187
249
  npm run build
188
250
  npm run parity:check
251
+ npm run smoke:write-dryrun
189
252
  npm run ag -- help
190
253
  ```
254
+
255
+ Write dry-run smoke test notes:
256
+
257
+ - `npm run smoke:write-dryrun` validates write paths without broadcasting any transaction.
258
+ - To run against an installed binary instead of local source:
259
+ - `AG_BIN=/absolute/path/to/ag npm run smoke:write-dryrun`
package/dist/abi.js ADDED
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseAbiFile = parseAbiFile;
37
+ exports.getAbiFunctionEntries = getAbiFunctionEntries;
38
+ exports.formatAbiFunctionSignature = formatAbiFunctionSignature;
39
+ exports.formatAbiFunctionInputs = formatAbiFunctionInputs;
40
+ const fs = __importStar(require("fs"));
41
+ const errors_1 = require("./errors");
42
+ function normalizeTypeName(item) {
43
+ if (!item.type) {
44
+ return "unknown";
45
+ }
46
+ if (!item.type.includes("tuple")) {
47
+ return item.type;
48
+ }
49
+ const components = item.components || [];
50
+ const componentTypes = components.map((component) => component.type).join(",");
51
+ return item.type.replace("tuple", `tuple(${componentTypes})`);
52
+ }
53
+ function parseAbiFile(filePath) {
54
+ if (!fs.existsSync(filePath)) {
55
+ throw new errors_1.CliError("ABI_NOT_FOUND", `ABI file not found: ${filePath}`, 2);
56
+ }
57
+ let parsed;
58
+ try {
59
+ parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
60
+ }
61
+ catch {
62
+ throw new errors_1.CliError("INVALID_ABI", `ABI file is not valid JSON: ${filePath}`, 2);
63
+ }
64
+ if (Array.isArray(parsed)) {
65
+ return parsed;
66
+ }
67
+ if (typeof parsed === "object" && parsed !== null && "abi" in parsed && Array.isArray(parsed.abi)) {
68
+ return parsed.abi;
69
+ }
70
+ throw new errors_1.CliError("INVALID_ABI", "ABI file must be an array or object containing 'abi'.", 2);
71
+ }
72
+ function getAbiFunctionEntries(abi, functionName) {
73
+ return abi.filter((item) => item.type === "function" && typeof item.name === "string" && item.name === functionName);
74
+ }
75
+ function formatAbiFunctionSignature(item) {
76
+ const inputTypes = (item.inputs || []).map((input) => normalizeTypeName(input));
77
+ return `${item.name}(${inputTypes.join(",")})`;
78
+ }
79
+ function formatAbiFunctionInputs(item) {
80
+ const inputs = item.inputs || [];
81
+ if (inputs.length === 0) {
82
+ return ["(none)"];
83
+ }
84
+ return inputs.map((input, index) => {
85
+ const label = input.name ? input.name : `arg${index}`;
86
+ return `${index}: ${label} (${normalizeTypeName(input)})`;
87
+ });
88
+ }
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listKnownCommands = listKnownCommands;
4
+ exports.suggestCommands = suggestCommands;
5
+ const mapped_1 = require("./commands/mapped");
6
+ const stubs_1 = require("./commands/stubs");
7
+ const BUILTIN_COMMANDS = [
8
+ "help",
9
+ "bootstrap",
10
+ "profile list",
11
+ "profile show",
12
+ "profile use",
13
+ "profile export",
14
+ "signer check",
15
+ "signer keychain list",
16
+ "signer keychain import",
17
+ "signer keychain remove",
18
+ "policy list",
19
+ "policy show",
20
+ "policy upsert",
21
+ "rpc check",
22
+ "tx send",
23
+ "tx status",
24
+ "tx resume",
25
+ "tx watch",
26
+ "batch run",
27
+ "onchain call",
28
+ "onchain send",
29
+ "subgraph list",
30
+ "subgraph check",
31
+ "subgraph query",
32
+ "baazaar listing get",
33
+ "baazaar listing active",
34
+ "baazaar listing mine",
35
+ "auction get",
36
+ "auction active",
37
+ "auction mine",
38
+ "auction bids",
39
+ "auction bids-mine",
40
+ ];
41
+ function listDomainReadCommands() {
42
+ return (0, stubs_1.listDomainStubRoots)().map((root) => `${root} read`);
43
+ }
44
+ function levenshteinDistance(a, b) {
45
+ if (a === b) {
46
+ return 0;
47
+ }
48
+ if (a.length === 0) {
49
+ return b.length;
50
+ }
51
+ if (b.length === 0) {
52
+ return a.length;
53
+ }
54
+ const previous = new Array(b.length + 1).fill(0).map((_, index) => index);
55
+ const current = new Array(b.length + 1).fill(0);
56
+ for (let i = 1; i <= a.length; i++) {
57
+ current[0] = i;
58
+ for (let j = 1; j <= b.length; j++) {
59
+ const substitutionCost = a[i - 1] === b[j - 1] ? 0 : 1;
60
+ current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + substitutionCost);
61
+ }
62
+ for (let j = 0; j <= b.length; j++) {
63
+ previous[j] = current[j];
64
+ }
65
+ }
66
+ return previous[b.length];
67
+ }
68
+ function listKnownCommands() {
69
+ return [
70
+ ...BUILTIN_COMMANDS,
71
+ ...listDomainReadCommands(),
72
+ ...(0, mapped_1.listMappedCommands)(),
73
+ ];
74
+ }
75
+ function suggestCommands(input, max = 5) {
76
+ const query = input.trim().toLowerCase();
77
+ if (!query) {
78
+ return [];
79
+ }
80
+ const scored = listKnownCommands().map((command) => {
81
+ const normalized = command.toLowerCase();
82
+ const startsWith = normalized.startsWith(query);
83
+ const includes = normalized.includes(query);
84
+ const distance = levenshteinDistance(query, normalized);
85
+ let score = distance;
86
+ if (startsWith) {
87
+ score = 0;
88
+ }
89
+ else if (includes) {
90
+ score = Math.min(score, 1);
91
+ }
92
+ return { command, score };
93
+ });
94
+ scored.sort((a, b) => {
95
+ if (a.score !== b.score) {
96
+ return a.score - b.score;
97
+ }
98
+ return a.command.localeCompare(b.command);
99
+ });
100
+ const threshold = Math.max(2, Math.floor(query.length / 2) + 1);
101
+ const filtered = scored.filter((entry) => entry.score <= threshold);
102
+ const results = (filtered.length > 0 ? filtered : scored).slice(0, max).map((entry) => entry.command);
103
+ return [...new Set(results)];
104
+ }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeCommandPath = normalizeCommandPath;
4
4
  exports.executeCommand = executeCommand;
5
5
  const errors_1 = require("./errors");
6
+ const command_catalog_1 = require("./command-catalog");
6
7
  const batch_1 = require("./commands/batch");
7
8
  const bootstrap_1 = require("./commands/bootstrap");
8
9
  const mapped_1 = require("./commands/mapped");
@@ -17,9 +18,12 @@ const stubs_1 = require("./commands/stubs");
17
18
  const subgraph_1 = require("./commands/subgraph");
18
19
  const tx_1 = require("./commands/tx");
19
20
  function normalizeCommandPath(positionals) {
20
- if (positionals.length === 0 || positionals[0] === "help") {
21
+ if (positionals.length === 0) {
21
22
  return ["help"];
22
23
  }
24
+ if (positionals[0] === "help") {
25
+ return positionals;
26
+ }
23
27
  return positionals;
24
28
  }
25
29
  async function executeCommand(ctx) {
@@ -220,5 +224,11 @@ async function executeCommand(ctx) {
220
224
  data: await (0, stubs_1.runDomainStubCommand)(ctx),
221
225
  };
222
226
  }
223
- throw new errors_1.CliError("UNKNOWN_COMMAND", `Unknown command '${ctx.commandPath.join(" ")}'.`, 2);
227
+ const command = ctx.commandPath.join(" ");
228
+ const suggestions = (0, command_catalog_1.suggestCommands)(command);
229
+ throw new errors_1.CliError("UNKNOWN_COMMAND", `Unknown command '${command}'.`, 2, {
230
+ command,
231
+ suggestions,
232
+ hint: suggestions.length > 0 ? "Try one of the suggested commands with '--help'." : "Run 'ag help'.",
233
+ });
224
234
  }
@@ -26,6 +26,15 @@ function buildSignerConfig(ctx, signerInput) {
26
26
  ...(authEnvVar ? { authEnvVar } : {}),
27
27
  };
28
28
  }
29
+ if (signer.type === "bankr") {
30
+ const address = (0, args_1.getFlagString)(ctx.args.flags, "signer-address");
31
+ const apiKeyEnvVar = (0, args_1.getFlagString)(ctx.args.flags, "signer-auth-env-var");
32
+ return {
33
+ ...signer,
34
+ ...(address ? { address: address } : {}),
35
+ ...(apiKeyEnvVar ? { apiKeyEnvVar } : {}),
36
+ };
37
+ }
29
38
  if (signer.type === "ledger") {
30
39
  const address = (0, args_1.getFlagString)(ctx.args.flags, "signer-address");
31
40
  const bridgeCommandEnvVar = (0, args_1.getFlagString)(ctx.args.flags, "signer-bridge-env-var");
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findMappedFunction = findMappedFunction;
4
+ exports.listMappedCommands = listMappedCommands;
4
5
  exports.listMappedCommandsForRoot = listMappedCommandsForRoot;
6
+ exports.getMappedCommandEntries = getMappedCommandEntries;
5
7
  exports.runMappedDomainCommand = runMappedDomainCommand;
6
8
  const errors_1 = require("../errors");
7
9
  const onchain_1 = require("./onchain");
@@ -50,8 +52,14 @@ function findMappedFunction(commandPath) {
50
52
  const key = commandPath.join(" ");
51
53
  return MAPPED_WRITE_COMMANDS[key];
52
54
  }
55
+ function listMappedCommands() {
56
+ return Object.keys(MAPPED_WRITE_COMMANDS).sort((a, b) => a.localeCompare(b));
57
+ }
53
58
  function listMappedCommandsForRoot(root) {
54
- return Object.keys(MAPPED_WRITE_COMMANDS).filter((entry) => entry.startsWith(`${root} `));
59
+ return listMappedCommands().filter((entry) => entry.startsWith(`${root} `));
60
+ }
61
+ function getMappedCommandEntries() {
62
+ return { ...MAPPED_WRITE_COMMANDS };
55
63
  }
56
64
  async function runMappedDomainCommand(ctx) {
57
65
  const key = ctx.commandPath.join(" ");
@@ -1,44 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.runOnchainCallCommand = runOnchainCallCommand;
37
4
  exports.runOnchainSendWithFunction = runOnchainSendWithFunction;
38
5
  exports.runOnchainSendCommand = runOnchainSendCommand;
39
- const fs = __importStar(require("fs"));
40
6
  const viem_1 = require("viem");
41
7
  const args_1 = require("../args");
8
+ const abi_1 = require("../abi");
42
9
  const chains_1 = require("../chains");
43
10
  const config_1 = require("../config");
44
11
  const errors_1 = require("../errors");
@@ -66,25 +33,6 @@ function parseArgsJson(value) {
66
33
  }
67
34
  return parsed;
68
35
  }
69
- function parseAbiFile(filePath) {
70
- if (!fs.existsSync(filePath)) {
71
- throw new errors_1.CliError("ABI_NOT_FOUND", `ABI file not found: ${filePath}`, 2);
72
- }
73
- let parsed;
74
- try {
75
- parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
76
- }
77
- catch {
78
- throw new errors_1.CliError("INVALID_ABI", `ABI file is not valid JSON: ${filePath}`, 2);
79
- }
80
- if (Array.isArray(parsed)) {
81
- return parsed;
82
- }
83
- if (typeof parsed === "object" && parsed !== null && "abi" in parsed && Array.isArray(parsed.abi)) {
84
- return parsed.abi;
85
- }
86
- throw new errors_1.CliError("INVALID_ABI", `ABI file must be an array or object containing 'abi'.`, 2);
87
- }
88
36
  function parseValueWei(value) {
89
37
  if (!value) {
90
38
  return undefined;
@@ -123,7 +71,7 @@ async function runOnchainCallCommand(ctx) {
123
71
  const chain = (0, chains_1.resolveChain)(profile.chain);
124
72
  const rpcUrl = (0, chains_1.resolveRpcUrl)(chain, (0, args_1.getFlagString)(ctx.args.flags, "rpc-url") || profile.rpcUrl);
125
73
  const abiFile = requireFlag((0, args_1.getFlagString)(ctx.args.flags, "abi-file"), "--abi-file");
126
- const abi = parseAbiFile(abiFile);
74
+ const abi = (0, abi_1.parseAbiFile)(abiFile);
127
75
  const address = parseAddress((0, args_1.getFlagString)(ctx.args.flags, "address"), "--address");
128
76
  const functionName = requireFlag((0, args_1.getFlagString)(ctx.args.flags, "function"), "--function");
129
77
  const args = parseArgsJson((0, args_1.getFlagString)(ctx.args.flags, "args-json"));
@@ -162,7 +110,7 @@ async function runOnchainSendWithFunction(ctx, forcedFunctionName, commandOverri
162
110
  const chain = (0, chains_1.resolveChain)(profile.chain);
163
111
  const rpcUrl = (0, chains_1.resolveRpcUrl)(chain, (0, args_1.getFlagString)(ctx.args.flags, "rpc-url") || profile.rpcUrl);
164
112
  const abiFile = requireFlag((0, args_1.getFlagString)(ctx.args.flags, "abi-file"), "--abi-file");
165
- const abi = parseAbiFile(abiFile);
113
+ const abi = (0, abi_1.parseAbiFile)(abiFile);
166
114
  const address = parseAddress((0, args_1.getFlagString)(ctx.args.flags, "address"), "--address");
167
115
  const functionName = forcedFunctionName || requireFlag((0, args_1.getFlagString)(ctx.args.flags, "function"), "--function");
168
116
  const args = parseArgsJson((0, args_1.getFlagString)(ctx.args.flags, "args-json"));
@@ -184,6 +132,10 @@ async function runOnchainSendWithFunction(ctx, forcedFunctionName, commandOverri
184
132
  });
185
133
  }
186
134
  const waitForReceipt = (0, args_1.getFlagBoolean)(ctx.args.flags, "wait");
135
+ const dryRun = (0, args_1.getFlagBoolean)(ctx.args.flags, "dry-run");
136
+ if (dryRun && waitForReceipt) {
137
+ throw new errors_1.CliError("INVALID_ARGUMENT", "--dry-run cannot be combined with --wait.", 2);
138
+ }
187
139
  const intent = {
188
140
  idempotencyKey: (0, args_1.getFlagString)(ctx.args.flags, "idempotency-key"),
189
141
  profileName: profile.name,
@@ -197,6 +149,7 @@ async function runOnchainSendWithFunction(ctx, forcedFunctionName, commandOverri
197
149
  noncePolicy: noncePolicyRaw,
198
150
  nonce,
199
151
  waitForReceipt,
152
+ dryRun,
200
153
  timeoutMs: parseTimeoutMs((0, args_1.getFlagString)(ctx.args.flags, "timeout-ms")),
201
154
  command: commandOverride || `onchain send ${functionName}`,
202
155
  };
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isDomainStubRoot = isDomainStubRoot;
4
+ exports.listDomainStubRoots = listDomainStubRoots;
4
5
  exports.runDomainStubCommand = runDomainStubCommand;
5
6
  const errors_1 = require("../errors");
7
+ const mapped_1 = require("./mapped");
6
8
  const SUPPORTED_STUB_ROOTS = [
7
9
  "gotchi",
8
10
  "portal",
@@ -22,11 +24,17 @@ const SUPPORTED_STUB_ROOTS = [
22
24
  function isDomainStubRoot(root) {
23
25
  return SUPPORTED_STUB_ROOTS.includes(root);
24
26
  }
27
+ function listDomainStubRoots() {
28
+ return SUPPORTED_STUB_ROOTS;
29
+ }
25
30
  async function runDomainStubCommand(ctx) {
26
31
  const command = ctx.commandPath.join(" ");
32
+ const root = ctx.commandPath[0];
33
+ const availableMapped = (0, mapped_1.listMappedCommandsForRoot)(root);
27
34
  throw new errors_1.CliError("COMMAND_NOT_IMPLEMENTED", `Command '${command}' is planned but not implemented yet.`, 2, {
28
35
  command,
29
- hint: "If this is a mapped onchain write, pass full subcommand plus --abi-file/--address/--args-json.",
36
+ hint: "Run 'ag help <command>' for usage. Mapped writes require --abi-file/--address/--args-json.",
37
+ availableMapped,
30
38
  plannedRoots: SUPPORTED_STUB_ROOTS,
31
39
  });
32
40
  }
@@ -75,10 +75,14 @@ async function runTxSendCommand(ctx) {
75
75
  const nonceValue = (0, args_1.getFlagString)(ctx.args.flags, "nonce");
76
76
  const nonce = nonceValue ? parseNumberFlag(nonceValue, "--nonce", 0) : undefined;
77
77
  const waitForReceipt = (0, args_1.getFlagBoolean)(ctx.args.flags, "wait") || (0, args_1.getFlagBoolean)(ctx.args.flags, "confirm");
78
+ const dryRun = (0, args_1.getFlagBoolean)(ctx.args.flags, "dry-run");
78
79
  const timeoutMs = parseNumberFlag((0, args_1.getFlagString)(ctx.args.flags, "timeout-ms"), "--timeout-ms", 120000);
79
80
  if (noncePolicy === "manual" && nonce === undefined) {
80
81
  throw new errors_1.CliError("MISSING_NONCE", "--nonce is required when --nonce-policy=manual.", 2);
81
82
  }
83
+ if (dryRun && waitForReceipt) {
84
+ throw new errors_1.CliError("INVALID_ARGUMENT", "--dry-run cannot be combined with --wait/--confirm.", 2);
85
+ }
82
86
  const intent = {
83
87
  idempotencyKey,
84
88
  profileName: profile.name,
@@ -92,6 +96,7 @@ async function runTxSendCommand(ctx) {
92
96
  noncePolicy,
93
97
  nonce,
94
98
  waitForReceipt,
99
+ dryRun,
95
100
  timeoutMs,
96
101
  command: "tx send",
97
102
  };
package/dist/index.js CHANGED
@@ -11,8 +11,13 @@ async function run() {
11
11
  const globals = (0, args_1.normalizeGlobals)(args);
12
12
  (0, logger_1.initializeLogger)(globals);
13
13
  const commandPath = (0, command_runner_1.normalizeCommandPath)(args.positionals);
14
+ const helpRequested = (0, args_1.getFlagBoolean)(args.flags, "help", "h");
14
15
  if (commandPath[0] === "help") {
15
- (0, output_1.outputHelp)();
16
+ (0, output_1.outputHelp)(commandPath.slice(1), args.flags);
17
+ return;
18
+ }
19
+ if (helpRequested) {
20
+ (0, output_1.outputHelp)(commandPath, args.flags);
16
21
  return;
17
22
  }
18
23
  const ctx = {