hyperstack-core 1.0.0 → 1.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 CHANGED
@@ -91,6 +91,10 @@ const blockers = await builder.tools.hs_blockers({ slug: "deploy-prod" });
91
91
  # Initialize with multi-agent template
92
92
  npx hyperstack-core init openclaw-multiagent
93
93
 
94
+ # Auto-extract cards from text (zero LLM cost)
95
+ npx hyperstack-core ingest project.txt
96
+ echo "We use Next.js 14 and PostgreSQL" | npx hyperstack-core ingest -
97
+
94
98
  # Store cards
95
99
  npx hyperstack-core store --slug "use-clerk" --title "Use Clerk" --type decision
96
100
 
@@ -176,12 +176,79 @@ function createOpenClawAdapter(opts = {}) {
176
176
  cards,
177
177
  };
178
178
  },
179
+
180
+ /**
181
+ * Auto-extract cards from raw text (conversation, project description).
182
+ * Finds preferences, people, decisions, tech stack mentions.
183
+ * Zero LLM cost. Best for onboarding.
184
+ */
185
+ async hs_ingest({ text }) {
186
+ if (!text || text.trim().length === 0) {
187
+ return { text: "Error: text parameter required", cards: [] };
188
+ }
189
+
190
+ const result = await client.ingest(text);
191
+ const cards = result.cards || [];
192
+
193
+ return {
194
+ text: `✅ Created ${cards.length} cards from ${text.length} chars of text:\n` +
195
+ cards.map(c => ` [${c.slug}] ${c.title} (${c.cardType})`).join("\n"),
196
+ cards,
197
+ count: cards.length,
198
+ };
199
+ },
179
200
  },
180
201
 
181
202
  /**
182
203
  * Hook: called when agent session starts.
183
204
  * Registers the agent and loads relevant context.
184
205
  */
206
+ /**
207
+ * Check for cards directed at this agent from other agents.
208
+ * Enables cross-agent coordination via shared memory.
209
+ */
210
+ async hs_inbox({ since } = {}) {
211
+ const result = await client.inbox({ since });
212
+ const cards = result.cards || [];
213
+ if (!cards.length) {
214
+ return { text: `No messages for agent "${agentId}".`, cards: [] };
215
+ }
216
+ return {
217
+ text: `${cards.length} message(s) for "${agentId}":\n` +
218
+ cards.map(c => ` [${c.slug}] ${c.title}${c.body ? `\n ${c.body.slice(0, 150)}` : ""}`).join("\n"),
219
+ cards,
220
+ };
221
+ },
222
+
223
+ /**
224
+ * Register a webhook for real-time notifications. Requires Team plan.
225
+ */
226
+ async hs_webhook({ url, events, secret } = {}) {
227
+ if (!url) return { text: "Error: url required" };
228
+ const result = await client.registerWebhook({
229
+ url,
230
+ events: events ? events.split(",").map(e => e.trim()) : ["*"],
231
+ secret,
232
+ });
233
+ if (result.error) return { text: `Webhook error: ${result.error}` };
234
+ return { text: `Webhook registered for agent "${agentId}".\nURL: ${url}\nID: ${result.id}` };
235
+ },
236
+
237
+ /**
238
+ * List all registered webhooks for this account.
239
+ */
240
+ async hs_webhooks() {
241
+ const result = await client.listWebhooks();
242
+ const hooks = result.webhooks || [];
243
+ if (!hooks.length) return { text: "No webhooks registered." };
244
+ return {
245
+ text: `${hooks.length} webhook(s):\n` +
246
+ hooks.map(h => ` [${h.agentId}] ${h.url} — events: ${h.events.join(", ")}`).join("\n"),
247
+ hooks,
248
+ };
249
+ },
250
+ },
251
+
185
252
  async onSessionStart({ agentName, agentRole }) {
186
253
  // Register this agent in the graph
187
254
  await client.registerAgent({
package/cli.js CHANGED
@@ -42,6 +42,7 @@ Commands:
42
42
  decide Record a decision (use --slug, --title, --rationale)
43
43
  blockers <slug> Show what blocks a card
44
44
  graph <slug> Traverse graph from a card
45
+ ingest <file|text> Auto-extract cards from text (zero LLM cost)
45
46
  list List all cards
46
47
 
47
48
  Templates:
@@ -61,6 +62,8 @@ Examples:
61
62
  npx hyperstack-core store --slug "use-clerk" --title "Use Clerk for auth" --type decision
62
63
  npx hyperstack-core blockers deploy-prod
63
64
  npx hyperstack-core graph auth-api --depth 2
65
+ npx hyperstack-core ingest project.txt
66
+ echo "We use Next.js 14 and PostgreSQL" | npx hyperstack-core ingest -
64
67
  `);
65
68
  }
66
69
 
@@ -480,6 +483,47 @@ async function run() {
480
483
  return;
481
484
  }
482
485
 
486
+ if (command === "ingest") {
487
+ let text = "";
488
+ const input = args[1];
489
+
490
+ if (!input) {
491
+ console.error("Usage: npx hyperstack-core ingest <file|text|->");
492
+ console.error(" file Read from file");
493
+ console.error(" - Read from stdin");
494
+ console.error(" text Treat argument as raw text");
495
+ process.exit(1);
496
+ }
497
+
498
+ if (input === "-") {
499
+ // Read from stdin
500
+ const chunks = [];
501
+ process.stdin.on("data", chunk => chunks.push(chunk));
502
+ await new Promise(resolve => process.stdin.on("end", resolve));
503
+ text = Buffer.concat(chunks).toString("utf-8");
504
+ } else if (existsSync(input)) {
505
+ // Read from file
506
+ text = readFileSync(input, "utf-8");
507
+ } else {
508
+ // Treat as raw text
509
+ text = input;
510
+ }
511
+
512
+ if (!text || text.trim().length === 0) {
513
+ console.error("Error: No text provided");
514
+ process.exit(1);
515
+ }
516
+
517
+ console.log(`Ingesting ${text.length} chars...`);
518
+ const result = await client.ingest(text);
519
+ const cards = result.cards || [];
520
+ console.log(`\n✅ Created ${cards.length} cards:\n`);
521
+ for (const c of cards) {
522
+ console.log(` [${c.slug}] ${c.title} (${c.cardType || "general"})`);
523
+ }
524
+ return;
525
+ }
526
+
483
527
  if (command === "list") {
484
528
  const result = await client.list();
485
529
  console.log(`HyperStack: ${result.count ?? 0}/${result.limit ?? "?"} cards (plan: ${result.plan || "?"})\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperstack-core",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Typed graph memory for AI agents. Replace GOALS.md with queryable cards + relations. Works with OpenClaw, Claude Desktop, Cursor.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/src/client.js CHANGED
@@ -129,6 +129,17 @@ class HyperStackClient {
129
129
  return this._request("DELETE", `/api/cards?workspace=${this.workspace}&id=${slug}`);
130
130
  }
131
131
 
132
+ /**
133
+ * Auto-extract cards from raw text.
134
+ * Finds preferences, people, decisions, tech stack mentions using regex.
135
+ * Zero LLM cost. Great for onboarding.
136
+ * @param {string} text — raw conversation or project description
137
+ * @returns {Promise<{cards: Array, count: number}>}
138
+ */
139
+ async ingest(text) {
140
+ return this._request("POST", `/api/ingest?workspace=${this.workspace}`, { text });
141
+ }
142
+
132
143
  // ─── Graph ───────────────────────────────────────────
133
144
 
134
145
  /**
@@ -242,6 +253,48 @@ class HyperStackClient {
242
253
  * @param {string} agent.role — what this agent does
243
254
  * @param {string[]} [agent.owns] — slugs this agent owns
244
255
  */
256
+ /**
257
+ * Check for cards directed at this agent by other agents.
258
+ * Enables cross-agent coordination via shared memory.
259
+ * @param {object} [opts]
260
+ * @param {string} [opts.since] — ISO timestamp, only cards after this time
261
+ * @returns {Promise<{cards: Array}>}
262
+ */
263
+ async inbox(opts = {}) {
264
+ const agentId = this.agentId;
265
+ if (!agentId) throw new Error("agentId required for inbox");
266
+ let url = `/api/cards?workspace=${this.workspace}&targetAgent=${agentId}`;
267
+ if (opts.since) url += `&since=${encodeURIComponent(opts.since)}`;
268
+ return this._request("GET", url);
269
+ }
270
+
271
+ /**
272
+ * Register a webhook to receive real-time notifications.
273
+ * Agent gets notified when cards are directed at it. Requires Team plan.
274
+ * @param {object} webhook
275
+ * @param {string} webhook.url — URL to receive POST events
276
+ * @param {string[]} [webhook.events] — events to listen for (default: all)
277
+ * @param {string} [webhook.secret] — HMAC secret for signature verification
278
+ * @returns {Promise<{id: string}>}
279
+ */
280
+ async registerWebhook(webhook) {
281
+ return this._request("POST", `/api/agent-webhooks`, {
282
+ agentId: this.agentId,
283
+ url: webhook.url,
284
+ events: webhook.events || ["*"],
285
+ workspace: this.workspace,
286
+ secret: webhook.secret || undefined,
287
+ });
288
+ }
289
+
290
+ /**
291
+ * List all registered webhooks for this account.
292
+ * @returns {Promise<{webhooks: Array}>}
293
+ */
294
+ async listWebhooks() {
295
+ return this._request("GET", `/api/agent-webhooks`);
296
+ }
297
+
245
298
  async registerAgent(agent) {
246
299
  const links = [];
247
300
  if (agent.owns) {