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.
- package/cli.js +112 -1
- 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