clawpowers 1.1.1 → 1.1.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.
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+ // runtime/payments/ledger.js — Payment decision ledger
3
+ //
4
+ // Records every payment decision (approve, deny, dry-run) to a persistent
5
+ // JSONL log at ~/.clawpowers/logs/payments.jsonl. Provides CLI commands to
6
+ // review recent decisions and summarize spending by skill, chain, and outcome.
7
+ //
8
+ // Usage (CLI):
9
+ // node ledger.js log [--limit <n>]
10
+ // node ledger.js summary
11
+ //
12
+ // Usage (module):
13
+ // const { logPaymentDecision, getPaymentSummary } = require('./ledger');
14
+ // await logPaymentDecision({ skill: 'agent-payments', url: '...', ... });
15
+ // const summary = getPaymentSummary();
16
+ 'use strict';
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const os = require('os');
21
+
22
+ // Logs directory — contains payments.jsonl and other audit logs
23
+ const LOGS_DIR = path.join(
24
+ process.env.CLAWPOWERS_DIR || path.join(os.homedir(), '.clawpowers'),
25
+ 'logs'
26
+ );
27
+
28
+ /** Absolute path to the payment ledger file. */
29
+ const LEDGER_FILE = path.join(LOGS_DIR, 'payments.jsonl');
30
+
31
+ /**
32
+ * Creates the logs directory if it doesn't already exist.
33
+ * Mode 0o700 restricts access to the current user only.
34
+ */
35
+ function ensureLogsDir() {
36
+ if (!fs.existsSync(LOGS_DIR)) {
37
+ fs.mkdirSync(LOGS_DIR, { recursive: true, mode: 0o700 });
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Returns an ISO 8601 timestamp without milliseconds.
43
+ *
44
+ * @returns {string} e.g. "2026-03-22T21:42:00Z"
45
+ */
46
+ function isoTimestamp() {
47
+ return new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
48
+ }
49
+
50
+ /**
51
+ * Records a payment decision to the JSONL ledger.
52
+ *
53
+ * Each entry captures what happened at a payment gate: the skill that
54
+ * encountered the payment requirement, the URL that required payment,
55
+ * the amount and asset, the policy evaluation result, and whether payment
56
+ * would have been (or was) made.
57
+ *
58
+ * The entry is appended as a single JSON line to `~/.clawpowers/logs/payments.jsonl`.
59
+ * The file is created if it doesn't exist.
60
+ *
61
+ * @param {object} entry - Payment decision details.
62
+ * @param {string} [entry.skill='unknown'] - Skill that triggered the payment gate.
63
+ * @param {string} [entry.type='decision'] - Entry type: 'decision' | 'payment' | 'denial'.
64
+ * @param {string} [entry.url=''] - Resource URL that required payment.
65
+ * @param {string} [entry.required_amount='0'] - Amount required (in smallest asset unit).
66
+ * @param {string} [entry.asset='USDC'] - Asset symbol (e.g. 'USDC', 'ETH').
67
+ * @param {string} [entry.chain='base'] - Chain name (e.g. 'base', 'base-sepolia').
68
+ * @param {string} [entry.policy_result='dry_run'] - Policy outcome: 'dry_run' | 'approved' | 'denied' | 'disabled'.
69
+ * @param {string} [entry.reason=''] - Human-readable reason for the policy result.
70
+ * @param {boolean} [entry.would_have_paid=false] - Whether payment would have succeeded if live.
71
+ * @returns {object} The complete entry as written to the ledger (including timestamp).
72
+ */
73
+ function logPaymentDecision(entry) {
74
+ ensureLogsDir();
75
+
76
+ /** @type {object} Full ledger record with timestamp and defaults applied. */
77
+ const record = {
78
+ timestamp: isoTimestamp(),
79
+ skill: entry.skill || 'unknown',
80
+ type: entry.type || 'decision',
81
+ url: entry.url || '',
82
+ required_amount: entry.required_amount || '0',
83
+ asset: entry.asset || 'USDC',
84
+ chain: entry.chain || 'base',
85
+ policy_result: entry.policy_result || 'dry_run',
86
+ reason: entry.reason || '',
87
+ would_have_paid: Boolean(entry.would_have_paid),
88
+ };
89
+
90
+ // Append as a single JSON line — safe for concurrent appends
91
+ const line = JSON.stringify(record) + '\n';
92
+ fs.appendFileSync(LEDGER_FILE, line);
93
+ try { fs.chmodSync(LEDGER_FILE, 0o600); } catch (_) { /* non-fatal on Windows */ }
94
+
95
+ return record;
96
+ }
97
+
98
+ /**
99
+ * Reads all payment decision records from the ledger.
100
+ * Malformed JSON lines are silently skipped.
101
+ *
102
+ * @returns {object[]} Array of parsed ledger records in chronological order.
103
+ */
104
+ function loadLedger() {
105
+ if (!fs.existsSync(LEDGER_FILE)) return [];
106
+
107
+ const content = fs.readFileSync(LEDGER_FILE, 'utf8');
108
+ const records = [];
109
+ for (const line of content.split('\n')) {
110
+ if (!line.trim()) continue;
111
+ try {
112
+ records.push(JSON.parse(line));
113
+ } catch (_) {
114
+ // Skip malformed lines — don't crash on a single bad record
115
+ }
116
+ }
117
+ return records;
118
+ }
119
+
120
+ /**
121
+ * Computes a payment summary aggregated by skill, chain, and policy outcome.
122
+ *
123
+ * Returns totals broken down by:
124
+ * - Skill name: which skills hit payment gates most often
125
+ * - Chain: which networks were targeted for payment
126
+ * - Outcome: how many were dry_run vs approved vs denied vs disabled
127
+ *
128
+ * @returns {{
129
+ * total: number,
130
+ * by_skill: Object.<string, number>,
131
+ * by_chain: Object.<string, number>,
132
+ * by_outcome: Object.<string, number>,
133
+ * would_have_paid: number,
134
+ * records: object[]
135
+ * }} Aggregated summary object.
136
+ */
137
+ function getPaymentSummary() {
138
+ const records = loadLedger();
139
+
140
+ /** @type {Object.<string, number>} Count per skill name. */
141
+ const by_skill = {};
142
+ /** @type {Object.<string, number>} Count per chain name. */
143
+ const by_chain = {};
144
+ /** @type {Object.<string, number>} Count per policy_result value. */
145
+ const by_outcome = {};
146
+ let would_have_paid = 0;
147
+
148
+ for (const r of records) {
149
+ // Tally by skill
150
+ by_skill[r.skill] = (by_skill[r.skill] || 0) + 1;
151
+ // Tally by chain
152
+ by_chain[r.chain] = (by_chain[r.chain] || 0) + 1;
153
+ // Tally by outcome
154
+ by_outcome[r.policy_result] = (by_outcome[r.policy_result] || 0) + 1;
155
+ // Count would-have-paid scenarios (dry-run hits)
156
+ if (r.would_have_paid) would_have_paid++;
157
+ }
158
+
159
+ return {
160
+ total: records.length,
161
+ by_skill,
162
+ by_chain,
163
+ by_outcome,
164
+ would_have_paid,
165
+ records,
166
+ };
167
+ }
168
+
169
+ /**
170
+ * `log` CLI command — prints recent payment decisions to stdout.
171
+ * Defaults to the last 20 records. Use --limit to change.
172
+ *
173
+ * @param {string[]} argv - Arguments after 'log'.
174
+ */
175
+ function cmdLog(argv) {
176
+ // Parse optional --limit flag
177
+ let limit = 20;
178
+ for (let i = 0; i < argv.length; i++) {
179
+ if (argv[i] === '--limit' && argv[i + 1]) {
180
+ limit = parseInt(argv[i + 1], 10);
181
+ i++;
182
+ }
183
+ }
184
+
185
+ const records = loadLedger();
186
+ if (records.length === 0) {
187
+ console.log('No payment decisions recorded yet.');
188
+ console.log(`Ledger location: ${LEDGER_FILE}`);
189
+ return;
190
+ }
191
+
192
+ const slice = records.slice(Math.max(0, records.length - limit));
193
+ console.log(`Recent payment decisions (last ${slice.length} of ${records.length}):`);
194
+ console.log('');
195
+ for (const r of slice) {
196
+ const paid = r.would_have_paid ? '[would pay]' : '[would skip]';
197
+ console.log(` ${r.timestamp} | ${r.skill} | ${r.policy_result} ${paid}`);
198
+ if (r.url) console.log(` URL: ${r.url}`);
199
+ if (r.required_amount !== '0') {
200
+ console.log(` Amount: ${r.required_amount} ${r.asset} on ${r.chain}`);
201
+ }
202
+ if (r.reason) console.log(` Reason: ${r.reason}`);
203
+ console.log('');
204
+ }
205
+ }
206
+
207
+ /**
208
+ * `summary` CLI command — prints aggregated payment totals to stdout.
209
+ * Shows totals by skill, chain, and outcome.
210
+ */
211
+ function cmdSummary() {
212
+ const summary = getPaymentSummary();
213
+
214
+ if (summary.total === 0) {
215
+ console.log('No payment decisions recorded yet.');
216
+ console.log(`Ledger location: ${LEDGER_FILE}`);
217
+ return;
218
+ }
219
+
220
+ console.log(`Payment Decision Summary`);
221
+ console.log(`========================`);
222
+ console.log(`Total decisions: ${summary.total}`);
223
+ console.log(`Would have paid: ${summary.would_have_paid}`);
224
+ console.log('');
225
+
226
+ console.log('By skill:');
227
+ for (const [skill, count] of Object.entries(summary.by_skill).sort()) {
228
+ console.log(` ${skill}: ${count}`);
229
+ }
230
+ console.log('');
231
+
232
+ console.log('By chain:');
233
+ for (const [chain, count] of Object.entries(summary.by_chain).sort()) {
234
+ console.log(` ${chain}: ${count}`);
235
+ }
236
+ console.log('');
237
+
238
+ console.log('By outcome:');
239
+ for (const [outcome, count] of Object.entries(summary.by_outcome).sort()) {
240
+ const pct = Math.round(count / summary.total * 100);
241
+ console.log(` ${outcome}: ${count} (${pct}%)`);
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Prints usage information for the ledger CLI to stdout.
247
+ */
248
+ function printUsage() {
249
+ console.log(`Usage: ledger.js <command> [options]
250
+
251
+ Commands:
252
+ log [--limit <n>] Show recent payment decisions (default: last 20)
253
+ summary Show totals by skill, chain, and outcome
254
+
255
+ Ledger file: ~/.clawpowers/logs/payments.jsonl
256
+
257
+ Examples:
258
+ ledger.js log
259
+ ledger.js log --limit 50
260
+ ledger.js summary`);
261
+ }
262
+
263
+ /**
264
+ * CLI dispatch — routes argv to the appropriate command.
265
+ *
266
+ * @param {string[]} argv - Argument array (typically process.argv.slice(2)).
267
+ */
268
+ function main(argv) {
269
+ const [cmd, ...rest] = argv;
270
+
271
+ switch (cmd) {
272
+ case 'log':
273
+ cmdLog(rest);
274
+ break;
275
+ case 'summary':
276
+ cmdSummary();
277
+ break;
278
+ case 'help':
279
+ case '-h':
280
+ case '--help':
281
+ printUsage();
282
+ break;
283
+ case undefined:
284
+ case '':
285
+ printUsage();
286
+ process.exit(1);
287
+ break;
288
+ default:
289
+ process.stderr.write(`Unknown command: ${cmd}\n`);
290
+ printUsage();
291
+ process.exit(1);
292
+ }
293
+ }
294
+
295
+ // Guard: only run CLI dispatch when invoked directly, not when require()'d
296
+ if (require.main === module) {
297
+ try {
298
+ main(process.argv.slice(2));
299
+ } catch (err) {
300
+ process.stderr.write(`Error: ${err.message}\n`);
301
+ process.exit(1);
302
+ }
303
+ }
304
+
305
+ module.exports = { logPaymentDecision, getPaymentSummary, LEDGER_FILE, LOGS_DIR };
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bash
2
+ # runtime/payments/ledger.sh — Payment decision ledger (bash version)
3
+ #
4
+ # Records payment decisions to ~/.clawpowers/logs/payments.jsonl.
5
+ # Provides CLI commands to review recent decisions and summarize spending.
6
+ #
7
+ # Usage:
8
+ # bash ledger.sh log [--limit <n>]
9
+ # bash ledger.sh summary
10
+ # bash ledger.sh record --skill <name> --url <url> --amount <amt> \
11
+ # --asset <asset> --chain <chain> \
12
+ # --policy <result> --reason <text> [--would-pay]
13
+ set -euo pipefail
14
+
15
+ # Runtime root — override with CLAWPOWERS_DIR for testing
16
+ CLAWPOWERS_DIR="${CLAWPOWERS_DIR:-$HOME/.clawpowers}"
17
+ LOGS_DIR="$CLAWPOWERS_DIR/logs"
18
+ LEDGER_FILE="$LOGS_DIR/payments.jsonl"
19
+
20
+ ## === Helpers ===
21
+
22
+ # Ensures the logs directory exists with correct permissions.
23
+ ensure_logs_dir() {
24
+ if [[ ! -d "$LOGS_DIR" ]]; then
25
+ mkdir -p "$LOGS_DIR"
26
+ chmod 700 "$LOGS_DIR"
27
+ fi
28
+ }
29
+
30
+ # Returns an ISO 8601 timestamp (seconds precision).
31
+ iso_timestamp() {
32
+ date -u +"%Y-%m-%dT%H:%M:%SZ"
33
+ }
34
+
35
+ # Escapes a string for safe embedding in a JSON value.
36
+ # Handles backslash, double-quote, and common control characters.
37
+ json_escape() {
38
+ local s="$1"
39
+ s="${s//\\/\\\\}" # backslash
40
+ s="${s//\"/\\\"}" # double quote
41
+ s="${s//$'\t'/\\t}" # tab
42
+ s="${s//$'\n'/\\n}" # newline
43
+ printf '%s' "$s"
44
+ }
45
+
46
+ ## === record command ===
47
+
48
+ # Appends a single payment decision JSON line to the ledger.
49
+ #
50
+ # Options:
51
+ # --skill <name> Skill that triggered the payment gate
52
+ # --type <type> Entry type: decision | payment | denial (default: decision)
53
+ # --url <url> Resource URL that required payment
54
+ # --amount <n> Required amount in smallest unit (default: 0)
55
+ # --asset <sym> Asset symbol: USDC, ETH, etc. (default: USDC)
56
+ # --chain <name> Chain name: base, base-sepolia, etc. (default: base)
57
+ # --policy <result> Policy outcome: dry_run | approved | denied | disabled
58
+ # --reason <text> Human-readable reason for the policy result
59
+ # --would-pay Flag: set would_have_paid=true
60
+ cmd_record() {
61
+ local skill="unknown"
62
+ local type="decision"
63
+ local url=""
64
+ local amount="0"
65
+ local asset="USDC"
66
+ local chain="base"
67
+ local policy="dry_run"
68
+ local reason=""
69
+ local would_pay="false"
70
+
71
+ while [[ $# -gt 0 ]]; do
72
+ case "$1" in
73
+ --skill) skill="$2"; shift 2 ;;
74
+ --type) type="$2"; shift 2 ;;
75
+ --url) url="$2"; shift 2 ;;
76
+ --amount) amount="$2"; shift 2 ;;
77
+ --asset) asset="$2"; shift 2 ;;
78
+ --chain) chain="$2"; shift 2 ;;
79
+ --policy) policy="$2"; shift 2 ;;
80
+ --reason) reason="$2"; shift 2 ;;
81
+ --would-pay) would_pay="true"; shift ;;
82
+ *) echo "Unknown option: $1" >&2; exit 1 ;;
83
+ esac
84
+ done
85
+
86
+ ensure_logs_dir
87
+
88
+ local ts
89
+ ts=$(iso_timestamp)
90
+
91
+ # Build the JSON line manually (no jq dependency required)
92
+ local line
93
+ line="{\"timestamp\":\"$(json_escape "$ts")\","
94
+ line+="\"skill\":\"$(json_escape "$skill")\","
95
+ line+="\"type\":\"$(json_escape "$type")\","
96
+ line+="\"url\":\"$(json_escape "$url")\","
97
+ line+="\"required_amount\":\"$(json_escape "$amount")\","
98
+ line+="\"asset\":\"$(json_escape "$asset")\","
99
+ line+="\"chain\":\"$(json_escape "$chain")\","
100
+ line+="\"policy_result\":\"$(json_escape "$policy")\","
101
+ line+="\"reason\":\"$(json_escape "$reason")\","
102
+ line+="\"would_have_paid\":$would_pay}"
103
+
104
+ echo "$line" >> "$LEDGER_FILE"
105
+ chmod 600 "$LEDGER_FILE" 2>/dev/null || true
106
+
107
+ echo "Recorded: $skill → $policy ($(basename "$LEDGER_FILE"))"
108
+ }
109
+
110
+ ## === log command ===
111
+
112
+ # Shows recent payment decisions from the ledger.
113
+ #
114
+ # Options:
115
+ # --limit <n> Maximum records to show (default: 20)
116
+ cmd_log() {
117
+ local limit=20
118
+
119
+ while [[ $# -gt 0 ]]; do
120
+ case "$1" in
121
+ --limit) limit="$2"; shift 2 ;;
122
+ *) echo "Unknown option: $1" >&2; exit 1 ;;
123
+ esac
124
+ done
125
+
126
+ if [[ ! -f "$LEDGER_FILE" ]]; then
127
+ echo "No payment decisions recorded yet."
128
+ echo "Ledger location: $LEDGER_FILE"
129
+ return
130
+ fi
131
+
132
+ local total
133
+ total=$(wc -l < "$LEDGER_FILE" | tr -d ' ')
134
+
135
+ if [[ "$total" -eq 0 ]]; then
136
+ echo "No payment decisions recorded yet."
137
+ return
138
+ fi
139
+
140
+ echo "Recent payment decisions (last $limit of $total):"
141
+ echo ""
142
+
143
+ # tail the last N lines and pretty-print each JSON record
144
+ tail -n "$limit" "$LEDGER_FILE" | while IFS= read -r line; do
145
+ [[ -z "$line" ]] && continue
146
+
147
+ # Extract fields using basic shell tools (no jq dependency)
148
+ local ts skill policy would_pay url amount asset chain reason
149
+ ts=$(echo "$line" | grep -o '"timestamp":"[^"]*"' | cut -d'"' -f4)
150
+ skill=$(echo "$line" | grep -o '"skill":"[^"]*"' | cut -d'"' -f4)
151
+ policy=$(echo "$line" | grep -o '"policy_result":"[^"]*"' | cut -d'"' -f4)
152
+ would_pay=$(echo "$line" | grep -o '"would_have_paid":[a-z]*' | cut -d: -f2)
153
+ url=$(echo "$line" | grep -o '"url":"[^"]*"' | cut -d'"' -f4)
154
+ amount=$(echo "$line" | grep -o '"required_amount":"[^"]*"' | cut -d'"' -f4)
155
+ asset=$(echo "$line" | grep -o '"asset":"[^"]*"' | cut -d'"' -f4)
156
+ chain=$(echo "$line" | grep -o '"chain":"[^"]*"' | cut -d'"' -f4)
157
+ reason=$(echo "$line" | grep -o '"reason":"[^"]*"' | cut -d'"' -f4)
158
+
159
+ local paid_label="[would skip]"
160
+ [[ "$would_pay" == "true" ]] && paid_label="[would pay]"
161
+
162
+ echo " $ts | $skill | $policy $paid_label"
163
+ [[ -n "$url" ]] && echo " URL: $url"
164
+ [[ "$amount" != "0" ]] && echo " Amount: $amount $asset on $chain"
165
+ [[ -n "$reason" ]] && echo " Reason: $reason"
166
+ echo ""
167
+ done
168
+ }
169
+
170
+ ## === summary command ===
171
+
172
+ # Shows aggregated payment totals by skill, chain, and outcome.
173
+ cmd_summary() {
174
+ if [[ ! -f "$LEDGER_FILE" ]]; then
175
+ echo "No payment decisions recorded yet."
176
+ echo "Ledger location: $LEDGER_FILE"
177
+ return
178
+ fi
179
+
180
+ local total
181
+ total=$(grep -c '.' "$LEDGER_FILE" 2>/dev/null || echo 0)
182
+
183
+ if [[ "$total" -eq 0 ]]; then
184
+ echo "No payment decisions recorded yet."
185
+ return
186
+ fi
187
+
188
+ local would_have_paid
189
+ would_have_paid=$(grep -c '"would_have_paid":true' "$LEDGER_FILE" 2>/dev/null || echo 0)
190
+
191
+ echo "Payment Decision Summary"
192
+ echo "========================"
193
+ echo "Total decisions: $total"
194
+ echo "Would have paid: $would_have_paid"
195
+ echo ""
196
+
197
+ echo "By skill:"
198
+ grep -o '"skill":"[^"]*"' "$LEDGER_FILE" | \
199
+ cut -d'"' -f4 | sort | uniq -c | sort -rn | \
200
+ while read -r count skill; do echo " $skill: $count"; done
201
+ echo ""
202
+
203
+ echo "By chain:"
204
+ grep -o '"chain":"[^"]*"' "$LEDGER_FILE" | \
205
+ cut -d'"' -f4 | sort | uniq -c | sort -rn | \
206
+ while read -r count chain; do echo " $chain: $count"; done
207
+ echo ""
208
+
209
+ echo "By outcome:"
210
+ grep -o '"policy_result":"[^"]*"' "$LEDGER_FILE" | \
211
+ cut -d'"' -f4 | sort | uniq -c | sort -rn | \
212
+ while read -r count outcome; do echo " $outcome: $count"; done
213
+ }
214
+
215
+ ## === usage ===
216
+
217
+ print_usage() {
218
+ echo "Usage: ledger.sh <command> [options]"
219
+ echo ""
220
+ echo "Commands:"
221
+ echo " log [--limit <n>] Show recent payment decisions (default: last 20)"
222
+ echo " summary Show totals by skill, chain, and outcome"
223
+ echo " record [options] Record a payment decision"
224
+ echo ""
225
+ echo "record options:"
226
+ echo " --skill <name> Skill name (default: unknown)"
227
+ echo " --type <type> decision | payment | denial (default: decision)"
228
+ echo " --url <url> Resource URL"
229
+ echo " --amount <n> Required amount"
230
+ echo " --asset <sym> Asset symbol (default: USDC)"
231
+ echo " --chain <name> Chain name (default: base)"
232
+ echo " --policy <result> dry_run | approved | denied | disabled"
233
+ echo " --reason <text> Reason for policy result"
234
+ echo " --would-pay Set would_have_paid=true"
235
+ echo ""
236
+ echo "Ledger file: ~/.clawpowers/logs/payments.jsonl"
237
+ }
238
+
239
+ ## === main dispatch ===
240
+
241
+ main() {
242
+ local cmd="${1:-}"
243
+ shift || true
244
+
245
+ case "$cmd" in
246
+ log) cmd_log "$@" ;;
247
+ summary) cmd_summary "$@" ;;
248
+ record) cmd_record "$@" ;;
249
+ help|-h|--help) print_usage ;;
250
+ "")
251
+ print_usage
252
+ exit 1
253
+ ;;
254
+ *)
255
+ echo "Unknown command: $cmd" >&2
256
+ print_usage >&2
257
+ exit 1
258
+ ;;
259
+ esac
260
+ }
261
+
262
+ main "$@"
package/skill.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clawpowers",
3
- "version": "1.0.0",
4
- "description": "Runtime-powered skills framework 20 skills with persistent memory, self-improvement (RSI), outcome tracking, and autonomous agent payments (x402). Works on Claude Code, Cursor, Codex, OpenCode, Gemini CLI.",
3
+ "version": "1.1.1",
4
+ "description": "Runtime-powered skills framework \u2014 25 skills with persistent memory, self-improvement (RSI), outcome tracking, and autonomous agent payments (x402). Works on Claude Code, Cursor, Codex, OpenCode, Gemini CLI.",
5
5
  "author": {
6
6
  "name": "AI Agent Economy",
7
7
  "url": "https://github.com/up2itnow0822"
@@ -38,7 +38,11 @@
38
38
  "market research",
39
39
  "analyze performance"
40
40
  ],
41
- "platforms": ["macos", "linux", "windows"],
41
+ "platforms": [
42
+ "macos",
43
+ "linux",
44
+ "windows"
45
+ ],
42
46
  "category": "development",
43
47
  "runtime": {
44
48
  "init": "node bin/clawpowers.js init",
@@ -69,6 +73,13 @@
69
73
  "learn-how-to-learn",
70
74
  "market-intelligence",
71
75
  "prospecting"
76
+ ],
77
+ "rsi_intelligence": [
78
+ "meta-skill-evolution",
79
+ "self-healing-code",
80
+ "cross-project-knowledge",
81
+ "formal-verification-lite",
82
+ "economic-code-optimization"
72
83
  ]
73
84
  }
74
85
  }
@@ -46,6 +46,74 @@ Is the response HTTP 402?
46
46
  └── Yes → Proceed with autonomous payment
47
47
  ```
48
48
 
49
+ ## Dry-Run Mode
50
+
51
+ **Default mode is dry-run.** Until you explicitly enable live payments, the agent-payments skill operates in observation mode: it detects payment requirements, evaluates spending policy, and logs what *would* happen — without moving any funds.
52
+
53
+ ### What Dry-Run Does
54
+
55
+ - Intercepts HTTP 402 responses and parses x402 payment requirements
56
+ - Evaluates the payment against your configured policy (limits, allowlist, mode)
57
+ - Logs a structured entry to `~/.clawpowers/logs/payments.jsonl` describing the decision
58
+ - Reports the outcome inline: `[dry-run: would pay $0.03 USDC on base-sepolia]`
59
+ - Never submits a transaction or touches a wallet
60
+
61
+ ### No Funds Move in Dry-Run
62
+
63
+ Dry-run is safe to leave enabled indefinitely. No wallet is required, no credentials are needed, and no on-chain operations occur. The skill continues to function — it simply skips the actual payment step and logs instead.
64
+
65
+ ### Build Confidence Before Going Live
66
+
67
+ After 10+ dry-run cycles, review your payment log:
68
+
69
+ ```bash
70
+ npx clawpowers payments log
71
+ npx clawpowers payments summary
72
+ ```
73
+
74
+ You'll see exactly which skills are hitting payment gates, which APIs require payment, which chains and assets are involved, and how much you would have spent. When the pattern looks predictable and the amounts look reasonable, enable live payments with confidence.
75
+
76
+ ### How to Switch Modes
77
+
78
+ **Interactive wizard (recommended):**
79
+ ```bash
80
+ npx clawpowers payments setup
81
+ ```
82
+
83
+ **Manual config edit:**
84
+ Edit `~/.clawpowers/config.json`:
85
+ ```json
86
+ {
87
+ "payments": {
88
+ "enabled": true,
89
+ "mode": "live",
90
+ "per_tx_limit_usd": 0.10,
91
+ "daily_limit_usd": 5.00
92
+ }
93
+ }
94
+ ```
95
+
96
+ ### Example Dry-Run Log Entry
97
+
98
+ ```json
99
+ {
100
+ "timestamp": "2026-03-22T21:42:00Z",
101
+ "skill": "agent-payments",
102
+ "type": "decision",
103
+ "url": "https://api.example.com/premium-data",
104
+ "required_amount": "30000",
105
+ "asset": "USDC",
106
+ "chain": "base-sepolia",
107
+ "policy_result": "dry_run",
108
+ "reason": "payments.mode=dry_run",
109
+ "would_have_paid": true
110
+ }
111
+ ```
112
+
113
+ `would_have_paid: true` means the payment would have been approved by your policy — only the dry-run mode prevented it from executing.
114
+
115
+ ---
116
+
49
117
  ## Background: x402 Protocol
50
118
 
51
119
  The x402 protocol is a standard for machine-to-machine payments embedded in HTTP. When a server requires payment it returns: