spora 0.1.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 (62) hide show
  1. package/README.md +87 -0
  2. package/bin/spora.js +2 -0
  3. package/dist/account-creator-PZW5JLHS.js +498 -0
  4. package/dist/account-creator-PZW5JLHS.js.map +1 -0
  5. package/dist/chunk-3JEDGXEM.js +32 -0
  6. package/dist/chunk-3JEDGXEM.js.map +1 -0
  7. package/dist/chunk-53YLFYJF.js +59 -0
  8. package/dist/chunk-53YLFYJF.js.map +1 -0
  9. package/dist/chunk-7CR4ID6P.js +614 -0
  10. package/dist/chunk-7CR4ID6P.js.map +1 -0
  11. package/dist/chunk-AHXZIGQE.js +156 -0
  12. package/dist/chunk-AHXZIGQE.js.map +1 -0
  13. package/dist/chunk-DJJWHOL3.js +162 -0
  14. package/dist/chunk-DJJWHOL3.js.map +1 -0
  15. package/dist/chunk-EBO4F5NU.js +105 -0
  16. package/dist/chunk-EBO4F5NU.js.map +1 -0
  17. package/dist/chunk-ERTBXYOP.js +81 -0
  18. package/dist/chunk-ERTBXYOP.js.map +1 -0
  19. package/dist/chunk-KELPENM3.js +47 -0
  20. package/dist/chunk-KELPENM3.js.map +1 -0
  21. package/dist/chunk-NFDZ47AG.js +57 -0
  22. package/dist/chunk-NFDZ47AG.js.map +1 -0
  23. package/dist/chunk-O23NWMYU.js +124 -0
  24. package/dist/chunk-O23NWMYU.js.map +1 -0
  25. package/dist/chunk-YEKHNTQO.js +80 -0
  26. package/dist/chunk-YEKHNTQO.js.map +1 -0
  27. package/dist/chunk-ZJZKH7N7.js +56 -0
  28. package/dist/chunk-ZJZKH7N7.js.map +1 -0
  29. package/dist/cli.js +675 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/client-3AQCA4YE.js +401 -0
  32. package/dist/client-3AQCA4YE.js.map +1 -0
  33. package/dist/client-RBGZWS3Q.js +373 -0
  34. package/dist/client-RBGZWS3Q.js.map +1 -0
  35. package/dist/colony-J5KQIV6M.js +229 -0
  36. package/dist/colony-J5KQIV6M.js.map +1 -0
  37. package/dist/config-NZAFARS6.js +14 -0
  38. package/dist/config-NZAFARS6.js.map +1 -0
  39. package/dist/crypto-FHSQ72NU.js +14 -0
  40. package/dist/crypto-FHSQ72NU.js.map +1 -0
  41. package/dist/heartbeat-J4JLYH2B.js +358 -0
  42. package/dist/heartbeat-J4JLYH2B.js.map +1 -0
  43. package/dist/init-BG4Z4XQU.js +205 -0
  44. package/dist/init-BG4Z4XQU.js.map +1 -0
  45. package/dist/llm-RDNC5Y3G.js +16 -0
  46. package/dist/llm-RDNC5Y3G.js.map +1 -0
  47. package/dist/mcp-server.js +773 -0
  48. package/dist/mcp-server.js.map +1 -0
  49. package/dist/memory-7FBE26K3.js +26 -0
  50. package/dist/memory-7FBE26K3.js.map +1 -0
  51. package/dist/memory-O3AJIKBX.js +24 -0
  52. package/dist/memory-O3AJIKBX.js.map +1 -0
  53. package/dist/paths-5GFUUHCZ.js +13 -0
  54. package/dist/paths-5GFUUHCZ.js.map +1 -0
  55. package/dist/prompt-builder-WNMZ2QCN.js +17 -0
  56. package/dist/prompt-builder-WNMZ2QCN.js.map +1 -0
  57. package/dist/queue-ELK5ZX7J.js +14 -0
  58. package/dist/queue-ELK5ZX7J.js.map +1 -0
  59. package/dist/x-client-J4GE5A7P.js +12 -0
  60. package/dist/x-client-J4GE5A7P.js.map +1 -0
  61. package/package.json +57 -0
  62. package/templates/SKILL.md +335 -0
package/dist/cli.js ADDED
@@ -0,0 +1,675 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ FRAMEWORKS,
4
+ GOAL_PRESETS,
5
+ createIdentity,
6
+ identityExists,
7
+ loadIdentity,
8
+ mutateIdentity,
9
+ renderIdentityDocument,
10
+ saveIdentity
11
+ } from "./chunk-7CR4ID6P.js";
12
+ import {
13
+ loadConfig
14
+ } from "./chunk-YEKHNTQO.js";
15
+ import {
16
+ hasXCredentials,
17
+ sporaExists
18
+ } from "./chunk-53YLFYJF.js";
19
+
20
+ // src/cli.ts
21
+ import { Command } from "commander";
22
+ import chalk from "chalk";
23
+ var BANNER = `
24
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557
25
+ \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
26
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551
27
+ \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551
28
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
29
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
30
+ `;
31
+ var program = new Command();
32
+ program.name("spora").description("AI agents (Spores) that autonomously manage X/Twitter accounts").version("0.1.0");
33
+ program.command("init").description("Set up X account credentials for your Spore").option("--method <method>", "Connection method: create | browser | api").option("--username <username>", "X username (without @)").option("--password <password>", "X password").option("--email <email>", "Email associated with X account").option("--account-name <name>", "Name for the new X account (create mode)").option("--api-key <key>", "X API Key (api mode)").option("--api-secret <secret>", "X API Secret (api mode)").option("--access-token <token>", "X Access Token (api mode)").option("--access-token-secret <secret>", "X Access Token Secret (api mode)").option("--bearer-token <token>", "X Bearer Token (api mode)").option("--api-tier <tier>", "X API tier: free | basic (api mode)").action(async (opts) => {
34
+ if (opts.method) {
35
+ const { ensureDirectories } = await import("./paths-5GFUUHCZ.js");
36
+ const { saveCredentials } = await import("./crypto-FHSQ72NU.js");
37
+ const { createDefaultConfig, saveConfig } = await import("./config-NZAFARS6.js");
38
+ ensureDirectories();
39
+ if (opts.method === "create") {
40
+ const accountName = opts.accountName ?? `Spore${Math.floor(Math.random() * 9e3) + 1e3}`;
41
+ console.log(JSON.stringify({ status: "Ensuring browser is installed..." }));
42
+ const { execSync } = await import("child_process");
43
+ try {
44
+ execSync("npx playwright install chromium", { stdio: "pipe" });
45
+ } catch {
46
+ }
47
+ console.log(JSON.stringify({ status: "Creating X account...", name: accountName }));
48
+ try {
49
+ const { provisionAccount } = await import("./account-creator-PZW5JLHS.js");
50
+ const result = await provisionAccount({
51
+ name: accountName
52
+ });
53
+ if (result.success) {
54
+ saveCredentials({
55
+ method: "browser",
56
+ username: result.username,
57
+ password: result.password,
58
+ email: result.email
59
+ });
60
+ const config2 = createDefaultConfig({ xMethod: "browser" });
61
+ saveConfig(config2);
62
+ console.log(JSON.stringify({
63
+ success: true,
64
+ method: "browser",
65
+ username: result.username,
66
+ email: result.email,
67
+ message: "X account created and credentials saved!"
68
+ }));
69
+ } else {
70
+ console.log(JSON.stringify({
71
+ success: false,
72
+ error: result.error,
73
+ message: "Automated creation failed. Try again with --method browser and provide existing credentials."
74
+ }));
75
+ process.exit(1);
76
+ }
77
+ } catch (error) {
78
+ console.log(JSON.stringify({
79
+ success: false,
80
+ error: error.message,
81
+ message: "Account creation failed. Try --method browser with existing credentials instead."
82
+ }));
83
+ process.exit(1);
84
+ }
85
+ return;
86
+ }
87
+ if (opts.method === "browser") {
88
+ if (!opts.username || !opts.password) {
89
+ console.log(JSON.stringify({ error: "Browser mode requires --username and --password" }));
90
+ process.exit(1);
91
+ }
92
+ saveCredentials({
93
+ method: "browser",
94
+ username: opts.username,
95
+ password: opts.password,
96
+ email: opts.email
97
+ });
98
+ } else if (opts.method === "api") {
99
+ if (!opts.apiKey || !opts.apiSecret || !opts.accessToken || !opts.accessTokenSecret || !opts.bearerToken) {
100
+ console.log(JSON.stringify({ error: "API mode requires --api-key, --api-secret, --access-token, --access-token-secret, --bearer-token" }));
101
+ process.exit(1);
102
+ }
103
+ saveCredentials({
104
+ method: "api",
105
+ apiKey: opts.apiKey,
106
+ apiSecret: opts.apiSecret,
107
+ accessToken: opts.accessToken,
108
+ accessTokenSecret: opts.accessTokenSecret,
109
+ bearerToken: opts.bearerToken
110
+ });
111
+ } else {
112
+ console.log(JSON.stringify({ error: "Method must be 'create', 'browser', or 'api'" }));
113
+ process.exit(1);
114
+ }
115
+ const config = createDefaultConfig({
116
+ xMethod: opts.method === "create" ? "browser" : opts.method,
117
+ xApiTier: opts.apiTier
118
+ });
119
+ saveConfig(config);
120
+ console.log(JSON.stringify({ success: true, method: opts.method, username: opts.username || "api-mode" }));
121
+ return;
122
+ }
123
+ console.log(chalk.cyan(BANNER));
124
+ console.log(chalk.bold("Welcome to Spora."));
125
+ console.log(chalk.gray("The global town square for AI agents.\n"));
126
+ const { runInit } = await import("./init-BG4Z4XQU.js");
127
+ await runInit();
128
+ });
129
+ program.command("serve").description("Start the Spora MCP server (stdio)").action(async () => {
130
+ const { startServer } = await import("./mcp-server.js");
131
+ await startServer();
132
+ });
133
+ program.command("status").description("Show Spore status").action(() => {
134
+ if (!hasXCredentials()) {
135
+ console.log(JSON.stringify({ error: "No X credentials found. Run `spora init` first." }));
136
+ process.exit(1);
137
+ }
138
+ const config = loadConfig();
139
+ const result = {
140
+ xMethod: config.xMethod,
141
+ credits: {
142
+ used: config.credits.postsUsedThisMonth,
143
+ limit: config.credits.monthlyPostLimit,
144
+ remaining: config.credits.monthlyPostLimit - config.credits.postsUsedThisMonth,
145
+ resetDate: config.credits.resetDate
146
+ }
147
+ };
148
+ if (identityExists()) {
149
+ const identity = loadIdentity();
150
+ result.identity = {
151
+ name: identity.name,
152
+ handle: identity.handle,
153
+ framework: identity.framework,
154
+ sporeId: identity.sporeId,
155
+ generation: identity.generation,
156
+ colony: identity.colony.joined,
157
+ goals: identity.goals,
158
+ traits: identity.traits,
159
+ coreValues: identity.coreValues
160
+ };
161
+ } else {
162
+ result.identity = null;
163
+ }
164
+ console.log(JSON.stringify(result, null, 2));
165
+ });
166
+ program.command("frameworks").description("List available inspiration frameworks").action(() => {
167
+ const list = Object.entries(FRAMEWORKS).map(([key, fw]) => ({
168
+ id: key,
169
+ label: fw.label,
170
+ tagline: fw.tagline,
171
+ description: fw.description
172
+ }));
173
+ console.log(JSON.stringify({ frameworks: list, goals: [...GOAL_PRESETS] }, null, 2));
174
+ });
175
+ program.command("framework").description("Get details of a specific framework").argument("<id>", "Framework ID").action((id) => {
176
+ const fw = FRAMEWORKS[id];
177
+ if (!fw) {
178
+ console.log(JSON.stringify({ error: `Unknown framework: ${id}` }));
179
+ process.exit(1);
180
+ }
181
+ console.log(JSON.stringify({ id, ...fw }, null, 2));
182
+ });
183
+ program.command("create").description("Create a new Spore identity").requiredOption("--framework <framework>", "Framework ID or 'custom'").requiredOption("--name <name>", "Display name").requiredOption("--handle <handle>", "X handle (without @)").option("--bio <bio>", "X bio (max 160 chars)").option("--origin <story>", "Origin story").option("--tone <tone>", "Voice/writing style").option("--worldview <worldview>", "How this Spore sees the world").option("--values <values...>", "Core values").option("--topics <topics...>", "Topics to engage with").option("--goals <goals...>", "Strategic goals").option("--boundaries <boundaries...>", "Things this Spore will NOT do").option("--catchphrases <phrases...>", "Signature phrases").option("--conflict-style <style>", "agree-to-disagree|debate|clap-back|ignore|humor-deflect").option("--vocabulary <style>", "academic|casual|internet-native|poetic|technical|mixed").option("--emoji-usage <level>", "never|rare|moderate|heavy").option("--tweet-style <style>", "one-liners|short-form|threads|mixed").option("--colony", "Join The Colony", false).option("--trait-aggression <n>", "Trait: aggression (0-1)", parseFloat).option("--trait-humor <n>", "Trait: humor (0-1)", parseFloat).option("--trait-formality <n>", "Trait: formality (0-1)", parseFloat).option("--trait-verbosity <n>", "Trait: verbosity (0-1)", parseFloat).option("--trait-empathy <n>", "Trait: empathy (0-1)", parseFloat).option("--trait-curiosity <n>", "Trait: curiosity (0-1)", parseFloat).option("--trait-confidence <n>", "Trait: confidence (0-1)", parseFloat).option("--trait-originality <n>", "Trait: originality (0-1)", parseFloat).action((opts) => {
184
+ try {
185
+ const customTraits = {};
186
+ for (const t of ["aggression", "humor", "formality", "verbosity", "empathy", "curiosity", "confidence", "originality"]) {
187
+ const val = opts[`trait${t.charAt(0).toUpperCase() + t.slice(1)}`];
188
+ if (val !== void 0) customTraits[t] = val;
189
+ }
190
+ const identity = createIdentity({
191
+ framework: opts.framework,
192
+ name: opts.name,
193
+ handle: opts.handle,
194
+ bio: opts.bio,
195
+ originStory: opts.origin,
196
+ tone: opts.tone,
197
+ worldview: opts.worldview,
198
+ coreValues: opts.values,
199
+ topics: opts.topics,
200
+ goals: opts.goals,
201
+ boundaries: opts.boundaries,
202
+ catchphrases: opts.catchphrases,
203
+ conflictStyle: opts.conflictStyle,
204
+ vocabularyStyle: opts.vocabulary,
205
+ emojiUsage: opts.emojiUsage,
206
+ tweetStyle: opts.tweetStyle,
207
+ joinColony: opts.colony,
208
+ customTraits: Object.keys(customTraits).length > 0 ? customTraits : void 0
209
+ });
210
+ saveIdentity(identity);
211
+ console.log(renderIdentityDocument(identity));
212
+ } catch (error) {
213
+ console.log(JSON.stringify({ error: error.message }));
214
+ process.exit(1);
215
+ }
216
+ });
217
+ program.command("identity").description("Show the full identity document (markdown)").action(() => {
218
+ if (!identityExists()) {
219
+ console.log(JSON.stringify({ error: "No Spore identity found." }));
220
+ process.exit(1);
221
+ }
222
+ console.log(renderIdentityDocument(loadIdentity()));
223
+ });
224
+ program.command("identity-json").description("Show identity as raw JSON").action(() => {
225
+ if (!identityExists()) {
226
+ console.log(JSON.stringify({ error: "No Spore identity found." }));
227
+ process.exit(1);
228
+ }
229
+ console.log(JSON.stringify(loadIdentity(), null, 2));
230
+ });
231
+ program.command("evolve").description("Mutate an identity field (dot notation)").argument("<field>", "Field path (e.g. traits.humor, tone, goals)").argument("<value>", "New value (JSON-parsed)").argument("<reason>", "Why this change is happening").action((field, value, reason) => {
232
+ if (!identityExists()) {
233
+ console.log(JSON.stringify({ error: "No Spore identity found." }));
234
+ process.exit(1);
235
+ }
236
+ try {
237
+ let parsed;
238
+ try {
239
+ parsed = JSON.parse(value);
240
+ } catch {
241
+ parsed = value;
242
+ }
243
+ let identity = loadIdentity();
244
+ identity = mutateIdentity(identity, field, parsed, reason);
245
+ saveIdentity(identity);
246
+ console.log(JSON.stringify({ success: true, field, generation: identity.generation, reason }));
247
+ } catch (error) {
248
+ console.log(JSON.stringify({ error: error.message }));
249
+ process.exit(1);
250
+ }
251
+ });
252
+ program.command("journal").description("Add a reflection to the evolution journal").argument("<reflection>", "Your reflection").action((reflection) => {
253
+ if (!identityExists()) {
254
+ console.log(JSON.stringify({ error: "No Spore identity found." }));
255
+ process.exit(1);
256
+ }
257
+ const identity = loadIdentity();
258
+ identity.evolutionJournal.push({ date: (/* @__PURE__ */ new Date()).toISOString(), reflection });
259
+ saveIdentity(identity);
260
+ console.log(JSON.stringify({ success: true, totalEntries: identity.evolutionJournal.length }));
261
+ });
262
+ program.command("post").description("Post a tweet").argument("<content>", "Tweet content (max 280 chars)").action(async (content) => {
263
+ try {
264
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
265
+ const client = await getXClient();
266
+ const result = await client.postTweet(content);
267
+ console.log(JSON.stringify(result, null, 2));
268
+ } catch (error) {
269
+ console.log(JSON.stringify({ error: error.message }));
270
+ process.exit(1);
271
+ }
272
+ });
273
+ program.command("reply").description("Reply to a tweet").argument("<tweetId>", "Tweet ID to reply to").argument("<content>", "Reply content").action(async (tweetId, content) => {
274
+ try {
275
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
276
+ const client = await getXClient();
277
+ const result = await client.replyToTweet(tweetId, content);
278
+ console.log(JSON.stringify(result, null, 2));
279
+ } catch (error) {
280
+ console.log(JSON.stringify({ error: error.message }));
281
+ process.exit(1);
282
+ }
283
+ });
284
+ program.command("like").description("Like a tweet").argument("<tweetId>", "Tweet ID").action(async (tweetId) => {
285
+ try {
286
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
287
+ const client = await getXClient();
288
+ const result = await client.likeTweet(tweetId);
289
+ console.log(JSON.stringify(result, null, 2));
290
+ } catch (error) {
291
+ console.log(JSON.stringify({ error: error.message }));
292
+ process.exit(1);
293
+ }
294
+ });
295
+ program.command("retweet").description("Retweet a tweet").argument("<tweetId>", "Tweet ID").action(async (tweetId) => {
296
+ try {
297
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
298
+ const client = await getXClient();
299
+ const result = await client.retweet(tweetId);
300
+ console.log(JSON.stringify(result, null, 2));
301
+ } catch (error) {
302
+ console.log(JSON.stringify({ error: error.message }));
303
+ process.exit(1);
304
+ }
305
+ });
306
+ program.command("follow").description("Follow a user").argument("<handle>", "User handle or ID").action(async (handle) => {
307
+ try {
308
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
309
+ const client = await getXClient();
310
+ const result = await client.followUser(handle);
311
+ console.log(JSON.stringify(result, null, 2));
312
+ } catch (error) {
313
+ console.log(JSON.stringify({ error: error.message }));
314
+ process.exit(1);
315
+ }
316
+ });
317
+ program.command("unfollow").description("Unfollow a user").argument("<handle>", "User handle or ID").action(async (handle) => {
318
+ try {
319
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
320
+ const client = await getXClient();
321
+ const result = await client.unfollowUser(handle);
322
+ console.log(JSON.stringify(result, null, 2));
323
+ } catch (error) {
324
+ console.log(JSON.stringify({ error: error.message }));
325
+ process.exit(1);
326
+ }
327
+ });
328
+ program.command("timeline").description("Read home timeline").option("-c, --count <n>", "Number of tweets", "20").action(async (opts) => {
329
+ try {
330
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
331
+ const client = await getXClient();
332
+ const result = await client.getTimeline({ count: parseInt(opts.count) });
333
+ console.log(JSON.stringify(result, null, 2));
334
+ } catch (error) {
335
+ console.log(JSON.stringify({ error: error.message }));
336
+ process.exit(1);
337
+ }
338
+ });
339
+ program.command("mentions").description("Read mentions").option("-c, --count <n>", "Number of mentions", "20").action(async (opts) => {
340
+ try {
341
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
342
+ const client = await getXClient();
343
+ const result = await client.getMentions({ count: parseInt(opts.count) });
344
+ console.log(JSON.stringify(result, null, 2));
345
+ } catch (error) {
346
+ console.log(JSON.stringify({ error: error.message }));
347
+ process.exit(1);
348
+ }
349
+ });
350
+ program.command("search").description("Search for tweets").argument("<query>", "Search query").option("-c, --count <n>", "Number of results", "20").action(async (query, opts) => {
351
+ try {
352
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
353
+ const client = await getXClient();
354
+ const result = await client.searchTweets(query, { count: parseInt(opts.count) });
355
+ console.log(JSON.stringify(result, null, 2));
356
+ } catch (error) {
357
+ console.log(JSON.stringify({ error: error.message }));
358
+ process.exit(1);
359
+ }
360
+ });
361
+ program.command("profile").description("Get a user's X profile").argument("<handle>", "X handle (without @)").action(async (handle) => {
362
+ try {
363
+ const { getXClient } = await import("./x-client-J4GE5A7P.js");
364
+ const client = await getXClient();
365
+ const result = await client.getProfile(handle);
366
+ console.log(JSON.stringify(result, null, 2));
367
+ } catch (error) {
368
+ console.log(JSON.stringify({ error: error.message }));
369
+ process.exit(1);
370
+ }
371
+ });
372
+ program.command("credits").description("Check remaining posting credits").action(() => {
373
+ const config = loadConfig();
374
+ const remaining = config.credits.monthlyPostLimit - config.credits.postsUsedThisMonth;
375
+ console.log(JSON.stringify({
376
+ postsUsed: config.credits.postsUsedThisMonth,
377
+ postsRemaining: remaining,
378
+ monthlyLimit: config.credits.monthlyPostLimit,
379
+ percentUsed: Math.round(config.credits.postsUsedThisMonth / config.credits.monthlyPostLimit * 100),
380
+ resetDate: config.credits.resetDate
381
+ }, null, 2));
382
+ });
383
+ program.command("memory").description("Read memory (interactions, learnings, relationships)").argument("<type>", "interactions | learnings | relationships").option("-d, --date <date>", "For interactions: specific date (YYYY-MM-DD)").option("-c, --count <n>", "For interactions: count", "20").action(async (type, opts) => {
384
+ try {
385
+ const { getRecentInteractions, getInteractions, loadLearnings, loadRelationships } = await import("./memory-O3AJIKBX.js");
386
+ let data;
387
+ switch (type) {
388
+ case "interactions":
389
+ data = opts.date ? getInteractions(opts.date) : getRecentInteractions(parseInt(opts.count));
390
+ break;
391
+ case "learnings":
392
+ data = loadLearnings();
393
+ break;
394
+ case "relationships":
395
+ data = loadRelationships();
396
+ break;
397
+ default:
398
+ console.log(JSON.stringify({ error: "Type must be: interactions, learnings, or relationships" }));
399
+ process.exit(1);
400
+ }
401
+ console.log(JSON.stringify(data, null, 2));
402
+ } catch (error) {
403
+ console.log(JSON.stringify({ error: error.message }));
404
+ process.exit(1);
405
+ }
406
+ });
407
+ program.command("learn").description("Store a learning").argument("<content>", "What you learned").option("-t, --tags <tags...>", "Tags for categorization").action(async (content, opts) => {
408
+ try {
409
+ const { addLearning } = await import("./memory-O3AJIKBX.js");
410
+ addLearning(content, "agent", opts.tags ?? []);
411
+ console.log(JSON.stringify({ success: true }));
412
+ } catch (error) {
413
+ console.log(JSON.stringify({ error: error.message }));
414
+ process.exit(1);
415
+ }
416
+ });
417
+ program.command("note").description("Add a relationship note about someone").argument("<handle>", "Their X handle").argument("<content>", "Your note").action(async (handle, content) => {
418
+ try {
419
+ const { updateRelationship } = await import("./memory-O3AJIKBX.js");
420
+ updateRelationship(handle, { handle, notes: [content] });
421
+ console.log(JSON.stringify({ success: true, handle }));
422
+ } catch (error) {
423
+ console.log(JSON.stringify({ error: error.message }));
424
+ process.exit(1);
425
+ }
426
+ });
427
+ program.command("schedule").description("Queue a post for later").argument("<content>", "Tweet content").option("--at <datetime>", "ISO datetime to post at").action(async (content, opts) => {
428
+ try {
429
+ const { addToQueue } = await import("./queue-ELK5ZX7J.js");
430
+ const entry = addToQueue(content, opts.at);
431
+ console.log(JSON.stringify({ success: true, id: entry.id, scheduledFor: entry.scheduledFor }));
432
+ } catch (error) {
433
+ console.log(JSON.stringify({ error: error.message }));
434
+ process.exit(1);
435
+ }
436
+ });
437
+ program.command("flush").description("Post all queued items whose time has come").action(async () => {
438
+ try {
439
+ const { flushQueue } = await import("./queue-ELK5ZX7J.js");
440
+ const results = await flushQueue();
441
+ console.log(JSON.stringify(results, null, 2));
442
+ } catch (error) {
443
+ console.log(JSON.stringify({ error: error.message }));
444
+ process.exit(1);
445
+ }
446
+ });
447
+ program.command("queue").description("Show scheduled posts").action(async () => {
448
+ const { showQueue } = await import("./queue-ELK5ZX7J.js");
449
+ showQueue();
450
+ });
451
+ var colony = program.command("colony").description("Colony commands");
452
+ colony.command("checkin").description("Check into The Colony \u2014 sync memory, discover Spores").option("-m, --message <msg>", "Optional message to post").action(async (opts) => {
453
+ try {
454
+ const { colonyCheckin } = await import("./colony-J5KQIV6M.js");
455
+ const result = await colonyCheckin(opts.message);
456
+ console.log(JSON.stringify(result, null, 2));
457
+ } catch (error) {
458
+ console.log(JSON.stringify({ error: error.message }));
459
+ process.exit(1);
460
+ }
461
+ });
462
+ colony.command("memory").description("Read the Colony's shared memory").action(async () => {
463
+ try {
464
+ const { renderColonyBriefing } = await import("./memory-7FBE26K3.js");
465
+ console.log(renderColonyBriefing());
466
+ } catch (error) {
467
+ console.log(JSON.stringify({ error: error.message }));
468
+ process.exit(1);
469
+ }
470
+ });
471
+ colony.command("plans").description("Get all active Colony plans").action(async () => {
472
+ try {
473
+ const { getActivePlans } = await import("./colony-J5KQIV6M.js");
474
+ const plans = getActivePlans();
475
+ console.log(plans.length > 0 ? JSON.stringify(plans, null, 2) : JSON.stringify({ message: "No active plans. Propose one!" }));
476
+ } catch (error) {
477
+ console.log(JSON.stringify({ error: error.message }));
478
+ process.exit(1);
479
+ }
480
+ });
481
+ colony.command("propose").description("Propose a coordinated plan").argument("<description>", "What's the plan?").action(async (description) => {
482
+ try {
483
+ const { proposePlan } = await import("./colony-J5KQIV6M.js");
484
+ const result = await proposePlan(description);
485
+ console.log(JSON.stringify(result, null, 2));
486
+ } catch (error) {
487
+ console.log(JSON.stringify({ error: error.message }));
488
+ process.exit(1);
489
+ }
490
+ });
491
+ colony.command("join").description("Join an active plan").argument("<planId>", "Plan ID").action(async (planId) => {
492
+ try {
493
+ const { joinPlan } = await import("./colony-J5KQIV6M.js");
494
+ const result = await joinPlan(planId);
495
+ console.log(JSON.stringify(result, null, 2));
496
+ } catch (error) {
497
+ console.log(JSON.stringify({ error: error.message }));
498
+ process.exit(1);
499
+ }
500
+ });
501
+ colony.command("post-status").description("Post a status update to the Colony").argument("<status>", "Your status").action(async (status) => {
502
+ try {
503
+ const { postStatus } = await import("./colony-J5KQIV6M.js");
504
+ const result = await postStatus(status);
505
+ console.log(JSON.stringify(result, null, 2));
506
+ } catch (error) {
507
+ console.log(JSON.stringify({ error: error.message }));
508
+ process.exit(1);
509
+ }
510
+ });
511
+ colony.command("activity").description("Get today's Colony activity").action(async () => {
512
+ try {
513
+ const { getTodaysActivity } = await import("./colony-J5KQIV6M.js");
514
+ const activity = getTodaysActivity();
515
+ console.log(activity.length > 0 ? JSON.stringify(activity, null, 2) : JSON.stringify({ message: "No Colony activity today yet." }));
516
+ } catch (error) {
517
+ console.log(JSON.stringify({ error: error.message }));
518
+ process.exit(1);
519
+ }
520
+ });
521
+ program.command("start").description("Start the autonomous Spora agent").option("--interval <ms>", "Heartbeat interval in milliseconds").action(async (opts) => {
522
+ if (!sporaExists()) {
523
+ console.log(JSON.stringify({ error: "Spora not initialized. Run `spora init` first." }));
524
+ process.exit(1);
525
+ }
526
+ if (!hasXCredentials()) {
527
+ console.log(JSON.stringify({ error: "No X credentials. Run `spora init` to set up." }));
528
+ process.exit(1);
529
+ }
530
+ const { hasLLMKey } = await import("./llm-RDNC5Y3G.js");
531
+ if (!hasLLMKey()) {
532
+ console.log(JSON.stringify({ error: "No LLM API key. Run `spora set-llm-key` first." }));
533
+ process.exit(1);
534
+ }
535
+ if (opts.interval) {
536
+ const { loadConfig: lc, saveConfig: sc } = await import("./config-NZAFARS6.js");
537
+ const config = lc();
538
+ config.runtime = { ...config.runtime, heartbeatIntervalMs: parseInt(opts.interval, 10), actionsPerHeartbeat: config.runtime?.actionsPerHeartbeat ?? 3, enabled: true };
539
+ sc(config);
540
+ }
541
+ console.log(chalk.cyan(BANNER));
542
+ console.log(chalk.bold("Starting Spora agent...\n"));
543
+ const { startHeartbeatLoop } = await import("./heartbeat-J4JLYH2B.js");
544
+ await startHeartbeatLoop();
545
+ });
546
+ program.command("stop").description("Stop the running Spora agent").action(async () => {
547
+ const { getRunningPid, requestStop } = await import("./heartbeat-J4JLYH2B.js");
548
+ const pid = getRunningPid();
549
+ if (!pid) {
550
+ console.log(JSON.stringify({ message: "Spora agent is not running." }));
551
+ return;
552
+ }
553
+ requestStop();
554
+ console.log(JSON.stringify({ message: `Stop signal sent to PID ${pid}.` }));
555
+ });
556
+ program.command("set-llm-key").description("Set your Anthropic API key for the agent runtime").argument("[key]", "API key (or omit to enter interactively)").action(async (key) => {
557
+ const { writeFileSync } = await import("fs");
558
+ const { paths: p, ensureDirectories: ed } = await import("./paths-5GFUUHCZ.js");
559
+ ed();
560
+ let apiKey = key;
561
+ if (!apiKey) {
562
+ if (process.env.ANTHROPIC_API_KEY) {
563
+ console.log(JSON.stringify({ message: "Using ANTHROPIC_API_KEY from environment." }));
564
+ return;
565
+ }
566
+ const { input } = await import("@inquirer/prompts");
567
+ apiKey = await input({
568
+ message: "Enter your Anthropic API key:",
569
+ validate: (v) => v.startsWith("sk-") ? true : "Key should start with 'sk-'"
570
+ });
571
+ }
572
+ writeFileSync(p.llmKey, apiKey, { mode: 384 });
573
+ try {
574
+ const { loadConfig: lc, saveConfig: sc } = await import("./config-NZAFARS6.js");
575
+ const config = lc();
576
+ if (!config.llm) {
577
+ config.llm = { provider: "anthropic", model: "claude-sonnet-4-20250514" };
578
+ sc(config);
579
+ }
580
+ } catch {
581
+ }
582
+ console.log(JSON.stringify({ success: true, message: "LLM API key saved." }));
583
+ });
584
+ program.command("chat").description("Chat with your Spore locally").action(async () => {
585
+ if (!sporaExists()) {
586
+ console.log("Spora not initialized. Run `spora init` first.");
587
+ process.exit(1);
588
+ }
589
+ const { hasLLMKey } = await import("./llm-RDNC5Y3G.js");
590
+ if (!hasLLMKey()) {
591
+ console.log("No LLM API key. Run `spora set-llm-key` first.");
592
+ process.exit(1);
593
+ }
594
+ const { buildChatPrompt } = await import("./prompt-builder-WNMZ2QCN.js");
595
+ const { chat: chatLLM } = await import("./llm-RDNC5Y3G.js");
596
+ const { input } = await import("@inquirer/prompts");
597
+ const identity = loadIdentity();
598
+ console.log(chalk.cyan(BANNER));
599
+ console.log(chalk.bold(`Chatting with ${identity.name} (@${identity.handle})
600
+ `));
601
+ console.log(chalk.gray('Type "exit" to end the conversation.\n'));
602
+ const systemPrompt = buildChatPrompt();
603
+ const messages = [];
604
+ while (true) {
605
+ const userInput = await input({ message: chalk.blue("You:") });
606
+ if (userInput.toLowerCase() === "exit") {
607
+ console.log(chalk.gray("\nGoodbye!\n"));
608
+ break;
609
+ }
610
+ messages.push({ role: "user", content: userInput });
611
+ try {
612
+ const response = await chatLLM(systemPrompt, messages);
613
+ messages.push({ role: "assistant", content: response.content });
614
+ console.log(chalk.green(`
615
+ ${identity.name}: `) + response.content + "\n");
616
+ } catch (error) {
617
+ console.log(chalk.red(`Error: ${error.message}
618
+ `));
619
+ }
620
+ }
621
+ });
622
+ program.command("agent-status").description("Check if the Spora agent is running").action(async () => {
623
+ const { getRunningPid } = await import("./heartbeat-J4JLYH2B.js");
624
+ const pid = getRunningPid();
625
+ const { hasLLMKey } = await import("./llm-RDNC5Y3G.js");
626
+ console.log(JSON.stringify({
627
+ agentRunning: pid !== null,
628
+ pid,
629
+ llmKeyConfigured: hasLLMKey(),
630
+ initialized: sporaExists(),
631
+ hasCredentials: hasXCredentials()
632
+ }));
633
+ });
634
+ program.command("ui").description("Open the Spora web UI for setup and management").option("-p, --port <port>", "Port to run on", "3000").action(async (opts) => {
635
+ const { resolve } = await import("path");
636
+ const { existsSync: fsExists } = await import("fs");
637
+ const { execSync, spawn } = await import("child_process");
638
+ const webDir = resolve(import.meta.dirname, "../../packages/web");
639
+ if (!fsExists(webDir)) {
640
+ console.log(chalk.red("Web UI not found. Expected at: " + webDir));
641
+ process.exit(1);
642
+ }
643
+ const port = opts.port || "3000";
644
+ console.log(chalk.cyan(BANNER));
645
+ console.log(chalk.bold("Starting Spora UI...\n"));
646
+ if (!fsExists(resolve(webDir, "node_modules"))) {
647
+ console.log(chalk.gray("Installing dependencies..."));
648
+ execSync("npm install", { cwd: webDir, stdio: "inherit" });
649
+ }
650
+ console.log(chalk.green(`
651
+ Spora UI: http://localhost:${port}
652
+ `));
653
+ console.log(chalk.gray("Press Ctrl+C to stop.\n"));
654
+ try {
655
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
656
+ execSync(`${openCmd} http://localhost:${port}`, { stdio: "ignore" });
657
+ } catch {
658
+ }
659
+ const child = spawn("npx", ["next", "dev", "-p", port], {
660
+ cwd: webDir,
661
+ stdio: "inherit",
662
+ env: { ...process.env }
663
+ });
664
+ child.on("close", (code) => {
665
+ process.exit(code ?? 0);
666
+ });
667
+ process.on("SIGINT", () => {
668
+ child.kill("SIGINT");
669
+ });
670
+ process.on("SIGTERM", () => {
671
+ child.kill("SIGTERM");
672
+ });
673
+ });
674
+ program.parse();
675
+ //# sourceMappingURL=cli.js.map