hyperstack-core 1.5.1 → 1.5.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.
Files changed (2) hide show
  1. package/cli.js +112 -1
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  /**
4
4
  * hyperstack-core CLI
@@ -37,6 +37,7 @@ hyperstack-core — Typed graph memory for AI agents
37
37
  Commands:
38
38
  login Authenticate via browser (OAuth device flow)
39
39
  logout Remove saved credentials
40
+ quickstart Scan current dir, ingest docs, show graph — 90 seconds to value
40
41
  init <template> Initialize a project with a template
41
42
  ingest <path> Auto-parse files into cards + edges (markdown/text/logs)
42
43
  search <query> Search the knowledge graph
@@ -387,6 +388,116 @@ async function run() {
387
388
  return;
388
389
  }
389
390
 
391
+ // ─── Quickstart command ─────────────────────────────
392
+
393
+ if (command === "quickstart") {
394
+ console.log("\n🧠 HyperStack Quickstart\n");
395
+
396
+ // 1. Auth check
397
+ const qsApiKey = getApiKey();
398
+ if (!qsApiKey) {
399
+ console.log(" Not authenticated.\n");
400
+ console.log(" Option A: npx hyperstack-core login");
401
+ console.log(" Option B: export HYPERSTACK_API_KEY=hs_your_key");
402
+ console.log(" Get a free key: https://cascadeai.dev/hyperstack\n");
403
+ process.exit(1);
404
+ }
405
+ console.log(" ✅ Authenticated\n");
406
+
407
+ // 2. Scan current directory for ingestable files
408
+ const cwd = process.cwd();
409
+ const scanDirs = [cwd];
410
+ const docsDir = join(cwd, "docs");
411
+ if (existsSync(docsDir) && statSync(docsDir).isDirectory()) scanDirs.push(docsDir);
412
+
413
+ const found = [];
414
+ for (const dir of scanDirs) {
415
+ const entries = readdirSync(dir).filter((f) => /\.(md|txt)$/i.test(f));
416
+ for (const f of entries) {
417
+ const full = join(dir, f);
418
+ const stat = statSync(full);
419
+ const rel = dir === cwd ? f : `docs/${f}`;
420
+ found.push({ path: full, name: rel, size: stat.size });
421
+ }
422
+ }
423
+
424
+ if (!found.length) {
425
+ console.log(" No .md or .txt files found in current directory.\n");
426
+ console.log(" Try: npx hyperstack-core ingest <file>");
427
+ process.exit(0);
428
+ }
429
+
430
+ console.log(" Found files:\n");
431
+ for (const f of found) {
432
+ const kb = (f.size / 1024).toFixed(1);
433
+ console.log(` 📄 ${f.name} (${kb} KB)`);
434
+ }
435
+
436
+ const workspace = getFlag("workspace", "default");
437
+ console.log(`\n Workspace: ${workspace}`);
438
+ console.log(` Files: ${found.length}\n`);
439
+
440
+ // 3. Confirm (skip with --yes)
441
+ if (!args.includes("--yes") && !args.includes("-y")) {
442
+ const { createInterface } = await import("readline");
443
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
444
+ const answer = await new Promise((res) => {
445
+ rl.question(" Ingest these files? [Y/n] ", (a) => { rl.close(); res(a.trim().toLowerCase()); });
446
+ });
447
+ if (answer === "n" || answer === "no") {
448
+ console.log("\n Cancelled.\n");
449
+ return;
450
+ }
451
+ }
452
+
453
+ // 4. Parse all files
454
+ console.log("\n Parsing...\n");
455
+ let allCards = [];
456
+ let totalEdges = 0;
457
+
458
+ for (const f of found) {
459
+ const content = readFileSync(f.path, "utf-8");
460
+ const prefix = slugify(f.name.replace(/\.[^.]+$/, ""));
461
+ const result = parse(content, { prefix });
462
+ allCards.push(...result.cards);
463
+ totalEdges += result.edgeCount;
464
+ console.log(` 📄 ${f.name} → ${result.cards.length} cards, ${result.edgeCount} edges (${result.format})`);
465
+ }
466
+
467
+ if (!allCards.length) {
468
+ console.log("\n No cards extracted. Files may be empty.\n");
469
+ return;
470
+ }
471
+
472
+ // 5. Store
473
+ console.log(`\n Storing ${allCards.length} cards...`);
474
+ const qsClient = new HyperStackClient({ apiKey: qsApiKey, workspace });
475
+
476
+ const result = await qsClient.ingest(allCards, {
477
+ onProgress: () => process.stdout.write("."),
478
+ });
479
+
480
+ console.log("\n");
481
+ if (result.failed) {
482
+ console.log(` ✅ ${result.stored} cards stored, ${result.failed} failed`);
483
+ for (const e of result.errors) console.log(` ❌ [${e.slug}] ${e.error}`);
484
+ } else {
485
+ console.log(` ✅ ${result.stored} cards stored, ${totalEdges} edges from ${found.length} file(s)`);
486
+ }
487
+
488
+ // 6. Next steps
489
+ const firstSlug = allCards[0]?.slug || "";
490
+ const searchTerm = allCards[0]?.title?.split(" ").slice(0, 3).join(" ") || "getting started";
491
+ console.log(`
492
+ Next steps:
493
+
494
+ npx hyperstack-core search "${searchTerm}"
495
+ npx hyperstack-core graph ${firstSlug} --depth 2 --reverse
496
+ npx hyperstack-core list
497
+ `);
498
+ return;
499
+ }
500
+
390
501
  // ─── Ingest command ─────────────────────────────────
391
502
 
392
503
  if (command === "ingest") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperstack-core",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
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",