@promptai.credit/cli 0.1.0 → 0.2.0
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/README.md +28 -4
- package/dist/index.js +315 -18
- package/package.json +1 -6
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# promptai CLI
|
|
2
2
|
|
|
3
3
|
Brings the earn loop (prompt → opt-in ad during the wait → server-side verification →
|
|
4
|
-
credit → USDC claim) to
|
|
4
|
+
credit → USDC claim) to agents beyond the classic Cursor IDE. Claude Code and the
|
|
5
|
+
Cursor Agents Window are supported today; Codex CLI is next.
|
|
5
6
|
|
|
6
7
|
## How it works
|
|
7
8
|
|
|
@@ -14,7 +15,29 @@ credit → USDC claim) to terminal agents. Claude Code is supported today; Codex
|
|
|
14
15
|
server-verified ad session against it via the API.
|
|
15
16
|
2. The `/watch` page creates the ad session server-side on load, so SSV wall-clock
|
|
16
17
|
timing is enforced by the server; the browser countdown is cosmetic.
|
|
17
|
-
3. `promptai claim` pays out your verified balance as USDC on Base
|
|
18
|
+
3. `promptai claim` pays out your verified balance as USDC on Base.
|
|
19
|
+
|
|
20
|
+
## Cursor Agents Window (Glass)
|
|
21
|
+
|
|
22
|
+
The Agents Window doesn't run VS Code extensions, so the promptai extension can't
|
|
23
|
+
cover it. `promptai install cursor` wires `beforeSubmitPrompt` + `stop` into
|
|
24
|
+
`~/.cursor/hooks.json` (core Cursor hooks fire in both the Agents Window and the
|
|
25
|
+
classic IDE):
|
|
26
|
+
|
|
27
|
+
- Hook payloads are routed by shape (`cursor_version`/`conversation_id`), so events
|
|
28
|
+
reach the right adapter even if Cursor loads our Claude-format hooks through its
|
|
29
|
+
third-party mapping.
|
|
30
|
+
- Cursor 3.7+ reports real token usage on the `stop` payload (`input_tokens`,
|
|
31
|
+
`output_tokens`, cache splits); cost is priced from that, with cached reads/writes
|
|
32
|
+
discounted. On older versions the cost falls back to a transcript character-volume
|
|
33
|
+
**estimate** (Glass transcripts carry no token counts and redact assistant prose).
|
|
34
|
+
- When the classic IDE's extension host is running it already credits prompts, so
|
|
35
|
+
the CLI detects its listener file (live pid) and stands down - no double credit.
|
|
36
|
+
- Duplicate stop events for the same generation are collapsed with an atomic
|
|
37
|
+
once-lock in `~/.promptai/locks/`.
|
|
38
|
+
- On remote/SSH workspaces (`CURSOR_CODE_REMOTE`), the remote host has no display:
|
|
39
|
+
no ad tabs open there. Bank credits with `promptai watch` from any browser
|
|
40
|
+
(`/watch?device=<your device id>`) instead.
|
|
18
41
|
|
|
19
42
|
State lives in `~/.promptai/` (`config.json`, `state.json`, `cli.log`).
|
|
20
43
|
|
|
@@ -22,13 +45,14 @@ State lives in `~/.promptai/` (`config.json`, `state.json`, `cli.log`).
|
|
|
22
45
|
|
|
23
46
|
```bash
|
|
24
47
|
npm install -g @promptai.credit/cli
|
|
25
|
-
promptai install claude
|
|
48
|
+
promptai install claude # Claude Code hooks
|
|
49
|
+
promptai install cursor # Cursor Agents Window / classic IDE hooks
|
|
26
50
|
|
|
27
51
|
# then:
|
|
28
52
|
promptai status # device, balance, banked ad watches, recent prompts
|
|
29
53
|
promptai watch # open a rewarded ad now (banks a credit for later)
|
|
30
54
|
promptai set wallet 0x... # payout address
|
|
31
|
-
promptai claim # USDC on Base
|
|
55
|
+
promptai claim # USDC on Base
|
|
32
56
|
promptai set ads off # opt out any time
|
|
33
57
|
```
|
|
34
58
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import * as fs5 from "node:fs";
|
|
5
|
+
import * as path4 from "node:path";
|
|
6
|
+
|
|
3
7
|
// src/claude.ts
|
|
4
8
|
import * as crypto3 from "node:crypto";
|
|
5
9
|
import * as fs3 from "node:fs";
|
|
@@ -208,6 +212,34 @@ function readTranscriptUsage(transcriptPath, watermark) {
|
|
|
208
212
|
seenCount: entries.length
|
|
209
213
|
};
|
|
210
214
|
}
|
|
215
|
+
function readGlassTranscriptUsage(transcriptPath, watermark) {
|
|
216
|
+
const raw = fs2.readFileSync(transcriptPath, "utf8");
|
|
217
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
218
|
+
let inputChars = 0;
|
|
219
|
+
let outputChars = 0;
|
|
220
|
+
let newEntries = 0;
|
|
221
|
+
for (const line of lines.slice(watermark.seenCount)) {
|
|
222
|
+
let entry;
|
|
223
|
+
try {
|
|
224
|
+
entry = JSON.parse(line);
|
|
225
|
+
} catch {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const chars = JSON.stringify(entry.message?.content ?? "").length;
|
|
229
|
+
if (entry.role === "assistant") {
|
|
230
|
+
outputChars += chars;
|
|
231
|
+
newEntries += 1;
|
|
232
|
+
} else {
|
|
233
|
+
inputChars += chars;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
inputTokens: Math.round(inputChars / 4),
|
|
238
|
+
outputTokens: Math.round(outputChars / 4),
|
|
239
|
+
newEntries,
|
|
240
|
+
seenCount: lines.length
|
|
241
|
+
};
|
|
242
|
+
}
|
|
211
243
|
|
|
212
244
|
// src/claude.ts
|
|
213
245
|
var SOURCE = "claude-code";
|
|
@@ -382,15 +414,248 @@ async function redeemAgainstAd(serverUrl, deviceId, promptId, cost) {
|
|
|
382
414
|
}
|
|
383
415
|
}
|
|
384
416
|
|
|
417
|
+
// src/cursor.ts
|
|
418
|
+
import * as crypto4 from "node:crypto";
|
|
419
|
+
import * as fs4 from "node:fs";
|
|
420
|
+
import * as os3 from "node:os";
|
|
421
|
+
import * as path3 from "node:path";
|
|
422
|
+
var CURSOR_SOURCE = "cursor-agents";
|
|
423
|
+
var HOOK_MARKER2 = "promptai";
|
|
424
|
+
var MAX_CREDIT_PER_AD_USD2 = 5;
|
|
425
|
+
var AD_MIN_INTERVAL_MS2 = 9e4;
|
|
426
|
+
var SETTLE_RETRIES2 = [400, 800, 1500];
|
|
427
|
+
function isCursorPayload(payload) {
|
|
428
|
+
return typeof payload.cursor_version === "string" || typeof payload.conversation_id === "string";
|
|
429
|
+
}
|
|
430
|
+
function cursorHooksJsonPath() {
|
|
431
|
+
return path3.join(os3.homedir(), ".cursor", "hooks.json");
|
|
432
|
+
}
|
|
433
|
+
function installCursorHooks() {
|
|
434
|
+
const hooksPath = cursorHooksJsonPath();
|
|
435
|
+
fs4.mkdirSync(path3.dirname(hooksPath), { recursive: true });
|
|
436
|
+
let config = {};
|
|
437
|
+
if (fs4.existsSync(hooksPath)) {
|
|
438
|
+
try {
|
|
439
|
+
config = JSON.parse(fs4.readFileSync(hooksPath, "utf8"));
|
|
440
|
+
} catch {
|
|
441
|
+
fs4.copyFileSync(hooksPath, hooksPath + ".promptai-backup");
|
|
442
|
+
config = {};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
config.version = config.version ?? 1;
|
|
446
|
+
config.hooks = config.hooks ?? {};
|
|
447
|
+
const command2 = hookCommand();
|
|
448
|
+
let changed = false;
|
|
449
|
+
for (const event of ["beforeSubmitPrompt", "stop"]) {
|
|
450
|
+
const entries = Array.isArray(config.hooks[event]) ? config.hooks[event] : [];
|
|
451
|
+
const ours = entries.find(
|
|
452
|
+
(e) => typeof e?.command === "string" && e.command.includes(HOOK_MARKER2)
|
|
453
|
+
);
|
|
454
|
+
if (!ours) {
|
|
455
|
+
entries.push({ command: command2 });
|
|
456
|
+
config.hooks[event] = entries;
|
|
457
|
+
changed = true;
|
|
458
|
+
} else if (ours.command !== command2) {
|
|
459
|
+
ours.command = command2;
|
|
460
|
+
changed = true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (changed || !fs4.existsSync(hooksPath)) {
|
|
464
|
+
fs4.writeFileSync(hooksPath, JSON.stringify(config, null, 2) + "\n");
|
|
465
|
+
}
|
|
466
|
+
return { changed, hooksPath };
|
|
467
|
+
}
|
|
468
|
+
function uninstallCursorHooks() {
|
|
469
|
+
const hooksPath = cursorHooksJsonPath();
|
|
470
|
+
if (!fs4.existsSync(hooksPath)) return { changed: false };
|
|
471
|
+
let config;
|
|
472
|
+
try {
|
|
473
|
+
config = JSON.parse(fs4.readFileSync(hooksPath, "utf8"));
|
|
474
|
+
} catch {
|
|
475
|
+
return { changed: false };
|
|
476
|
+
}
|
|
477
|
+
if (!config.hooks) return { changed: false };
|
|
478
|
+
let changed = false;
|
|
479
|
+
for (const [event, entries] of Object.entries(config.hooks)) {
|
|
480
|
+
if (!Array.isArray(entries)) continue;
|
|
481
|
+
const kept = entries.filter(
|
|
482
|
+
(e) => !(typeof e?.command === "string" && e.command.includes(HOOK_MARKER2))
|
|
483
|
+
);
|
|
484
|
+
if (kept.length !== entries.length) {
|
|
485
|
+
config.hooks[event] = kept;
|
|
486
|
+
changed = true;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (changed) {
|
|
490
|
+
fs4.writeFileSync(hooksPath, JSON.stringify(config, null, 2) + "\n");
|
|
491
|
+
}
|
|
492
|
+
return { changed };
|
|
493
|
+
}
|
|
494
|
+
function extensionIsActive() {
|
|
495
|
+
const infoPath = path3.join(os3.homedir(), ".cursor", "promptai-listener.json");
|
|
496
|
+
try {
|
|
497
|
+
const info = JSON.parse(fs4.readFileSync(infoPath, "utf8"));
|
|
498
|
+
if (typeof info.pid !== "number") return false;
|
|
499
|
+
process.kill(info.pid, 0);
|
|
500
|
+
return true;
|
|
501
|
+
} catch {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function acquireOnceLock(key) {
|
|
506
|
+
const dir = path3.join(promptaiDir(), "locks");
|
|
507
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
508
|
+
try {
|
|
509
|
+
const cutoff = Date.now() - 24 * 60 * 60 * 1e3;
|
|
510
|
+
for (const name of fs4.readdirSync(dir)) {
|
|
511
|
+
const p = path3.join(dir, name);
|
|
512
|
+
if (fs4.statSync(p).mtimeMs < cutoff) fs4.rmSync(p, { recursive: true, force: true });
|
|
513
|
+
}
|
|
514
|
+
} catch {
|
|
515
|
+
}
|
|
516
|
+
try {
|
|
517
|
+
fs4.mkdirSync(path3.join(dir, key.replace(/[^a-zA-Z0-9_-]/g, "_")));
|
|
518
|
+
return true;
|
|
519
|
+
} catch {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function watermarkKey(conversationId) {
|
|
524
|
+
return `glass:${conversationId}`;
|
|
525
|
+
}
|
|
526
|
+
function handleCursorPromptSubmitted(payload) {
|
|
527
|
+
const config = loadConfig();
|
|
528
|
+
const state = loadState();
|
|
529
|
+
const convId = payload.conversation_id ?? "unknown";
|
|
530
|
+
const key = watermarkKey(convId);
|
|
531
|
+
if (!(key in state.watermarks) && payload.transcript_path) {
|
|
532
|
+
try {
|
|
533
|
+
const usage = readGlassTranscriptUsage(payload.transcript_path, { seenCount: 0 });
|
|
534
|
+
state.watermarks[key] = { lastTs: 0, seenCount: usage.seenCount };
|
|
535
|
+
} catch {
|
|
536
|
+
state.watermarks[key] = { lastTs: 0, seenCount: 0 };
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const remote = process.env.CURSOR_CODE_REMOTE === "true";
|
|
540
|
+
if (config.adsOptIn && !extensionIsActive() && Date.now() - state.lastAdOpenedAt >= AD_MIN_INTERVAL_MS2) {
|
|
541
|
+
const url = watchUrl(config.serverUrl, config.deviceId, CURSOR_SOURCE);
|
|
542
|
+
if (!remote && openInBrowser(url)) {
|
|
543
|
+
state.lastAdOpenedAt = Date.now();
|
|
544
|
+
log(`[cursor] opened ad tab for conv=${convId}`);
|
|
545
|
+
} else if (remote || !hasDisplay()) {
|
|
546
|
+
log(`[cursor] no display here - watch ads at ${url}`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
saveState(state);
|
|
550
|
+
}
|
|
551
|
+
async function handleCursorStop(payload) {
|
|
552
|
+
const config = loadConfig();
|
|
553
|
+
const convId = payload.conversation_id ?? "unknown";
|
|
554
|
+
if (extensionIsActive()) {
|
|
555
|
+
log(`[cursor] extension host active, deferring settle for conv=${convId}`);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (!payload.transcript_path) {
|
|
559
|
+
log(`[cursor] stop without transcript_path conv=${convId} (transcripts disabled?)`);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (!acquireOnceLock(`stop-${convId}-${payload.generation_id ?? "nogen"}`)) {
|
|
563
|
+
log(`[cursor] duplicate stop event for conv=${convId}, skipping`);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const state = loadState();
|
|
567
|
+
const key = watermarkKey(convId);
|
|
568
|
+
const watermark = state.watermarks[key] ?? { lastTs: 0, seenCount: 0 };
|
|
569
|
+
const model = payload.model_id || payload.model || "unknown";
|
|
570
|
+
let inputTokens = 0;
|
|
571
|
+
let outputTokens = 0;
|
|
572
|
+
let estimated = false;
|
|
573
|
+
const reportedInput = Number(payload.input_tokens ?? 0);
|
|
574
|
+
const reportedOutput = Number(payload.output_tokens ?? 0);
|
|
575
|
+
if (reportedInput + reportedOutput > 0) {
|
|
576
|
+
const cacheRead = Number(payload.cache_read_tokens ?? 0);
|
|
577
|
+
const cacheWrite = Number(payload.cache_write_tokens ?? 0);
|
|
578
|
+
const uncached = Math.max(0, reportedInput - cacheRead - cacheWrite);
|
|
579
|
+
inputTokens = Math.round(uncached + cacheWrite * 1.25 + cacheRead * 0.1);
|
|
580
|
+
outputTokens = reportedOutput;
|
|
581
|
+
} else {
|
|
582
|
+
estimated = true;
|
|
583
|
+
let usage;
|
|
584
|
+
for (const delay of SETTLE_RETRIES2) {
|
|
585
|
+
try {
|
|
586
|
+
usage = readGlassTranscriptUsage(payload.transcript_path, watermark);
|
|
587
|
+
if (usage.newEntries > 0) break;
|
|
588
|
+
} catch (err) {
|
|
589
|
+
log(`[cursor] transcript read failed: ${String(err)}`);
|
|
590
|
+
}
|
|
591
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
592
|
+
}
|
|
593
|
+
if (!usage || usage.newEntries === 0) {
|
|
594
|
+
log(`[cursor] no new usage for conv=${convId}, skipping`);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
inputTokens = usage.inputTokens;
|
|
598
|
+
outputTokens = usage.outputTokens;
|
|
599
|
+
}
|
|
600
|
+
try {
|
|
601
|
+
const snapshot = readGlassTranscriptUsage(payload.transcript_path, { seenCount: 0 });
|
|
602
|
+
state.watermarks[key] = { lastTs: 0, seenCount: snapshot.seenCount };
|
|
603
|
+
} catch {
|
|
604
|
+
state.watermarks[key] = watermark;
|
|
605
|
+
}
|
|
606
|
+
const cost = costUsd(model, inputTokens, outputTokens);
|
|
607
|
+
const promptId = crypto4.randomUUID();
|
|
608
|
+
let verified = false;
|
|
609
|
+
if (config.adsOptIn && cost > 0) {
|
|
610
|
+
verified = await redeemAgainstAd2(config.serverUrl, config.deviceId, promptId, cost);
|
|
611
|
+
}
|
|
612
|
+
state.prompts.unshift({
|
|
613
|
+
id: promptId,
|
|
614
|
+
ts: Date.now(),
|
|
615
|
+
agent: "cursor",
|
|
616
|
+
sessionId: convId,
|
|
617
|
+
model,
|
|
618
|
+
inputTokens,
|
|
619
|
+
outputTokens,
|
|
620
|
+
costUsd: cost,
|
|
621
|
+
verified,
|
|
622
|
+
estimated
|
|
623
|
+
});
|
|
624
|
+
saveState(state);
|
|
625
|
+
log(
|
|
626
|
+
`[cursor] settled conv=${convId} model=${model} in=${inputTokens} out=${outputTokens} cost=$${cost}${estimated ? " (estimated)" : ""} verified=${verified}`
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
async function redeemAgainstAd2(serverUrl, deviceId, promptId, cost) {
|
|
630
|
+
try {
|
|
631
|
+
const { sessions } = await listVerifiedSessions(serverUrl, deviceId);
|
|
632
|
+
if (sessions.length === 0) {
|
|
633
|
+
log(`[cursor] no verified ad session available for prompt ${promptId}`);
|
|
634
|
+
return false;
|
|
635
|
+
}
|
|
636
|
+
await redeemCredit(serverUrl, {
|
|
637
|
+
sessionId: sessions[0].sessionId,
|
|
638
|
+
deviceId,
|
|
639
|
+
promptId,
|
|
640
|
+
amountUsd: Math.min(cost, MAX_CREDIT_PER_AD_USD2)
|
|
641
|
+
});
|
|
642
|
+
return true;
|
|
643
|
+
} catch (err) {
|
|
644
|
+
log(`[cursor] credit redeem failed for prompt ${promptId}: ${String(err)}`);
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
385
649
|
// src/index.ts
|
|
386
650
|
var HELP = `promptai - ad-subsidized prompt credits for terminal agents
|
|
387
651
|
|
|
388
652
|
Usage:
|
|
389
653
|
promptai install claude Wire hooks into ~/.claude/settings.json
|
|
390
|
-
promptai
|
|
654
|
+
promptai install cursor Wire hooks into ~/.cursor/hooks.json (Agents Window)
|
|
655
|
+
promptai uninstall <agent> Remove our hooks (other hooks untouched)
|
|
391
656
|
promptai status Device, balance, banked ad credits, recent prompts
|
|
392
657
|
promptai watch Open a rewarded ad in the browser now
|
|
393
|
-
promptai claim [address] Claim your verified balance as USDC (Base
|
|
658
|
+
promptai claim [address] Claim your verified balance as USDC (Base)
|
|
394
659
|
promptai set wallet 0x... Set the payout wallet
|
|
395
660
|
promptai set server <url> Point at a different API server
|
|
396
661
|
promptai set ads on|off Toggle the rewarded-ads opt-in
|
|
@@ -404,15 +669,31 @@ function readStdin() {
|
|
|
404
669
|
});
|
|
405
670
|
}
|
|
406
671
|
async function cmdHook() {
|
|
672
|
+
const raw = await readStdin();
|
|
407
673
|
let payload = {};
|
|
408
674
|
try {
|
|
409
|
-
payload = JSON.parse(
|
|
675
|
+
payload = JSON.parse(raw);
|
|
410
676
|
} catch {
|
|
411
677
|
return;
|
|
412
678
|
}
|
|
413
|
-
const event = payload.hook_event_name ?? "";
|
|
679
|
+
const event = String(payload.hook_event_name ?? "");
|
|
680
|
+
if (process.env.PROMPTAI_DUMP === "1" || fs5.existsSync(path4.join(promptaiDir(), "debug"))) {
|
|
681
|
+
try {
|
|
682
|
+
const dir = path4.join(promptaiDir(), "dump");
|
|
683
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
684
|
+
fs5.writeFileSync(path4.join(dir, `${Date.now()}-${event || "unknown"}.json`), raw);
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
}
|
|
414
688
|
try {
|
|
415
|
-
if (
|
|
689
|
+
if (isCursorPayload(payload)) {
|
|
690
|
+
const cursorPayload = payload;
|
|
691
|
+
if (event === "beforeSubmitPrompt" || event === "UserPromptSubmit") {
|
|
692
|
+
handleCursorPromptSubmitted(cursorPayload);
|
|
693
|
+
} else if (event === "stop" || event === "Stop") {
|
|
694
|
+
await handleCursorStop(cursorPayload);
|
|
695
|
+
}
|
|
696
|
+
} else if (event === "UserPromptSubmit") {
|
|
416
697
|
handleUserPromptSubmit(payload);
|
|
417
698
|
} else if (event === "Stop") {
|
|
418
699
|
await handleStop(payload);
|
|
@@ -428,6 +709,7 @@ async function cmdStatus() {
|
|
|
428
709
|
console.log(`server ${config.serverUrl}`);
|
|
429
710
|
console.log(`wallet ${config.wallet || "(not set - promptai set wallet 0x...)"}`);
|
|
430
711
|
console.log(`ads ${config.adsOptIn ? "opted in" : "opted out"}`);
|
|
712
|
+
console.log(`watch ${watchUrl(config.serverUrl, config.deviceId, "manual")}`);
|
|
431
713
|
try {
|
|
432
714
|
const [balance, verified] = await Promise.all([
|
|
433
715
|
fetchBalance(config.serverUrl, config.deviceId),
|
|
@@ -503,25 +785,40 @@ function cmdSet(key, value) {
|
|
|
503
785
|
console.log(`${key} updated.`);
|
|
504
786
|
}
|
|
505
787
|
function cmdInstall(agent) {
|
|
506
|
-
if (agent
|
|
507
|
-
|
|
508
|
-
|
|
788
|
+
if (agent === "claude" || agent === "claude-code") {
|
|
789
|
+
const { changed, settingsPath } = installClaudeHooks();
|
|
790
|
+
console.log(
|
|
791
|
+
changed ? `Hooks installed into ${settingsPath}.` : `Hooks already installed in ${settingsPath}.`
|
|
792
|
+
);
|
|
793
|
+
console.log("Claude Code picks them up on its next session. Try: promptai watch, then run a prompt.");
|
|
509
794
|
return;
|
|
510
795
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
796
|
+
if (agent === "cursor" || agent === "cursor-agents") {
|
|
797
|
+
const { changed, hooksPath } = installCursorHooks();
|
|
798
|
+
console.log(
|
|
799
|
+
changed ? `Hooks installed into ${hooksPath}.` : `Hooks already installed in ${hooksPath}.`
|
|
800
|
+
);
|
|
801
|
+
console.log(
|
|
802
|
+
"Works in the Agents Window and the classic IDE (the CLI stands down when the promptai extension is running). Cursor hot-reloads hooks.json."
|
|
803
|
+
);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
console.error("Supported agents: claude, cursor (Codex CLI coming next). Usage: promptai install <agent>");
|
|
807
|
+
process.exitCode = 1;
|
|
516
808
|
}
|
|
517
809
|
function cmdUninstall(agent) {
|
|
518
|
-
if (agent
|
|
519
|
-
|
|
520
|
-
|
|
810
|
+
if (agent === "claude" || agent === "claude-code") {
|
|
811
|
+
const { changed } = uninstallClaudeHooks();
|
|
812
|
+
console.log(changed ? "Hooks removed." : "No promptai hooks found.");
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (agent === "cursor" || agent === "cursor-agents") {
|
|
816
|
+
const { changed } = uninstallCursorHooks();
|
|
817
|
+
console.log(changed ? "Hooks removed." : "No promptai hooks found.");
|
|
521
818
|
return;
|
|
522
819
|
}
|
|
523
|
-
|
|
524
|
-
|
|
820
|
+
console.error("Usage: promptai uninstall claude|cursor");
|
|
821
|
+
process.exitCode = 1;
|
|
525
822
|
}
|
|
526
823
|
var [, , command, ...args] = process.argv;
|
|
527
824
|
try {
|
package/package.json
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptai.credit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"description": "Earn ad-subsidized prompt credits from terminal AI agents (Claude Code). Watch a dev-tool ad while your agent works; verified watches pay your prompt's token cost in USDC.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"license": "MIT",
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/Vib-UX/promptai.git",
|
|
13
|
-
"directory": "product/cli"
|
|
14
|
-
},
|
|
15
10
|
"homepage": "https://promptai.credit",
|
|
16
11
|
"keywords": [
|
|
17
12
|
"claude-code",
|