chainlesschain 0.47.8 → 0.49.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.
Files changed (86) hide show
  1. package/bin/chainlesschain.js +0 -0
  2. package/package.json +10 -8
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
  5. package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
  6. package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
  7. package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
  8. package/src/assets/web-panel/index.html +2 -2
  9. package/src/commands/activitypub.js +533 -0
  10. package/src/commands/codegen.js +303 -0
  11. package/src/commands/collab.js +482 -0
  12. package/src/commands/compliance.js +597 -6
  13. package/src/commands/crosschain.js +382 -0
  14. package/src/commands/dbevo.js +388 -0
  15. package/src/commands/dev.js +411 -0
  16. package/src/commands/federation.js +427 -0
  17. package/src/commands/fusion.js +332 -0
  18. package/src/commands/governance.js +505 -0
  19. package/src/commands/hardening.js +110 -0
  20. package/src/commands/incentive.js +373 -0
  21. package/src/commands/inference.js +304 -0
  22. package/src/commands/infra.js +361 -0
  23. package/src/commands/kg.js +371 -0
  24. package/src/commands/marketplace.js +326 -0
  25. package/src/commands/matrix.js +283 -0
  26. package/src/commands/mcp.js +441 -18
  27. package/src/commands/nlprog.js +329 -0
  28. package/src/commands/nostr.js +196 -7
  29. package/src/commands/ops.js +408 -0
  30. package/src/commands/perception.js +385 -0
  31. package/src/commands/pqc.js +34 -0
  32. package/src/commands/privacy.js +345 -0
  33. package/src/commands/quantization.js +280 -0
  34. package/src/commands/recommend.js +336 -0
  35. package/src/commands/reputation.js +349 -0
  36. package/src/commands/runtime.js +500 -0
  37. package/src/commands/sla.js +352 -0
  38. package/src/commands/social.js +265 -0
  39. package/src/commands/stress.js +252 -0
  40. package/src/commands/tech.js +268 -0
  41. package/src/commands/tenant.js +576 -0
  42. package/src/commands/trust.js +366 -0
  43. package/src/harness/mcp-client.js +330 -54
  44. package/src/index.js +114 -0
  45. package/src/lib/activitypub-bridge.js +623 -0
  46. package/src/lib/aiops.js +523 -0
  47. package/src/lib/autonomous-developer.js +524 -0
  48. package/src/lib/code-agent.js +442 -0
  49. package/src/lib/collaboration-governance.js +556 -0
  50. package/src/lib/community-governance.js +649 -0
  51. package/src/lib/compliance-framework-reporter.js +600 -0
  52. package/src/lib/content-recommendation.js +600 -0
  53. package/src/lib/cross-chain.js +669 -0
  54. package/src/lib/dbevo.js +669 -0
  55. package/src/lib/decentral-infra.js +445 -0
  56. package/src/lib/federation-hardening.js +587 -0
  57. package/src/lib/hardening-manager.js +409 -0
  58. package/src/lib/inference-network.js +407 -0
  59. package/src/lib/knowledge-graph.js +530 -0
  60. package/src/lib/matrix-bridge.js +252 -0
  61. package/src/lib/mcp-client.js +3 -0
  62. package/src/lib/mcp-registry.js +347 -0
  63. package/src/lib/mcp-scaffold.js +385 -0
  64. package/src/lib/multimodal.js +698 -0
  65. package/src/lib/nl-programming.js +595 -0
  66. package/src/lib/nostr-bridge.js +214 -38
  67. package/src/lib/perception.js +500 -0
  68. package/src/lib/pqc-manager.js +141 -9
  69. package/src/lib/privacy-computing.js +575 -0
  70. package/src/lib/protocol-fusion.js +535 -0
  71. package/src/lib/quantization.js +362 -0
  72. package/src/lib/reputation-optimizer.js +509 -0
  73. package/src/lib/skill-marketplace.js +397 -0
  74. package/src/lib/sla-manager.js +484 -0
  75. package/src/lib/social-graph.js +408 -0
  76. package/src/lib/stix-parser.js +167 -0
  77. package/src/lib/stress-tester.js +383 -0
  78. package/src/lib/tech-learning-engine.js +651 -0
  79. package/src/lib/tenant-saas.js +831 -0
  80. package/src/lib/threat-intel.js +268 -0
  81. package/src/lib/token-incentive.js +513 -0
  82. package/src/lib/topic-classifier.js +400 -0
  83. package/src/lib/trust-security.js +473 -0
  84. package/src/lib/ueba.js +403 -0
  85. package/src/lib/universal-runtime.js +771 -0
  86. package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
@@ -0,0 +1,329 @@
1
+ /**
2
+ * `cc nlprog` — CLI surface for Phase 28 Natural Language Programming.
3
+ */
4
+
5
+ import { Command } from "commander";
6
+
7
+ import {
8
+ INTENT,
9
+ TRANSLATION_STATUS,
10
+ STYLE_CATEGORY,
11
+ ensureNlProgrammingTables,
12
+ classifyIntent,
13
+ extractEntities,
14
+ detectTechStack,
15
+ scoreCompleteness,
16
+ translate,
17
+ getTranslation,
18
+ listTranslations,
19
+ updateTranslationStatus,
20
+ refineTranslation,
21
+ removeTranslation,
22
+ addConvention,
23
+ getConvention,
24
+ listConventions,
25
+ removeConvention,
26
+ getNlProgrammingStats,
27
+ } from "../lib/nl-programming.js";
28
+
29
+ function _dbFromCtx(cmd) {
30
+ const root = cmd?.parent?.parent ?? cmd?.parent;
31
+ return root?._db;
32
+ }
33
+
34
+ export function registerNlProgCommand(program) {
35
+ const nlp = new Command("nlprog")
36
+ .description("Natural language programming system (Phase 28)")
37
+ .hook("preAction", (thisCmd) => {
38
+ const db = _dbFromCtx(thisCmd);
39
+ if (db) ensureNlProgrammingTables(db);
40
+ });
41
+
42
+ /* ── Catalogs ────────────────────────────────────── */
43
+
44
+ nlp
45
+ .command("intents")
46
+ .description("List supported intent types")
47
+ .option("--json", "JSON output")
48
+ .action((opts) => {
49
+ const intents = Object.values(INTENT);
50
+ if (opts.json) return console.log(JSON.stringify(intents, null, 2));
51
+ for (const i of intents) console.log(` ${i}`);
52
+ });
53
+
54
+ nlp
55
+ .command("statuses")
56
+ .description("List translation statuses")
57
+ .option("--json", "JSON output")
58
+ .action((opts) => {
59
+ const statuses = Object.values(TRANSLATION_STATUS);
60
+ if (opts.json) return console.log(JSON.stringify(statuses, null, 2));
61
+ for (const s of statuses) console.log(` ${s}`);
62
+ });
63
+
64
+ nlp
65
+ .command("style-categories")
66
+ .description("List style analysis categories")
67
+ .option("--json", "JSON output")
68
+ .action((opts) => {
69
+ const cats = Object.values(STYLE_CATEGORY);
70
+ if (opts.json) return console.log(JSON.stringify(cats, null, 2));
71
+ for (const c of cats) console.log(` ${c}`);
72
+ });
73
+
74
+ /* ── Analysis (stateless) ───────────────────────── */
75
+
76
+ nlp
77
+ .command("classify <text>")
78
+ .description("Classify intent of natural language input")
79
+ .option("--json", "JSON output")
80
+ .action((text, opts) => {
81
+ const result = classifyIntent(text);
82
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
83
+ console.log(`Intent: ${result.intent}`);
84
+ console.log(`Confidence: ${result.confidence}`);
85
+ });
86
+
87
+ nlp
88
+ .command("extract <text>")
89
+ .description("Extract entities from natural language input")
90
+ .option("--json", "JSON output")
91
+ .action((text, opts) => {
92
+ const result = extractEntities(text);
93
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
94
+ if (result.count === 0) return console.log("No entities found.");
95
+ console.log(`Found ${result.count} entity(ies):`);
96
+ for (const e of result.entities) {
97
+ console.log(` [${e.type}] ${e.value}`);
98
+ }
99
+ });
100
+
101
+ nlp
102
+ .command("detect-stack <text>")
103
+ .description("Detect technology stack from text")
104
+ .option("--json", "JSON output")
105
+ .action((text, opts) => {
106
+ const result = detectTechStack(text);
107
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
108
+ if (result.detected.length === 0)
109
+ return console.log("No tech stack detected.");
110
+ console.log(`Primary: ${result.primary}`);
111
+ console.log(`All: ${result.detected.join(", ")}`);
112
+ });
113
+
114
+ /* ── Translation ────────────────────────────────── */
115
+
116
+ nlp
117
+ .command("translate <text>")
118
+ .description("Translate natural language to spec (heuristic)")
119
+ .option("-i, --intent <intent>", "Override intent classification")
120
+ .option("-s, --spec <json>", "Attach spec JSON")
121
+ .option("-a, --ambiguities <json>", "Ambiguity list JSON")
122
+ .option("--json", "JSON output")
123
+ .action((text, opts) => {
124
+ const db = _dbFromCtx(nlp);
125
+ const result = translate(db, {
126
+ text,
127
+ intent: opts.intent,
128
+ spec: opts.spec,
129
+ ambiguities: opts.ambiguities,
130
+ });
131
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
132
+ if (result.translated) {
133
+ console.log(`Translation: ${result.translationId}`);
134
+ console.log(`Intent: ${result.intent}`);
135
+ console.log(`Completeness: ${result.completeness}`);
136
+ } else console.log(`Failed: ${result.reason}`);
137
+ });
138
+
139
+ nlp
140
+ .command("show <id>")
141
+ .description("Show translation details")
142
+ .option("--json", "JSON output")
143
+ .action((id, opts) => {
144
+ const db = _dbFromCtx(nlp);
145
+ const t = getTranslation(db, id);
146
+ if (!t) return console.log("Translation not found.");
147
+ if (opts.json) return console.log(JSON.stringify(t, null, 2));
148
+ console.log(`ID: ${t.id}`);
149
+ console.log(`Input: ${t.input_text.slice(0, 80)}`);
150
+ console.log(`Intent: ${t.intent}`);
151
+ console.log(`Status: ${t.status}`);
152
+ console.log(`Completeness: ${t.completeness_score}`);
153
+ if (t.entities) console.log(`Entities: ${t.entities.slice(0, 60)}`);
154
+ if (t.tech_stack) console.log(`Tech Stack: ${t.tech_stack}`);
155
+ if (t.ambiguities)
156
+ console.log(`Ambiguities: ${t.ambiguities.slice(0, 60)}`);
157
+ });
158
+
159
+ nlp
160
+ .command("list")
161
+ .description("List translations")
162
+ .option("-i, --intent <intent>", "Filter by intent")
163
+ .option("-s, --status <status>", "Filter by status")
164
+ .option("--limit <n>", "Max results", parseInt)
165
+ .option("--json", "JSON output")
166
+ .action((opts) => {
167
+ const db = _dbFromCtx(nlp);
168
+ const results = listTranslations(db, {
169
+ intent: opts.intent,
170
+ status: opts.status,
171
+ limit: opts.limit,
172
+ });
173
+ if (opts.json) return console.log(JSON.stringify(results, null, 2));
174
+ if (results.length === 0) return console.log("No translations.");
175
+ for (const t of results) {
176
+ console.log(
177
+ ` ${t.intent.padEnd(18)} ${t.status.padEnd(10)} comp:${String(t.completeness_score).padEnd(6)} ${t.input_text.slice(0, 40).padEnd(42)} ${t.id.slice(0, 8)}`,
178
+ );
179
+ }
180
+ });
181
+
182
+ nlp
183
+ .command("status <id> <status>")
184
+ .description("Update translation status (draft/complete/refined)")
185
+ .option("--json", "JSON output")
186
+ .action((id, status, opts) => {
187
+ const db = _dbFromCtx(nlp);
188
+ const result = updateTranslationStatus(db, id, status);
189
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
190
+ console.log(
191
+ result.updated
192
+ ? `Status updated to: ${result.status}`
193
+ : `Failed: ${result.reason}`,
194
+ );
195
+ });
196
+
197
+ nlp
198
+ .command("refine <id>")
199
+ .description("Refine a translation with updated spec")
200
+ .option("-s, --spec <json>", "Updated spec JSON")
201
+ .option("-a, --ambiguities <json>", "Updated ambiguities JSON")
202
+ .option("--json", "JSON output")
203
+ .action((id, opts) => {
204
+ const db = _dbFromCtx(nlp);
205
+ const result = refineTranslation(db, id, {
206
+ spec: opts.spec,
207
+ ambiguities: opts.ambiguities,
208
+ });
209
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
210
+ if (result.refined)
211
+ console.log(`Refined (completeness: ${result.completeness})`);
212
+ else console.log(`Failed: ${result.reason}`);
213
+ });
214
+
215
+ nlp
216
+ .command("remove <id>")
217
+ .description("Remove a translation")
218
+ .option("--json", "JSON output")
219
+ .action((id, opts) => {
220
+ const db = _dbFromCtx(nlp);
221
+ const result = removeTranslation(db, id);
222
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
223
+ console.log(
224
+ result.removed ? "Translation removed." : `Failed: ${result.reason}`,
225
+ );
226
+ });
227
+
228
+ /* ── Conventions ────────────────────────────────── */
229
+
230
+ nlp
231
+ .command("convention-add")
232
+ .description("Add a project convention")
233
+ .requiredOption(
234
+ "-c, --category <cat>",
235
+ "Category (naming/architecture/testing/style/imports/components)",
236
+ )
237
+ .requiredOption("-p, --pattern <text>", "Pattern description")
238
+ .option("-e, --examples <json>", "Examples JSON")
239
+ .option("-f, --confidence <n>", "Confidence (0-1)", parseFloat)
240
+ .option("-s, --source-files <json>", "Source files JSON")
241
+ .option("--json", "JSON output")
242
+ .action((opts) => {
243
+ const db = _dbFromCtx(nlp);
244
+ const result = addConvention(db, {
245
+ category: opts.category,
246
+ pattern: opts.pattern,
247
+ examples: opts.examples,
248
+ confidence: opts.confidence,
249
+ sourceFiles: opts.sourceFiles,
250
+ });
251
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
252
+ if (result.added) console.log(`Convention added: ${result.conventionId}`);
253
+ else console.log(`Failed: ${result.reason}`);
254
+ });
255
+
256
+ nlp
257
+ .command("convention-show <id>")
258
+ .description("Show convention details")
259
+ .option("--json", "JSON output")
260
+ .action((id, opts) => {
261
+ const db = _dbFromCtx(nlp);
262
+ const c = getConvention(db, id);
263
+ if (!c) return console.log("Convention not found.");
264
+ if (opts.json) return console.log(JSON.stringify(c, null, 2));
265
+ console.log(`ID: ${c.id}`);
266
+ console.log(`Category: ${c.category}`);
267
+ console.log(`Pattern: ${c.pattern}`);
268
+ console.log(`Confidence: ${c.confidence}`);
269
+ if (c.examples) console.log(`Examples: ${c.examples.slice(0, 60)}`);
270
+ });
271
+
272
+ nlp
273
+ .command("conventions")
274
+ .description("List project conventions")
275
+ .option("-c, --category <cat>", "Filter by category")
276
+ .option("--limit <n>", "Max results", parseInt)
277
+ .option("--json", "JSON output")
278
+ .action((opts) => {
279
+ const db = _dbFromCtx(nlp);
280
+ const results = listConventions(db, {
281
+ category: opts.category,
282
+ limit: opts.limit,
283
+ });
284
+ if (opts.json) return console.log(JSON.stringify(results, null, 2));
285
+ if (results.length === 0) return console.log("No conventions.");
286
+ for (const c of results) {
287
+ console.log(
288
+ ` ${c.category.padEnd(14)} conf:${String(c.confidence).padEnd(5)} ${c.pattern.slice(0, 40).padEnd(42)} ${c.id.slice(0, 8)}`,
289
+ );
290
+ }
291
+ });
292
+
293
+ nlp
294
+ .command("convention-remove <id>")
295
+ .description("Remove a convention")
296
+ .option("--json", "JSON output")
297
+ .action((id, opts) => {
298
+ const db = _dbFromCtx(nlp);
299
+ const result = removeConvention(db, id);
300
+ if (opts.json) return console.log(JSON.stringify(result, null, 2));
301
+ console.log(
302
+ result.removed ? "Convention removed." : `Failed: ${result.reason}`,
303
+ );
304
+ });
305
+
306
+ /* ── Stats ──────────────────────────────────────── */
307
+
308
+ nlp
309
+ .command("stats")
310
+ .description("NL Programming statistics")
311
+ .option("--json", "JSON output")
312
+ .action((opts) => {
313
+ const db = _dbFromCtx(nlp);
314
+ const s = getNlProgrammingStats(db);
315
+ if (opts.json) return console.log(JSON.stringify(s, null, 2));
316
+ console.log(
317
+ `Translations: ${s.translations.total} (avg completeness: ${s.translations.avgCompleteness})`,
318
+ );
319
+ for (const [intent, count] of Object.entries(s.translations.byIntent)) {
320
+ if (count > 0) console.log(` ${intent.padEnd(18)} ${count}`);
321
+ }
322
+ console.log(`Conventions: ${s.conventions.total}`);
323
+ for (const [cat, count] of Object.entries(s.conventions.byCategory)) {
324
+ if (count > 0) console.log(` ${cat.padEnd(14)} ${count}`);
325
+ }
326
+ });
327
+
328
+ program.addCommand(nlp);
329
+ }
@@ -14,6 +14,10 @@ import {
14
14
  getEvents,
15
15
  generateKeypair,
16
16
  mapDid,
17
+ publishDirectMessage,
18
+ decryptDirectMessage,
19
+ publishDeletion,
20
+ publishReaction,
17
21
  } from "../lib/nostr-bridge.js";
18
22
 
19
23
  export function registerNostrCommand(program) {
@@ -80,9 +84,13 @@ export function registerNostrCommand(program) {
80
84
 
81
85
  nostr
82
86
  .command("publish <content>")
83
- .description("Publish a text note event")
87
+ .description("Publish a text note event (signed if --privkey provided)")
84
88
  .option("-k, --kind <n>", "Event kind", "1")
85
- .option("-p, --pubkey <key>", "Author public key")
89
+ .option("-p, --pubkey <key>", "Author public key (unsigned path)")
90
+ .option(
91
+ "--privkey <hex>",
92
+ "Sign event with this 32-byte private key (hex). Pubkey is derived.",
93
+ )
86
94
  .action(async (content, options) => {
87
95
  try {
88
96
  const ctx = await bootstrap({ verbose: program.opts().verbose });
@@ -98,12 +106,17 @@ export function registerNostrCommand(program) {
98
106
  parseInt(options.kind),
99
107
  content,
100
108
  options.pubkey,
109
+ [],
110
+ options.privkey,
101
111
  );
102
112
  logger.success("Event published");
103
113
  logger.log(
104
- ` ${chalk.bold("ID:")} ${chalk.cyan(result.event.id.slice(0, 16))}...`,
114
+ ` ${chalk.bold("ID:")} ${chalk.cyan(result.event.id.slice(0, 16))}...`,
115
+ );
116
+ logger.log(
117
+ ` ${chalk.bold("Signed:")} ${result.event.sig ? chalk.green("yes (BIP-340)") : chalk.yellow("no — will be rejected by real relays")}`,
105
118
  );
106
- logger.log(` ${chalk.bold("Sent:")} ${result.sentCount} relay(s)`);
119
+ logger.log(` ${chalk.bold("Sent:")} ${result.sentCount} relay(s)`);
107
120
  await shutdown();
108
121
  } catch (err) {
109
122
  logger.error(`Failed: ${err.message}`);
@@ -158,11 +171,12 @@ export function registerNostrCommand(program) {
158
171
  if (options.json) {
159
172
  console.log(JSON.stringify(kp, null, 2));
160
173
  } else {
161
- logger.success("Keypair generated");
162
- logger.log(` ${chalk.bold("Public:")} ${chalk.cyan(kp.publicKey)}`);
174
+ logger.success("Keypair generated (BIP-340 schnorr / NIP-19)");
175
+ logger.log(` ${chalk.bold("npub:")} ${chalk.cyan(kp.npub)}`);
163
176
  logger.log(
164
- ` ${chalk.bold("Private:")} ${kp.privateKey.slice(0, 16)}...`,
177
+ ` ${chalk.bold("nsec:")} ${chalk.yellow(kp.nsec.slice(0, 20))}… ${chalk.dim("(keep private!)")}`,
165
178
  );
179
+ logger.log(` ${chalk.bold("pubHex:")} ${kp.publicKey}`);
166
180
  }
167
181
  } catch (err) {
168
182
  logger.error(`Failed: ${err.message}`);
@@ -182,4 +196,179 @@ export function registerNostrCommand(program) {
182
196
  process.exit(1);
183
197
  }
184
198
  });
199
+
200
+ // ── NIP-04: Encrypted Direct Message ──────────────────────────────
201
+
202
+ nostr
203
+ .command("dm <recipientPubkey> <plaintext>")
204
+ .description("Send a NIP-04 encrypted direct message (kind=4)")
205
+ .requiredOption("--sender-priv <hex>", "Sender's 32-byte private key (hex)")
206
+ .requiredOption(
207
+ "--sender-pub <hex>",
208
+ "Sender's 32-byte x-only public key (hex)",
209
+ )
210
+ .option("--json", "Output as JSON")
211
+ .action(async (recipientPubkey, plaintext, options) => {
212
+ try {
213
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
214
+ if (!ctx.db) {
215
+ logger.error("Database not available");
216
+ process.exit(1);
217
+ }
218
+ const db = ctx.db.getDatabase();
219
+ ensureNostrTables(db);
220
+
221
+ const result = publishDirectMessage(db, {
222
+ senderPrivkey: options.senderPriv,
223
+ senderPubkey: options.senderPub,
224
+ recipientPubkey,
225
+ plaintext,
226
+ });
227
+
228
+ if (options.json) {
229
+ console.log(JSON.stringify(result, null, 2));
230
+ } else {
231
+ logger.success("Encrypted DM sent");
232
+ logger.log(
233
+ ` ${chalk.bold("ID:")} ${chalk.cyan(result.event.id.slice(0, 16))}...`,
234
+ );
235
+ logger.log(` ${chalk.bold("Sent:")} ${result.sentCount} relay(s)`);
236
+ }
237
+ await shutdown();
238
+ } catch (err) {
239
+ logger.error(`Failed: ${err.message}`);
240
+ process.exit(1);
241
+ }
242
+ });
243
+
244
+ nostr
245
+ .command("dm-decrypt <eventId>")
246
+ .description("Decrypt a stored NIP-04 direct message by event id")
247
+ .requiredOption(
248
+ "--recipient-priv <hex>",
249
+ "Recipient's 32-byte private key (hex)",
250
+ )
251
+ .option("--json", "Output as JSON")
252
+ .action(async (eventId, options) => {
253
+ try {
254
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
255
+ if (!ctx.db) {
256
+ logger.error("Database not available");
257
+ process.exit(1);
258
+ }
259
+ const db = ctx.db.getDatabase();
260
+ ensureNostrTables(db);
261
+
262
+ const events = getEvents({ kinds: [4], limit: 1000 });
263
+ const event = events.find((e) => e.id === eventId);
264
+ if (!event) {
265
+ logger.error(`Event not found: ${eventId}`);
266
+ process.exit(1);
267
+ }
268
+
269
+ const plaintext = decryptDirectMessage({
270
+ event,
271
+ recipientPrivkey: options.recipientPriv,
272
+ });
273
+
274
+ if (options.json) {
275
+ console.log(JSON.stringify({ success: true, plaintext }, null, 2));
276
+ } else {
277
+ logger.success("Decrypted");
278
+ logger.log(
279
+ ` ${chalk.bold("From:")} ${chalk.cyan(event.pubkey.slice(0, 16))}...`,
280
+ );
281
+ logger.log(` ${chalk.bold("Message:")} ${plaintext}`);
282
+ }
283
+ await shutdown();
284
+ } catch (err) {
285
+ logger.error(`Failed: ${err.message}`);
286
+ process.exit(1);
287
+ }
288
+ });
289
+
290
+ // ── NIP-09: Event Deletion Request ─────────────────────────────────
291
+
292
+ nostr
293
+ .command("delete <eventIds...>")
294
+ .description("Publish a NIP-09 deletion request (kind=5)")
295
+ .option("-r, --reason <text>", "Reason for deletion", "")
296
+ .option("-p, --pubkey <key>", "Author public key")
297
+ .option("--json", "Output as JSON")
298
+ .action(async (eventIds, options) => {
299
+ try {
300
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
301
+ if (!ctx.db) {
302
+ logger.error("Database not available");
303
+ process.exit(1);
304
+ }
305
+ const db = ctx.db.getDatabase();
306
+ ensureNostrTables(db);
307
+
308
+ const result = publishDeletion(db, {
309
+ eventIds,
310
+ reason: options.reason,
311
+ pubkey: options.pubkey,
312
+ });
313
+
314
+ if (options.json) {
315
+ console.log(JSON.stringify(result, null, 2));
316
+ } else {
317
+ logger.success(
318
+ `Deletion request published for ${eventIds.length} event(s)`,
319
+ );
320
+ logger.log(
321
+ ` ${chalk.bold("ID:")} ${chalk.cyan(result.event.id.slice(0, 16))}...`,
322
+ );
323
+ logger.log(` ${chalk.bold("Sent:")} ${result.sentCount} relay(s)`);
324
+ }
325
+ await shutdown();
326
+ } catch (err) {
327
+ logger.error(`Failed: ${err.message}`);
328
+ process.exit(1);
329
+ }
330
+ });
331
+
332
+ // ── NIP-25: Reactions ──────────────────────────────────────────────
333
+
334
+ nostr
335
+ .command("react <targetEventId> <targetPubkey>")
336
+ .description(
337
+ 'Publish a NIP-25 reaction (kind=7). content "+" | "-" | emoji',
338
+ )
339
+ .option("-c, --content <symbol>", "Reaction symbol", "+")
340
+ .option("-p, --pubkey <key>", "Author public key")
341
+ .option("--json", "Output as JSON")
342
+ .action(async (targetEventId, targetPubkey, options) => {
343
+ try {
344
+ const ctx = await bootstrap({ verbose: program.opts().verbose });
345
+ if (!ctx.db) {
346
+ logger.error("Database not available");
347
+ process.exit(1);
348
+ }
349
+ const db = ctx.db.getDatabase();
350
+ ensureNostrTables(db);
351
+
352
+ const result = publishReaction(db, {
353
+ targetEventId,
354
+ targetPubkey,
355
+ content: options.content,
356
+ pubkey: options.pubkey,
357
+ });
358
+
359
+ if (options.json) {
360
+ console.log(JSON.stringify(result, null, 2));
361
+ } else {
362
+ logger.success(`Reaction "${options.content}" published`);
363
+ logger.log(
364
+ ` ${chalk.bold("ID:")} ${chalk.cyan(result.event.id.slice(0, 16))}...`,
365
+ );
366
+ logger.log(` ${chalk.bold("Sent:")} ${result.sentCount} relay(s)`);
367
+ }
368
+ await shutdown();
369
+ } catch (err) {
370
+ logger.error(`Failed: ${err.message}`);
371
+ process.exit(1);
372
+ }
373
+ });
185
374
  }