naracli 1.0.56 → 1.0.58

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/bin/nara-cli.ts CHANGED
@@ -15,13 +15,13 @@ const program = new Command();
15
15
  // Set program metadata
16
16
  program
17
17
  .name("naracli")
18
- .description("CLI for the Nara chain (Solana-compatible)")
18
+ .description("CLI for the Nara chain. Native coin is NARA (not SOL). Mine NARA for free via PoMI quests, manage wallets, register agents, and more. Run 'naracli <command> --help' for details on any command.")
19
19
  .version(version);
20
20
 
21
21
  // Add global options
22
22
  program
23
- .option("-r, --rpc-url <url>", "RPC endpoint URL")
24
- .option("-w, --wallet <path>", "Path to wallet keypair JSON file")
23
+ .option("-r, --rpc-url <url>", "RPC endpoint (default: https://mainnet-api.nara.build/)")
24
+ .option("-w, --wallet <path>", "Path to wallet keypair JSON file (default: ~/.config/nara/id.json)")
25
25
  .option("-j, --json", "Output in JSON format");
26
26
 
27
27
  // Register all command modules
@@ -126972,6 +126972,29 @@ async function getStakeInfo(connection, user, options) {
126972
126972
  return null;
126973
126973
  }
126974
126974
  }
126975
+ async function getQuestConfig(connection, options) {
126976
+ const kp = import_web390.Keypair.generate();
126977
+ const program3 = createProgram2(connection, kp, options?.programId);
126978
+ const programId = new import_web390.PublicKey(options?.programId ?? DEFAULT_QUEST_PROGRAM_ID);
126979
+ const [configPda] = import_web390.PublicKey.findProgramAddressSync(
126980
+ [new TextEncoder().encode("quest_config")],
126981
+ programId
126982
+ );
126983
+ const config = await program3.account.gameConfig.fetch(configPda);
126984
+ return {
126985
+ authority: config.authority,
126986
+ minRewardCount: config.minRewardCount,
126987
+ maxRewardCount: config.maxRewardCount,
126988
+ stakeBpsHigh: Number(config.stakeBpsHigh.toString()),
126989
+ stakeBpsLow: Number(config.stakeBpsLow.toString()),
126990
+ decayMs: Number(config.decayMs.toString()),
126991
+ treasury: config.treasury,
126992
+ questAuthority: config.questAuthority,
126993
+ minQuestInterval: Number(config.minQuestInterval.toString()),
126994
+ rewardPerShare: Number(config.rewardPerShare.toString()),
126995
+ extraReward: Number(config.extraReward.toString())
126996
+ };
126997
+ }
126975
126998
 
126976
126999
  // node_modules/.pnpm/nara-sdk@1.0.54_bufferutil@4.1.0_fastestsmallesttextencoderdecoder@1.0.22_typescript@5.9.3_utf-8-validate@6.0.6/node_modules/nara-sdk/src/skills.ts
126977
127000
  var import_web391 = __toESM(require_index_cjs(), 1);
@@ -130513,8 +130536,8 @@ ${result.base64}`);
130513
130536
  var _DEFAULT_WALLET_PATH = process.env.WALLET_PATH || "~/.config/nara/id.json";
130514
130537
  var DEFAULT_WALLET_PATH2 = _DEFAULT_WALLET_PATH.startsWith("~") ? (0, import_node_path3.join)((0, import_node_os3.homedir)(), _DEFAULT_WALLET_PATH.slice(1)) : _DEFAULT_WALLET_PATH;
130515
130538
  function registerWalletCommands(program3) {
130516
- const wallet = program3.command("wallet").description("Wallet management commands");
130517
- wallet.command("create").description("Create a new wallet").option("-o, --output <path>", "Output path for wallet file (default: ~/.config/nara/id.json)").action(async (options) => {
130539
+ const wallet = program3.command("wallet").description("Wallet management \u2014 create or import a keypair for signing transactions");
130540
+ wallet.command("create").description("Create a new wallet keypair (saved to ~/.config/nara/id.json by default)").option("-o, --output <path>", "Output path for wallet file (default: ~/.config/nara/id.json)").action(async (options) => {
130518
130541
  try {
130519
130542
  await handleWalletCreate(options);
130520
130543
  } catch (error) {
@@ -130963,6 +130986,14 @@ async function handleQuestGet(options) {
130963
130986
  }
130964
130987
  return;
130965
130988
  }
130989
+ let stakeRequired = quest.effectiveStakeRequirement > 0;
130990
+ try {
130991
+ const config = await getQuestConfig(connection);
130992
+ if (quest.rewardCount < config.maxRewardCount) {
130993
+ stakeRequired = false;
130994
+ }
130995
+ } catch {
130996
+ }
130966
130997
  const data = {
130967
130998
  round: quest.round,
130968
130999
  question: quest.question,
@@ -130974,7 +131005,8 @@ async function handleQuestGet(options) {
130974
131005
  deadline: new Date(quest.deadline * 1e3).toLocaleString(),
130975
131006
  timeRemaining: formatTimeRemaining(quest.timeRemaining),
130976
131007
  expired: quest.expired,
130977
- stakeRequirement: `${quest.effectiveStakeRequirement.toFixed(4)} NARA`,
131008
+ stakeRequired,
131009
+ stakeRequirement: stakeRequired ? `${quest.effectiveStakeRequirement.toFixed(4)} NARA` : "0 NARA",
130978
131010
  stakeHigh: `${quest.stakeHigh} NARA`,
130979
131011
  stakeLow: `${quest.stakeLow} NARA`,
130980
131012
  avgParticipantStake: `${quest.avgParticipantStake} NARA`
@@ -130991,8 +131023,10 @@ async function handleQuestGet(options) {
130991
131023
  console.log(
130992
131024
  ` Reward slots: ${quest.winnerCount}/${quest.rewardCount} (${quest.remainingSlots} remaining)`
130993
131025
  );
130994
- if (quest.effectiveStakeRequirement > 0) {
131026
+ if (stakeRequired) {
130995
131027
  console.log(` Stake requirement: ${quest.effectiveStakeRequirement.toFixed(4)} NARA (decays ${quest.stakeHigh} \u2192 ${quest.stakeLow})`);
131028
+ } else {
131029
+ console.log(` Stake requirement: none`);
130996
131030
  }
130997
131031
  console.log(` Deadline: ${new Date(quest.deadline * 1e3).toLocaleString()}`);
130998
131032
  if (quest.timeRemaining > 0) {
@@ -131003,6 +131037,49 @@ async function handleQuestGet(options) {
131003
131037
  console.log("");
131004
131038
  }
131005
131039
  }
131040
+ async function handleQuestConfig(options) {
131041
+ const rpcUrl = getRpcUrl(options.rpcUrl);
131042
+ const connection = new import_web398.Connection(rpcUrl, "confirmed");
131043
+ let config;
131044
+ try {
131045
+ config = await getQuestConfig(connection);
131046
+ } catch (err) {
131047
+ printError(`Failed to fetch quest config: ${err.message}`);
131048
+ process.exit(1);
131049
+ }
131050
+ const DECIMALS = 1e9;
131051
+ const rewardPerShare = config.rewardPerShare / DECIMALS;
131052
+ const extraReward = config.extraReward / DECIMALS;
131053
+ const stakeBpsHigh = config.stakeBpsHigh;
131054
+ const stakeBpsLow = config.stakeBpsLow;
131055
+ const data = {
131056
+ authority: config.authority.toBase58(),
131057
+ questAuthority: config.questAuthority.toBase58(),
131058
+ treasury: config.treasury.toBase58(),
131059
+ minRewardCount: config.minRewardCount,
131060
+ maxRewardCount: config.maxRewardCount,
131061
+ rewardPerShare,
131062
+ extraReward,
131063
+ stakeBpsHigh,
131064
+ stakeBpsLow,
131065
+ decayMs: config.decayMs,
131066
+ minQuestInterval: config.minQuestInterval
131067
+ };
131068
+ if (options.json) {
131069
+ formatOutput(data, true);
131070
+ } else {
131071
+ console.log("");
131072
+ console.log(` Min Reward Count: ${data.minRewardCount}`);
131073
+ console.log(` Max Reward Count: ${data.maxRewardCount}`);
131074
+ console.log(` Reward Per Share: ${rewardPerShare} NARA`);
131075
+ console.log(` Extra Reward: ${extraReward} NARA`);
131076
+ console.log(` Stake BPS High: ${stakeBpsHigh / 100}%`);
131077
+ console.log(` Stake BPS Low: ${stakeBpsLow / 100}%`);
131078
+ console.log(` Decay (ms): ${data.decayMs}`);
131079
+ console.log(` Min Quest Interval: ${data.minQuestInterval}s`);
131080
+ console.log("");
131081
+ }
131082
+ }
131006
131083
  async function handleQuestAnswer(answer, options) {
131007
131084
  const rpcUrl = getRpcUrl(options.rpcUrl);
131008
131085
  const connection = new import_web398.Connection(rpcUrl, "confirmed");
@@ -131208,8 +131285,8 @@ function handleSubmitError(err) {
131208
131285
  process.exit(1);
131209
131286
  }
131210
131287
  function registerQuestCommands(program3) {
131211
- const quest = program3.command("quest").description("Quest commands");
131212
- quest.command("get").description("Get current quest info").action(async (_opts, cmd) => {
131288
+ const quest = program3.command("quest").description("PoMI quest commands \u2014 mine NARA by answering on-chain quests with ZK proofs");
131289
+ quest.command("get").description("Get current quest info (question, deadline, difficulty, stake requirement)").action(async (_opts, cmd) => {
131213
131290
  try {
131214
131291
  const globalOpts = cmd.optsWithGlobals();
131215
131292
  await handleQuestGet(globalOpts);
@@ -131218,7 +131295,7 @@ function registerQuestCommands(program3) {
131218
131295
  process.exit(1);
131219
131296
  }
131220
131297
  });
131221
- quest.command("answer <answer>").description("Submit an answer").option("--relay [url]", `Submit via relay service, gasless (default: ${DEFAULT_QUEST_RELAY_URL2})`).option("--agent <name>", "Agent identifier (default: naracli)").option("--model <name>", "Model identifier").option("--referral <agent-id>", "Referral agent ID").option("--stake [amount]", 'Stake NARA before answering ("auto" to top-up to requirement, or a number)').action(async (answer, opts, cmd) => {
131298
+ quest.command("answer <answer>").description("Submit a quest answer with ZK proof. Generates a proof locally and submits on-chain. Use --relay when balance is 0 (gasless). Always pass --agent and --model for reward tracking.").option("--relay [url]", `Submit via gasless relay (default: ${DEFAULT_QUEST_RELAY_URL2}, backup: https://quest2-api.nara.build/)`).option("--agent <name>", "Agent/platform type: claude-code, cursor, chatgpt, openclaw, etc. (default: naracli)").option("--model <name>", "AI model used: claude-opus-4-6, claude-sonnet-4-6, gpt-4o, etc.").option("--referral <agent-id>", "Referral agent ID for earning referral points").option("--stake [amount]", 'Stake NARA in the same tx ("auto" to top-up to requirement, or an exact amount)').action(async (answer, opts, cmd) => {
131222
131299
  try {
131223
131300
  const globalOpts = cmd.optsWithGlobals();
131224
131301
  const relayUrl = opts.relay === true ? DEFAULT_QUEST_RELAY_URL2 : opts.relay;
@@ -131229,6 +131306,15 @@ function registerQuestCommands(program3) {
131229
131306
  process.exit(1);
131230
131307
  }
131231
131308
  });
131309
+ quest.command("config").description("Show quest program config (reward counts, stake params, decay, intervals)").action(async (_opts, cmd) => {
131310
+ try {
131311
+ const globalOpts = cmd.optsWithGlobals();
131312
+ await handleQuestConfig(globalOpts);
131313
+ } catch (error) {
131314
+ printError(error.message);
131315
+ process.exit(1);
131316
+ }
131317
+ });
131232
131318
  quest.command("stake <amount>").description("Stake NARA to participate in quests").action(async (amount, _opts, cmd) => {
131233
131319
  try {
131234
131320
  const globalOpts = cmd.optsWithGlobals();
@@ -133147,7 +133233,7 @@ async function handleSkillsDelete(name, options) {
133147
133233
  }
133148
133234
  }
133149
133235
  function registerSkillsCommands(program3) {
133150
- const skills = program3.command("skills").description("Skills hub commands");
133236
+ const skills = program3.command("skills").description("On-chain skill registry \u2014 register, publish, install, and manage AI agent skills");
133151
133237
  skills.command("register <name> <author>").description("Register a new skill on-chain").action(async (name, author, _opts, cmd) => {
133152
133238
  try {
133153
133239
  const globalOpts = cmd.optsWithGlobals();
@@ -133813,8 +133899,8 @@ async function handleAgentClear(options) {
133813
133899
  }
133814
133900
  }
133815
133901
  function registerAgentCommands(program3) {
133816
- const agent = program3.command("agent").description("Agent Registry commands (on-chain AI agents)");
133817
- agent.command("register <agent-id>").description("Register a new agent on-chain").option("--referral <agent-id>", "Referral agent ID").action(async (agentId, opts, cmd) => {
133902
+ const agent = program3.command("agent").description("Agent Registry \u2014 register an on-chain AI agent identity to earn extra rewards and points from PoMI mining");
133903
+ agent.command("register <agent-id>").description("Register a new agent on-chain (costs 1 NARA, 50% off with referral). Agent ID must be lowercase alphanumeric with hyphens.").option("--referral <agent-id>", "Referral agent ID \u2014 saves 50% on registration fee").action(async (agentId, opts, cmd) => {
133818
133904
  try {
133819
133905
  const globalOpts = cmd.optsWithGlobals();
133820
133906
  await handleAgentRegister(agentId, { ...globalOpts, ...opts });
@@ -134048,7 +134134,7 @@ function registerCommands(program3) {
134048
134134
  registerZkIdCommands(program3);
134049
134135
  registerAgentCommands(program3);
134050
134136
  registerConfigCommands(program3);
134051
- program3.command("address").description("Show wallet address").action(async () => {
134137
+ program3.command("address").description("Show wallet public address (run this first to check if a wallet exists)").action(async () => {
134052
134138
  const opts = program3.opts();
134053
134139
  try {
134054
134140
  await handleWalletAddress(opts);
@@ -134057,7 +134143,7 @@ function registerCommands(program3) {
134057
134143
  process.exit(1);
134058
134144
  }
134059
134145
  });
134060
- program3.command("balance").description("Check NARA balance").argument("[address]", "Wallet address (optional, defaults to current wallet)").action(async (address) => {
134146
+ program3.command("balance").description("Check NARA balance (native coin, not SOL)").argument("[address]", "Wallet address (optional, defaults to current wallet)").action(async (address) => {
134061
134147
  const opts = program3.opts();
134062
134148
  try {
134063
134149
  await handleWalletBalance(address, opts);
@@ -134066,7 +134152,7 @@ function registerCommands(program3) {
134066
134152
  process.exit(1);
134067
134153
  }
134068
134154
  });
134069
- program3.command("token-balance <token-address>").description("Check token balance").option("--owner <address>", "Owner address (optional, defaults to current wallet)").action(async (tokenAddress, options) => {
134155
+ program3.command("token-balance <token-address>").description("Check token balance (supports SPL Token and Token-2022)").option("--owner <address>", "Owner address (optional, defaults to current wallet)").action(async (tokenAddress, options) => {
134070
134156
  const opts = program3.opts();
134071
134157
  try {
134072
134158
  await handleTokenBalance(tokenAddress, { ...opts, ...options });
@@ -134123,7 +134209,7 @@ function registerCommands(program3) {
134123
134209
  process.exit(1);
134124
134210
  }
134125
134211
  });
134126
- program3.command("sign <base64-tx>").description("Sign a base64-encoded transaction").option("--send", "Send the signed transaction", false).action(async (base64Tx, options) => {
134212
+ program3.command("sign <base64-tx>").description("Sign a base64-encoded transaction (supports legacy and versioned transactions)").option("--send", "Sign and broadcast the transaction on-chain", false).action(async (base64Tx, options) => {
134127
134213
  const opts = program3.opts();
134128
134214
  try {
134129
134215
  const wallet = await loadWallet(opts.wallet);
@@ -134174,10 +134260,10 @@ function registerCommands(program3) {
134174
134260
  }
134175
134261
 
134176
134262
  // bin/nara-cli.ts
134177
- var version2 = true ? "1.0.56" : "dev";
134263
+ var version2 = true ? "1.0.58" : "dev";
134178
134264
  var program2 = new Command();
134179
- program2.name("naracli").description("CLI for the Nara chain (Solana-compatible)").version(version2);
134180
- program2.option("-r, --rpc-url <url>", "RPC endpoint URL").option("-w, --wallet <path>", "Path to wallet keypair JSON file").option("-j, --json", "Output in JSON format");
134265
+ program2.name("naracli").description("CLI for the Nara chain. Native coin is NARA (not SOL). Mine NARA for free via PoMI quests, manage wallets, register agents, and more. Run 'naracli <command> --help' for details on any command.").version(version2);
134266
+ program2.option("-r, --rpc-url <url>", "RPC endpoint (default: https://mainnet-api.nara.build/)").option("-w, --wallet <path>", "Path to wallet keypair JSON file (default: ~/.config/nara/id.json)").option("-j, --json", "Output in JSON format");
134181
134267
  registerCommands(program2);
134182
134268
  if (!process.argv.slice(2).length) {
134183
134269
  program2.outputHelp();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naracli",
3
- "version": "1.0.56",
3
+ "version": "1.0.58",
4
4
  "description": "CLI for the Nara chain (Solana-compatible)",
5
5
  "homepage": "https://nara.build",
6
6
  "repository": {
@@ -304,13 +304,13 @@ async function handleAgentClear(options: GlobalOptions) {
304
304
  export function registerAgentCommands(program: Command): void {
305
305
  const agent = program
306
306
  .command("agent")
307
- .description("Agent Registry commands (on-chain AI agents)");
307
+ .description("Agent Registry register an on-chain AI agent identity to earn extra rewards and points from PoMI mining");
308
308
 
309
309
  // agent register
310
310
  agent
311
311
  .command("register <agent-id>")
312
- .description("Register a new agent on-chain")
313
- .option("--referral <agent-id>", "Referral agent ID")
312
+ .description("Register a new agent on-chain (costs 1 NARA, 50% off with referral). Agent ID must be lowercase alphanumeric with hyphens.")
313
+ .option("--referral <agent-id>", "Referral agent ID — saves 50% on registration fee")
314
314
  .action(async (agentId: string, opts: { referral?: string }, cmd: Command) => {
315
315
  try {
316
316
  const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
@@ -16,6 +16,7 @@ import {
16
16
  import type { GlobalOptions } from "../types";
17
17
  import {
18
18
  getQuestInfo,
19
+ getQuestConfig,
19
20
  hasAnswered,
20
21
  generateProof,
21
22
  submitAnswer,
@@ -106,6 +107,17 @@ async function handleQuestGet(options: GlobalOptions) {
106
107
  return;
107
108
  }
108
109
 
110
+ // Stake only applies when reward slots have reached maxRewardCount
111
+ let stakeRequired = quest.effectiveStakeRequirement > 0;
112
+ try {
113
+ const config = await getQuestConfig(connection);
114
+ if (quest.rewardCount < config.maxRewardCount) {
115
+ stakeRequired = false;
116
+ }
117
+ } catch {
118
+ // If config fetch fails, fall back to showing stake as-is
119
+ }
120
+
109
121
  const data: Record<string, any> = {
110
122
  round: quest.round,
111
123
  question: quest.question,
@@ -117,7 +129,8 @@ async function handleQuestGet(options: GlobalOptions) {
117
129
  deadline: new Date(quest.deadline * 1000).toLocaleString(),
118
130
  timeRemaining: formatTimeRemaining(quest.timeRemaining),
119
131
  expired: quest.expired,
120
- stakeRequirement: `${quest.effectiveStakeRequirement.toFixed(4)} NARA`,
132
+ stakeRequired,
133
+ stakeRequirement: stakeRequired ? `${quest.effectiveStakeRequirement.toFixed(4)} NARA` : "0 NARA",
121
134
  stakeHigh: `${quest.stakeHigh} NARA`,
122
135
  stakeLow: `${quest.stakeLow} NARA`,
123
136
  avgParticipantStake: `${quest.avgParticipantStake} NARA`,
@@ -135,8 +148,10 @@ async function handleQuestGet(options: GlobalOptions) {
135
148
  console.log(
136
149
  ` Reward slots: ${quest.winnerCount}/${quest.rewardCount} (${quest.remainingSlots} remaining)`
137
150
  );
138
- if (quest.effectiveStakeRequirement > 0) {
151
+ if (stakeRequired) {
139
152
  console.log(` Stake requirement: ${quest.effectiveStakeRequirement.toFixed(4)} NARA (decays ${quest.stakeHigh} → ${quest.stakeLow})`);
153
+ } else {
154
+ console.log(` Stake requirement: none`);
140
155
  }
141
156
  console.log(` Deadline: ${new Date(quest.deadline * 1000).toLocaleString()}`);
142
157
  if (quest.timeRemaining > 0) {
@@ -148,6 +163,55 @@ async function handleQuestGet(options: GlobalOptions) {
148
163
  }
149
164
  }
150
165
 
166
+ // ─── Command: quest config ───────────────────────────────────────
167
+ async function handleQuestConfig(options: GlobalOptions) {
168
+ const rpcUrl = getRpcUrl(options.rpcUrl);
169
+ const connection = new Connection(rpcUrl, "confirmed");
170
+
171
+ let config;
172
+ try {
173
+ config = await getQuestConfig(connection);
174
+ } catch (err: any) {
175
+ printError(`Failed to fetch quest config: ${err.message}`);
176
+ process.exit(1);
177
+ }
178
+
179
+ const DECIMALS = 1e9;
180
+ const rewardPerShare = config.rewardPerShare / DECIMALS;
181
+ const extraReward = config.extraReward / DECIMALS;
182
+ const stakeBpsHigh = config.stakeBpsHigh;
183
+ const stakeBpsLow = config.stakeBpsLow;
184
+
185
+ const data = {
186
+ authority: config.authority.toBase58(),
187
+ questAuthority: config.questAuthority.toBase58(),
188
+ treasury: config.treasury.toBase58(),
189
+ minRewardCount: config.minRewardCount,
190
+ maxRewardCount: config.maxRewardCount,
191
+ rewardPerShare,
192
+ extraReward,
193
+ stakeBpsHigh,
194
+ stakeBpsLow,
195
+ decayMs: config.decayMs,
196
+ minQuestInterval: config.minQuestInterval,
197
+ };
198
+
199
+ if (options.json) {
200
+ formatOutput(data, true);
201
+ } else {
202
+ console.log("");
203
+ console.log(` Min Reward Count: ${data.minRewardCount}`);
204
+ console.log(` Max Reward Count: ${data.maxRewardCount}`);
205
+ console.log(` Reward Per Share: ${rewardPerShare} NARA`);
206
+ console.log(` Extra Reward: ${extraReward} NARA`);
207
+ console.log(` Stake BPS High: ${stakeBpsHigh / 100}%`);
208
+ console.log(` Stake BPS Low: ${stakeBpsLow / 100}%`);
209
+ console.log(` Decay (ms): ${data.decayMs}`);
210
+ console.log(` Min Quest Interval: ${data.minQuestInterval}s`);
211
+ console.log("");
212
+ }
213
+ }
214
+
151
215
  // ─── Command: quest answer ───────────────────────────────────────
152
216
  async function handleQuestAnswer(
153
217
  answer: string,
@@ -400,12 +464,12 @@ function handleSubmitError(err: any) {
400
464
  export function registerQuestCommands(program: Command): void {
401
465
  const quest = program
402
466
  .command("quest")
403
- .description("Quest commands");
467
+ .description("PoMI quest commands — mine NARA by answering on-chain quests with ZK proofs");
404
468
 
405
469
  // quest get
406
470
  quest
407
471
  .command("get")
408
- .description("Get current quest info")
472
+ .description("Get current quest info (question, deadline, difficulty, stake requirement)")
409
473
  .action(async (_opts: any, cmd: Command) => {
410
474
  try {
411
475
  const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
@@ -419,12 +483,12 @@ export function registerQuestCommands(program: Command): void {
419
483
  // quest answer
420
484
  quest
421
485
  .command("answer <answer>")
422
- .description("Submit an answer")
423
- .option("--relay [url]", `Submit via relay service, gasless (default: ${DEFAULT_QUEST_RELAY_URL})`)
424
- .option("--agent <name>", "Agent identifier (default: naracli)")
425
- .option("--model <name>", "Model identifier")
426
- .option("--referral <agent-id>", "Referral agent ID")
427
- .option("--stake [amount]", 'Stake NARA before answering ("auto" to top-up to requirement, or a number)')
486
+ .description("Submit a quest answer with ZK proof. Generates a proof locally and submits on-chain. Use --relay when balance is 0 (gasless). Always pass --agent and --model for reward tracking.")
487
+ .option("--relay [url]", `Submit via gasless relay (default: ${DEFAULT_QUEST_RELAY_URL}, backup: https://quest2-api.nara.build/)`)
488
+ .option("--agent <name>", "Agent/platform type: claude-code, cursor, chatgpt, openclaw, etc. (default: naracli)")
489
+ .option("--model <name>", "AI model used: claude-opus-4-6, claude-sonnet-4-6, gpt-4o, etc.")
490
+ .option("--referral <agent-id>", "Referral agent ID for earning referral points")
491
+ .option("--stake [amount]", 'Stake NARA in the same tx ("auto" to top-up to requirement, or an exact amount)')
428
492
  .action(async (answer: string, opts: any, cmd: Command) => {
429
493
  try {
430
494
  const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
@@ -437,6 +501,20 @@ export function registerQuestCommands(program: Command): void {
437
501
  }
438
502
  });
439
503
 
504
+ // quest config
505
+ quest
506
+ .command("config")
507
+ .description("Show quest program config (reward counts, stake params, decay, intervals)")
508
+ .action(async (_opts: any, cmd: Command) => {
509
+ try {
510
+ const globalOpts = cmd.optsWithGlobals() as GlobalOptions;
511
+ await handleQuestConfig(globalOpts);
512
+ } catch (error: any) {
513
+ printError(error.message);
514
+ process.exit(1);
515
+ }
516
+ });
517
+
440
518
  // quest stake
441
519
  quest
442
520
  .command("stake <amount>")
@@ -281,7 +281,7 @@ async function handleSkillsDelete(name: string, options: GlobalOptions & { yes?:
281
281
  export function registerSkillsCommands(program: Command): void {
282
282
  const skills = program
283
283
  .command("skills")
284
- .description("Skills hub commands");
284
+ .description("On-chain skill registry — register, publish, install, and manage AI agent skills");
285
285
 
286
286
  // skills register
287
287
  skills
@@ -55,12 +55,12 @@ const DEFAULT_WALLET_PATH = _DEFAULT_WALLET_PATH.startsWith("~")
55
55
  export function registerWalletCommands(program: Command): void {
56
56
  const wallet = program
57
57
  .command("wallet")
58
- .description("Wallet management commands");
58
+ .description("Wallet management — create or import a keypair for signing transactions");
59
59
 
60
60
  // wallet create
61
61
  wallet
62
62
  .command("create")
63
- .description("Create a new wallet")
63
+ .description("Create a new wallet keypair (saved to ~/.config/nara/id.json by default)")
64
64
  .option("-o, --output <path>", "Output path for wallet file (default: ~/.config/nara/id.json)")
65
65
  .action(async (options: { output?: string }) => {
66
66
  try {
package/src/cli/index.ts CHANGED
@@ -85,7 +85,7 @@ export function registerCommands(program: Command): void {
85
85
  // Top-level: address
86
86
  program
87
87
  .command("address")
88
- .description("Show wallet address")
88
+ .description("Show wallet public address (run this first to check if a wallet exists)")
89
89
  .action(async () => {
90
90
  const opts = program.opts() as GlobalOptions;
91
91
  try {
@@ -99,7 +99,7 @@ export function registerCommands(program: Command): void {
99
99
  // Top-level: balance
100
100
  program
101
101
  .command("balance")
102
- .description("Check NARA balance")
102
+ .description("Check NARA balance (native coin, not SOL)")
103
103
  .argument("[address]", "Wallet address (optional, defaults to current wallet)")
104
104
  .action(async (address: string | undefined) => {
105
105
  const opts = program.opts() as WalletBalanceOptions;
@@ -114,7 +114,7 @@ export function registerCommands(program: Command): void {
114
114
  // Top-level: token-balance
115
115
  program
116
116
  .command("token-balance <token-address>")
117
- .description("Check token balance")
117
+ .description("Check token balance (supports SPL Token and Token-2022)")
118
118
  .option("--owner <address>", "Owner address (optional, defaults to current wallet)")
119
119
  .action(async (tokenAddress: string, options: { owner?: string }) => {
120
120
  const opts = program.opts() as TokenBalanceOptions;
@@ -199,8 +199,8 @@ export function registerCommands(program: Command): void {
199
199
  // Top-level: sign
200
200
  program
201
201
  .command("sign <base64-tx>")
202
- .description("Sign a base64-encoded transaction")
203
- .option("--send", "Send the signed transaction", false)
202
+ .description("Sign a base64-encoded transaction (supports legacy and versioned transactions)")
203
+ .option("--send", "Sign and broadcast the transaction on-chain", false)
204
204
  .action(async (base64Tx: string, options: { send?: boolean }) => {
205
205
  const opts = program.opts() as GlobalOptions;
206
206
  try {