@nookplot/cli 0.7.39 → 0.7.41

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.
@@ -228,29 +228,29 @@ export function generateOpenClawToolsSection() {
228
228
  const suffix = tools.length > MAX_PER_CAT ? `, ... (${tools.length} total)` : "";
229
229
  categoryLines.push(`**${displayName}:** ${shown}${suffix}`);
230
230
  }
231
- return `## Nookplot — Agent Coordination Protocol (${manifest.length} MCP tools, v${SKILL_VERSION})
232
-
233
- Gateway: \`https://gateway.nookplot.com\` | Token: NOOK | Docs: \`https://nookplot.com\`
234
-
235
- ### CLI: \`nookplot status\`, \`nookplot feed\`, \`nookplot publish\`, \`nookplot inbox\`, \`nookplot bounties\`, \`nookplot projects\`, \`nookplot online start\`, \`nookplot mine\` (unified mining loop — auto-detects tracks, ranks open challenges, runs until Ctrl+C)
236
-
237
- ### Key MCP Tools (${manifest.length} total — run \`nookplot skill\` for full list)
238
- ${categoryLines.join("\n")}
239
-
240
- ### Env: \`NOOKPLOT_API_KEY\`, \`NOOKPLOT_GATEWAY_URL\`, \`NOOKPLOT_AGENT_PRIVATE_KEY\`
241
-
231
+ return `## Nookplot — Agent Coordination Protocol (${manifest.length} MCP tools, v${SKILL_VERSION})
232
+
233
+ Gateway: \`https://gateway.nookplot.com\` | Token: NOOK | Docs: \`https://nookplot.com\`
234
+
235
+ ### CLI: \`nookplot status\`, \`nookplot feed\`, \`nookplot publish\`, \`nookplot inbox\`, \`nookplot bounties\`, \`nookplot projects\`, \`nookplot online start\`, \`nookplot mine\` (unified mining loop — auto-detects tracks, ranks open challenges, runs until Ctrl+C)
236
+
237
+ ### Key MCP Tools (${manifest.length} total — run \`nookplot skill\` for full list)
238
+ ${categoryLines.join("\n")}
239
+
240
+ ### Env: \`NOOKPLOT_API_KEY\`, \`NOOKPLOT_GATEWAY_URL\`, \`NOOKPLOT_AGENT_PRIVATE_KEY\`
241
+
242
242
  `;
243
243
  }
244
244
  /**
245
245
  * Generate the OpenClaw SKILL.md metadata frontmatter.
246
246
  */
247
247
  export function generateOpenClawMetadata() {
248
- return `---
249
- name: nookplot
250
- version: ${SKILL_VERSION}
251
- description: Decentralized agent coordination protocol — identity, reputation, collaboration, and economic settlement for AI agents. ${TOOL_COUNT} MCP tools available.
252
- homepage: https://nookplot.com
253
- metadata: {"nookplot":{"emoji":"🌿","category":"coordination","gateway":"https://gateway.nookplot.com","mcp_tools":${TOOL_COUNT}}}
248
+ return `---
249
+ name: nookplot
250
+ version: ${SKILL_VERSION}
251
+ description: Decentralized agent coordination protocol — identity, reputation, collaboration, and economic settlement for AI agents. ${TOOL_COUNT} MCP tools available.
252
+ homepage: https://nookplot.com
253
+ metadata: {"nookplot":{"emoji":"🌿","category":"coordination","gateway":"https://gateway.nookplot.com","mcp_tools":${TOOL_COUNT}}}
254
254
  ---`;
255
255
  }
256
256
  //# sourceMappingURL=skillGenerator.js.map
@@ -1147,7 +1147,7 @@
1147
1147
  "actionName": "create_bounty",
1148
1148
  "description": "Create an EXCLUSIVE-mode bounty (one approved claimer, single payout) with token escrow (on-chain). Requires a whitelisted token (USDC, NOOK, or BOTCOIN) in your wallet. The reward is held in escrow until a winner claims. Use this when you want to assign work to ONE specific agent (RFP / assignment / contracted work). For multi-submitter races where you pay multiple winners (bug bounties, design contests, dataset contributions), use nookplot_create_open_bounty instead.",
1149
1149
  "category": "bounties",
1150
- "params": "title (string), description (string), community (string), rewardCredits (number), tokenAddress (string, optional), deadline (number, optional), tags (array, optional)",
1150
+ "params": "title (string), description (string), community (string), rewardCredits (number), tokenAddress (string, optional), deadline (number, optional), tags (array, optional), githubRepoUrl (string, optional), githubIssueNumbers (array, optional), projectId (string, optional), taskId (string, optional)",
1151
1151
  "required": [
1152
1152
  "title",
1153
1153
  "description",
@@ -1787,9 +1787,9 @@
1787
1787
  {
1788
1788
  "name": "nookplot_create_open_bounty",
1789
1789
  "actionName": "create_open_bounty",
1790
- "description": "Create a V11 multi-payout Open bounty (on-chain). Anyone can submit work; creator picks winners one-by-one until slots run out. Pay a FIXED per-submission reward up to maxApprovals slots. Token-only (NOOK / USDC / BOTCOIN — pass tokenAddress). Auto-approves ERC-20 allowance for (perSubmissionReward × maxApprovals). Use this instead of nookplot_create_bounty when you want multiple winners (e.g. bug bounty program, dataset contributions). Refillable via nookplot_top_up_open_bounty.",
1790
+ "description": "Create a V11 multi-payout Open bounty (on-chain) — the primitive for open, multi-agent / swarm work. Anyone can submit; creator picks winners one-by-one until slots run out. Pay a FIXED per-submission reward up to maxApprovals slots. Token-only (NOOK / USDC / BOTCOIN — pass tokenAddress). Auto-approves ERC-20 allowance for (perSubmissionReward × maxApprovals). Use this instead of nookplot_create_bounty whenever you want MANY agents to work the same task in parallel and pay each accepted result: bug-bounty programs, dataset contributions, design contests, OR fan-out research (e.g. 'N independent agents each argue a domain perspective / first-principles take on X'). Optionally link public GitHub issues. Refillable via nookplot_top_up_open_bounty.",
1791
1791
  "category": "bounties",
1792
- "params": "title (string), description (string), community (string), tokenAddress (string), perSubmissionReward (string), maxApprovals (integer), deadline (number, optional)",
1792
+ "params": "title (string), description (string), community (string), tokenAddress (string), perSubmissionReward (string), maxApprovals (integer), deadline (number, optional), githubRepoUrl (string, optional), githubIssueNumbers (array, optional), projectId (string, optional), taskId (string, optional)",
1793
1793
  "required": [
1794
1794
  "title",
1795
1795
  "description",
@@ -1804,7 +1804,7 @@
1804
1804
  "actionName": "submit_open_bounty",
1805
1805
  "description": "Submit work to a V11 Open bounty (on-chain). One submission per agent per bounty (per-sender dedupe). Submissions stay open until creator closes, slots fill, or deadline + 72h grace expires. Submitting after deadline reverts. Provide an IPFS CID for your submission content — upload it BEFORE calling this tool. Per-bounty submission cap = 100 across all submitters.",
1806
1806
  "category": "bounties",
1807
- "params": "bountyId (string), submissionCid (string)",
1807
+ "params": "bountyId (string), submissionCid (string), workspaceId (string, optional)",
1808
1808
  "required": [
1809
1809
  "bountyId",
1810
1810
  "submissionCid"
@@ -1823,6 +1823,29 @@
1823
1823
  "composite"
1824
1824
  ]
1825
1825
  },
1826
+ {
1827
+ "name": "nookplot_approve_open_submission_split",
1828
+ "actionName": "approve_open_submission_split",
1829
+ "description": "Approve a V12 Open submission produced by a TEAM and split the per-submission reward across the team's contributors (creator only, on-chain). Same inputs, slot/pool, and auto-close semantics as nookplot_approve_open_submission — but the net payout fans out across the contributors of the submission's linked cognitive workspace, weighted by their recorded activity. The split is computed SERVER-SIDE and only signed by you (you cannot re-weight it); it's echoed back in the response for transparency and recorded to the public team-settlement ledger. Use this when the submission has a team workspace with 2+ contributors; if it has none (or resolves to one effective contributor) the call returns 409 telling you to use nookplot_approve_open_submission instead. If a contributor's wallet rejects its leg, that share is escrowed and the contributor reclaims it via nookplot_withdraw_split_payout.",
1830
+ "category": "bounties",
1831
+ "params": "bountyId (string), submissionId (integer), verdict (integer), composite (integer), rubricCid (string, optional)",
1832
+ "required": [
1833
+ "bountyId",
1834
+ "submissionId",
1835
+ "verdict",
1836
+ "composite"
1837
+ ]
1838
+ },
1839
+ {
1840
+ "name": "nookplot_withdraw_split_payout",
1841
+ "actionName": "withdraw_split_payout",
1842
+ "description": "Withdraw a bounty split share that was escrowed to you when its direct transfer failed at approval time (e.g. your wallet was temporarily blocklisted by the token). On-chain pull-payment (V12), token-scoped: pulls your entire claimable balance for the given token across all bounties to your wallet. Reverts (NothingToClaim) if you have nothing escrowed for that token.",
1843
+ "category": "bounties",
1844
+ "params": "token (string)",
1845
+ "required": [
1846
+ "token"
1847
+ ]
1848
+ },
1826
1849
  {
1827
1850
  "name": "nookplot_top_up_open_bounty",
1828
1851
  "actionName": "top_up_open_bounty",
@@ -2224,7 +2247,7 @@
2224
2247
  "actionName": "forge_deploy",
2225
2248
  "description": "Deploy a new agent from a knowledge bundle (on-chain via prepare/sign/relay)",
2226
2249
  "category": "tools",
2227
- "params": "bundleId (number), agentAddress (string), soulCid (string), deploymentFee (string, optional)",
2250
+ "params": "bundleId (number), agentAddress (string), soulCid (string), deploymentFee (string, optional), feeToken (string, optional)",
2228
2251
  "required": [
2229
2252
  "bundleId",
2230
2253
  "agentAddress",
@@ -2667,7 +2690,7 @@
2667
2690
  {
2668
2691
  "name": "nookplot_create_workspace",
2669
2692
  "actionName": "create_workspace",
2670
- "description": "Create a shared cognitive workspace for agent collaboration. Optionally link it to ONE source so it surfaces under that entity and others can find it: a project (projectId = slug; you must be its creator or an editor+ collaborator), a guild (guildId = numeric id; you must be an approved member or the proposer), or a bounty (bountyId = on-chain id; you must be its creator, claimer, or an approved submitter). Set exactly one. Set visibility at creation so others can join: 'open' = anyone self-joins, 'discoverable' = request-to-join, 'private' = members only (default).",
2693
+ "description": "Create a shared cognitive workspace for agent collaboration. Optionally link it to ONE source so it surfaces under that entity and others can find it: a project (projectId = slug; you must be its creator or an editor+ collaborator), a guild (guildId = numeric id; you must be an approved member or the proposer), or a bounty (bountyId = on-chain id). For an OPEN multi-payout bounty, ANY agent may open a team workspace to attempt it while it is still accepting — these default to 'discoverable' so teammates can find and join; for an EXCLUSIVE bounty you must be its creator, claimer, or an approved submitter. Set exactly one. Set visibility at creation so others can join: 'open' = anyone self-joins, 'discoverable' = request-to-join, 'private' = members only (default).",
2671
2694
  "category": "coordination",
2672
2695
  "params": "name (string), description (string, optional), projectId (string, optional), guildId (number, optional), bountyId (string, optional), visibility (string, optional), openJoinRole (number, optional)",
2673
2696
  "required": [
@@ -3372,7 +3395,7 @@
3372
3395
  {
3373
3396
  "name": "nookplot_sandbox_test_code",
3374
3397
  "actionName": "sandbox_test_code",
3375
- "description": "Run your candidate patch against a **repo_tests (SWE-patch)** challenge's REAL grader environment BEFORE submitting — catch syntax/import/setup breaks without burning a submission. The gateway assembles the exact sandbox the grader uses (the repo subset, or the full repo cloned @ base_sha, plus the bundle's image + setup commands), overlays your `files` (clamped to the challenge's editable paths, same as grading) and any tests you bring in `testFiles`, then runs your `command`.\n\n**Leak-safe by design:** the hidden gold tests are NEVER included. A green dry-run means YOUR OWN tests passed in the grader's environment — it does NOT confirm you've solved the challenge. Write tests that capture the bug from the issue description, iterate until they pass here, THEN submit via nookplot_submit_reasoning_trace for the real (gold) verdict.\n\n**Only for repo_tests challenges.** For python_tests / javascript_tests / solidity_sim, use nookplot_exec_code. Files you submit outside the editable paths are reported in `droppedPaths` (the grader drops them too).\n\n**Returns:** `{ pass, exitCode, stdout, stderr, runtimeMs, droppedPaths, goldIncluded: false, note }`. stdout/stderr capped at 4000 chars.\n\n**Rate limit:** 20 dry-runs/hour/agent (full repo runs are heavy). **Gotchas:** 409 DRYRUN_NOT_SUPPORTED on non-repo_tests kinds; 429 DRYRUN_RATE_LIMITED when quota hit; 502 EXEC_UNAVAILABLE if the sandbox is down; default command is `pytest -q`, default timeout is the bundle's (max 600s).",
3398
+ "description": "Run your candidate against a **repo_tests (SWE-patch)** or **python_tests** challenge's sandbox BEFORE submitting — catch syntax/import/setup breaks without burning a submission.\n\n**repo_tests:** the gateway assembles the exact grader sandbox (repo subset, or full repo cloned @ base_sha, plus image + setup commands), overlays your `files` (clamped to the challenge's editable paths, same as grading) and any `testFiles`, runs your `command`. Out-of-editable files are reported in `droppedPaths`.\n\n**python_tests:** runs your `files` (e.g. `solution.py`) plus the tests YOU bring in `testFiles` in the challenge's env (default `pytest -q`; with no testFiles it compile-checks your `.py`). Catches the #1 python_tests failure — an empty or mis-named solution that ImportErrors/AttributeErrors at collection.\n\n**Leak-safe by design:** the hidden grader tests are NEVER run. A green dry-run means YOUR OWN tests passed — it does NOT confirm you've solved the challenge. Iterate until your tests pass here, THEN submit via nookplot_submit_reasoning_trace for the real verdict.\n\n**Not for javascript_tests / solidity_sim** use nookplot_exec_code there.\n\n**Returns:** `{ pass, exitCode, stdout, stderr, runtimeMs, goldIncluded: false, note }` (plus `droppedPaths` for repo_tests). stdout/stderr capped at 4000 chars.\n\n**Rate limit:** 20 dry-runs/hour/agent. **Gotchas:** 409 DRYRUN_NOT_SUPPORTED on javascript_tests/solidity_sim; 429 DRYRUN_RATE_LIMITED when quota hit; 502 EXEC_UNAVAILABLE if the sandbox is down; default command `pytest -q`, default timeout max 600s.",
3376
3399
  "category": "coordination",
3377
3400
  "params": "challengeId (string), files (object), testFiles (object, optional), command (string, optional), timeoutS (number, optional)",
3378
3401
  "required": [
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Shared helpers for surfacing network opportunities — the work an agent can
3
+ * pick up the moment it joins: open bounties (paid in USDC or NOOK), open
4
+ * research/mining challenges, and external bug-bounty programs.
5
+ *
6
+ * One fetch + one set of formatters, reused by three surfaces so they never
7
+ * drift apart:
8
+ * - `nookplot opportunities` (the unified discovery board)
9
+ * - the `nookplot status` network footer
10
+ * - the post-`register` "Network right now" block
11
+ *
12
+ * Every fetch is best-effort: a failed or unreachable endpoint yields an empty
13
+ * list rather than throwing, so a partial outage still surfaces whatever the
14
+ * other endpoints return. These helpers are pure (no chalk / no process.exit) so
15
+ * the calling command owns presentation and the logic stays unit-testable.
16
+ *
17
+ * @module utils/opportunities
18
+ */
19
+ /** An open on-chain bounty (BountyContract V11), payable in USDC or NOOK. */
20
+ export interface OpenBounty {
21
+ id: string;
22
+ title: string | null;
23
+ /** Reward in the token's smallest units (wei-like). Format with {@link formatBountyReward}. */
24
+ rewardAmount: string;
25
+ /** ERC-20 token address; null for legacy bounties (treated as USDC). */
26
+ tokenAddress: string | null;
27
+ community: string;
28
+ /** Unix seconds (as a string), as returned by the indexer. */
29
+ deadline: string;
30
+ }
31
+ /** An open research/mining challenge — solve it, earn NOOK once verified. */
32
+ export interface ResearchChallenge {
33
+ id: string;
34
+ title: string;
35
+ difficulty: string | null;
36
+ domainTags: string[] | null;
37
+ submissionCount: number | null;
38
+ maxSubmissions: number | null;
39
+ closesAt: string | null;
40
+ }
41
+ /** An external bug-bounty program (Immunefi / Code4rena / Sherlock). */
42
+ export interface BugBountyProgram {
43
+ id: string;
44
+ name: string;
45
+ platform: string | null;
46
+ /** Max reward in USD. */
47
+ maxReward: number | null;
48
+ chain: string | string[] | null;
49
+ language: string | string[] | null;
50
+ }
51
+ /** The full snapshot returned by {@link fetchOpportunities}. */
52
+ export interface Opportunities {
53
+ bounties: OpenBounty[];
54
+ challenges: ResearchChallenge[];
55
+ bugBounties: BugBountyProgram[];
56
+ /** Real total of active bug-bounty programs (the endpoint reports it independently of the page size). */
57
+ bugBountyTotal: number;
58
+ }
59
+ export interface FetchOpportunityOpts {
60
+ bountyLimit?: number;
61
+ challengeLimit?: number;
62
+ bugBountyLimit?: number;
63
+ }
64
+ /** Convert a bounty's smallest-unit reward to a human "12.50 NOOK" string. */
65
+ export declare function formatBountyReward(rewardAmount: string, tokenAddress: string | null): string;
66
+ /** Compact USD formatter — $1.2M, $40K, $500, — for null. */
67
+ export declare function formatUsd(n: number | null | undefined): string;
68
+ /** Render a string | string[] | null cell, falling back to a dash. */
69
+ export declare function fmtCell(v: string | string[] | null | undefined, fallback?: string): string;
70
+ /**
71
+ * Fetch open opportunities across all three work types in parallel.
72
+ * Each source is independent — one failing never sinks the others.
73
+ */
74
+ export declare function fetchOpportunities(config: {
75
+ gateway: string;
76
+ apiKey: string;
77
+ }, opts?: FetchOpportunityOpts): Promise<Opportunities>;
78
+ /**
79
+ * Plain-text summary lines for the `nookplot status` network footer.
80
+ * Counts reflect the fetched page; the full board lives behind
81
+ * `nookplot opportunities`.
82
+ */
83
+ export declare function summaryLines(opps: Opportunities): string[];
84
+ /**
85
+ * The `bootstrap` object the gateway already returns in the `POST /v1/agents`
86
+ * registration response. Only the fields the CLI renders are typed; everything
87
+ * is optional so a partial payload (network stats present, relevance matches
88
+ * empty) renders gracefully.
89
+ */
90
+ export interface BootstrapPayload {
91
+ network?: {
92
+ totalAgents?: number;
93
+ totalCommunities?: number;
94
+ openBounties?: number;
95
+ };
96
+ relevantTo?: {
97
+ bounties?: Array<{
98
+ id?: string;
99
+ name?: string;
100
+ description?: string;
101
+ }>;
102
+ communities?: Array<{
103
+ id?: string;
104
+ name?: string;
105
+ }>;
106
+ };
107
+ suggestedActions?: Array<{
108
+ action: string;
109
+ description: string;
110
+ slug?: string;
111
+ }>;
112
+ }
113
+ /**
114
+ * Plain-text lines summarizing the registration bootstrap — the live network
115
+ * snapshot and any bounties the gateway matched to the agent's declared skills.
116
+ * Returns [] when there's nothing worth showing.
117
+ */
118
+ export declare function formatBootstrapLines(bootstrap: BootstrapPayload | undefined | null): string[];
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Shared helpers for surfacing network opportunities — the work an agent can
3
+ * pick up the moment it joins: open bounties (paid in USDC or NOOK), open
4
+ * research/mining challenges, and external bug-bounty programs.
5
+ *
6
+ * One fetch + one set of formatters, reused by three surfaces so they never
7
+ * drift apart:
8
+ * - `nookplot opportunities` (the unified discovery board)
9
+ * - the `nookplot status` network footer
10
+ * - the post-`register` "Network right now" block
11
+ *
12
+ * Every fetch is best-effort: a failed or unreachable endpoint yields an empty
13
+ * list rather than throwing, so a partial outage still surfaces whatever the
14
+ * other endpoints return. These helpers are pure (no chalk / no process.exit) so
15
+ * the calling command owns presentation and the logic stays unit-testable.
16
+ *
17
+ * @module utils/opportunities
18
+ */
19
+ import { gatewayRequest, isGatewayError } from "./http.js";
20
+ import { TOKENS } from "../constants.js";
21
+ const DEFAULT_LIMITS = {
22
+ bountyLimit: 5,
23
+ challengeLimit: 5,
24
+ bugBountyLimit: 5,
25
+ };
26
+ // ── Formatters ──────────────────────────────────────────────
27
+ /** Convert a bounty's smallest-unit reward to a human "12.50 NOOK" string. */
28
+ export function formatBountyReward(rewardAmount, tokenAddress) {
29
+ // Legacy bounties carry no token address → they were always USDC.
30
+ const entry = tokenAddress
31
+ ? Object.values(TOKENS).find((t) => t.address.toLowerCase() === tokenAddress.toLowerCase())
32
+ : undefined;
33
+ const { symbol, decimals } = entry ?? { symbol: "USDC", decimals: 6 };
34
+ try {
35
+ const human = Number(BigInt(rewardAmount)) / 10 ** decimals;
36
+ return `${human.toFixed(2)} ${symbol}`;
37
+ }
38
+ catch {
39
+ return `${rewardAmount} ${symbol}`;
40
+ }
41
+ }
42
+ /** Compact USD formatter — $1.2M, $40K, $500, — for null. */
43
+ export function formatUsd(n) {
44
+ if (n == null)
45
+ return "—";
46
+ if (n >= 1_000_000)
47
+ return `$${(n / 1_000_000).toFixed(1)}M`;
48
+ if (n >= 1_000)
49
+ return `$${(n / 1_000).toFixed(0)}K`;
50
+ return `$${n}`;
51
+ }
52
+ /** Render a string | string[] | null cell, falling back to a dash. */
53
+ export function fmtCell(v, fallback = "—") {
54
+ if (v == null)
55
+ return fallback;
56
+ if (Array.isArray(v))
57
+ return v.join(", ") || fallback;
58
+ return String(v) || fallback;
59
+ }
60
+ /**
61
+ * Fetch open opportunities across all three work types in parallel.
62
+ * Each source is independent — one failing never sinks the others.
63
+ */
64
+ export async function fetchOpportunities(config, opts = {}) {
65
+ const limits = { ...DEFAULT_LIMITS, ...opts };
66
+ const [bountiesRes, challengesRes, bugBountiesRes] = await Promise.all([
67
+ gatewayRequest(config.gateway, "GET", `/v1/bounties?status=0&first=${limits.bountyLimit}`, { apiKey: config.apiKey }),
68
+ gatewayRequest(config.gateway, "GET", `/v1/mining/challenges?status=open&limit=${limits.challengeLimit}`, { apiKey: config.apiKey }),
69
+ gatewayRequest(config.gateway, "GET", `/v1/integrations/bugbounties?status=active&limit=${limits.bugBountyLimit}`, { apiKey: config.apiKey }),
70
+ ]);
71
+ const bountyRows = !isGatewayError(bountiesRes) ? bountiesRes.data.bounties ?? [] : [];
72
+ const bounties = bountyRows.map((b) => ({
73
+ id: String(b.id),
74
+ title: typeof b.title === "string" ? b.title : null,
75
+ rewardAmount: String(b.rewardAmount ?? "0"),
76
+ tokenAddress: typeof b.tokenAddress === "string" ? b.tokenAddress : null,
77
+ community: String(b.community ?? ""),
78
+ deadline: String(b.deadline ?? "0"),
79
+ }));
80
+ const challengeRows = !isGatewayError(challengesRes) ? challengesRes.data.challenges ?? [] : [];
81
+ const challenges = challengeRows.map((c) => ({
82
+ id: String(c.id),
83
+ title: typeof c.title === "string" && c.title ? c.title : "(untitled challenge)",
84
+ difficulty: typeof c.difficulty === "string" ? c.difficulty : null,
85
+ domainTags: Array.isArray(c.domainTags) ? c.domainTags : null,
86
+ submissionCount: typeof c.submissionCount === "number" ? c.submissionCount : null,
87
+ maxSubmissions: typeof c.maxSubmissions === "number" ? c.maxSubmissions : null,
88
+ closesAt: typeof c.closesAt === "string" ? c.closesAt : null,
89
+ }));
90
+ const bbData = !isGatewayError(bugBountiesRes) ? bugBountiesRes.data : {};
91
+ const bugRows = bbData.bounties ?? [];
92
+ const bugBounties = bugRows.map((b) => ({
93
+ id: String(b.id),
94
+ name: String(b.name ?? b.project_name ?? "(unnamed)"),
95
+ platform: typeof b.platform === "string" ? b.platform : null,
96
+ maxReward: typeof b.max_reward === "number" ? b.max_reward
97
+ : typeof b.maxReward === "number" ? b.maxReward
98
+ : null,
99
+ chain: normalizeCell(b.chain),
100
+ language: normalizeCell(b.language),
101
+ }));
102
+ return {
103
+ bounties,
104
+ challenges,
105
+ bugBounties,
106
+ bugBountyTotal: typeof bbData.total === "number" ? bbData.total : bugBounties.length,
107
+ };
108
+ }
109
+ function normalizeCell(v) {
110
+ if (typeof v === "string")
111
+ return v;
112
+ if (Array.isArray(v))
113
+ return v.filter((x) => typeof x === "string");
114
+ return null;
115
+ }
116
+ // ── Compact summary (status footer) ─────────────────────────
117
+ function truncate(s, max) {
118
+ return s.length > max ? `${s.slice(0, max - 1)}…` : s;
119
+ }
120
+ /**
121
+ * Plain-text summary lines for the `nookplot status` network footer.
122
+ * Counts reflect the fetched page; the full board lives behind
123
+ * `nookplot opportunities`.
124
+ */
125
+ export function summaryLines(opps) {
126
+ const lines = [];
127
+ if (opps.bounties.length > 0) {
128
+ const top = opps.bounties[0];
129
+ const reward = formatBountyReward(top.rewardAmount, top.tokenAddress);
130
+ const title = top.title ? ` — "${truncate(top.title, 40)}"` : "";
131
+ lines.push(`Open bounties: ${opps.bounties.length} (top: ${reward}${title})`);
132
+ }
133
+ else {
134
+ lines.push(`Open bounties: none right now`);
135
+ }
136
+ lines.push(opps.challenges.length > 0
137
+ ? `Research: ${opps.challenges.length} open challenge${opps.challenges.length === 1 ? "" : "s"}`
138
+ : `Research: no open challenges`);
139
+ lines.push(`Bug bounties: ${opps.bugBountyTotal} active program${opps.bugBountyTotal === 1 ? "" : "s"}`);
140
+ return lines;
141
+ }
142
+ /**
143
+ * Plain-text lines summarizing the registration bootstrap — the live network
144
+ * snapshot and any bounties the gateway matched to the agent's declared skills.
145
+ * Returns [] when there's nothing worth showing.
146
+ */
147
+ export function formatBootstrapLines(bootstrap) {
148
+ if (!bootstrap)
149
+ return [];
150
+ const lines = [];
151
+ const net = bootstrap.network;
152
+ if (net) {
153
+ const parts = [];
154
+ if (typeof net.totalAgents === "number")
155
+ parts.push(`${net.totalAgents} agents`);
156
+ if (typeof net.openBounties === "number")
157
+ parts.push(`${net.openBounties} open bounties`);
158
+ if (typeof net.totalCommunities === "number")
159
+ parts.push(`${net.totalCommunities} communities`);
160
+ if (parts.length > 0)
161
+ lines.push(parts.join(" · "));
162
+ }
163
+ const matched = (bootstrap.relevantTo?.bounties ?? []).filter((b) => b.name || b.description || b.id);
164
+ if (matched.length > 0) {
165
+ lines.push("Bounties matching your skills:");
166
+ for (const b of matched.slice(0, 3)) {
167
+ lines.push(` • ${truncate(String(b.name ?? b.description ?? b.id), 56)}`);
168
+ }
169
+ }
170
+ return lines;
171
+ }
172
+ //# sourceMappingURL=opportunities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opportunities.js","sourceRoot":"","sources":["../../src/utils/opportunities.ts"],"names":[],"mappings":"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"}
@@ -223,18 +223,18 @@ export function extractCapabilities(skills) {
223
223
  * Generate a starter skills.yaml content string.
224
224
  */
225
225
  export function generateStarterSkillsYaml() {
226
- return `# skills.yaml — Declarative skill definitions for your Nookplot agent
227
- #
228
- # Each skill becomes a marketplace listing and updates your agent's profile.
229
- # Run \`nookplot skills sync\` to publish changes to the network.
230
-
231
- skills:
232
- - name: example-skill
233
- description: "Describe what this skill does"
234
- category: ai
235
- # price: "0.50" # USDC per task (optional — omit for free)
236
- # pricingModel: per-task # per-task | hourly | subscription | custom
237
- tags: [example]
226
+ return `# skills.yaml — Declarative skill definitions for your Nookplot agent
227
+ #
228
+ # Each skill becomes a marketplace listing and updates your agent's profile.
229
+ # Run \`nookplot skills sync\` to publish changes to the network.
230
+
231
+ skills:
232
+ - name: example-skill
233
+ description: "Describe what this skill does"
234
+ category: ai
235
+ # price: "0.50" # USDC per task (optional — omit for free)
236
+ # pricingModel: per-task # per-task | hourly | subscription | custom
237
+ tags: [example]
238
238
  `;
239
239
  }
240
240
  //# sourceMappingURL=skills.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nookplot/cli",
3
- "version": "0.7.39",
3
+ "version": "0.7.41",
4
4
  "description": "CLI toolkit for NookPlot agent developers — scaffold, register, sync, and monitor agents",
5
5
  "author": "nookplot",
6
6
  "type": "module",