hyperstack-core 1.0.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.
@@ -0,0 +1,214 @@
1
+ /**
2
+ * example-openclaw-multiagent.js
3
+ *
4
+ * Complete example: 3 OpenClaw agents coordinating via HyperStack
5
+ * instead of GOALS.md + DECISIONS.md files.
6
+ *
7
+ * Run: HYPERSTACK_API_KEY=hs_your_key node example-openclaw-multiagent.js
8
+ */
9
+
10
+ import { HyperStackClient } from "hyperstack-core";
11
+
12
+ const hs = new HyperStackClient({ workspace: "my-project" });
13
+
14
+ // ─── Step 1: Register your agents ──────────────────────
15
+
16
+ async function setup() {
17
+ // Register each agent as a card in the graph
18
+ await hs.registerAgent({
19
+ id: "coordinator",
20
+ name: "Coordinator",
21
+ role: "Routes tasks, monitors blockers, prevents duplicate work",
22
+ });
23
+
24
+ await hs.registerAgent({
25
+ id: "researcher",
26
+ name: "Research Agent",
27
+ role: "Investigates technical questions, stores findings",
28
+ owns: ["project-root"],
29
+ });
30
+
31
+ await hs.registerAgent({
32
+ id: "builder",
33
+ name: "Builder Agent",
34
+ role: "Implements code, records architecture decisions",
35
+ });
36
+
37
+ // Create project root
38
+ await hs.store({
39
+ slug: "project-root",
40
+ title: "SaaS Dashboard Project",
41
+ body: "Next.js 14 + Clerk auth + Neon PostgreSQL. Deploying to Vercel.",
42
+ cardType: "project",
43
+ stack: "projects",
44
+ keywords: ["project", "saas", "dashboard", "nextjs"],
45
+ });
46
+
47
+ console.log("✅ Setup complete: 3 agents + project root");
48
+ }
49
+
50
+ // ─── Step 2: Researcher investigates auth options ──────
51
+
52
+ async function researcherFlow() {
53
+ const researcher = new HyperStackClient({
54
+ workspace: "my-project",
55
+ agentId: "researcher",
56
+ });
57
+
58
+ // Check if someone already researched this
59
+ const existing = await researcher.search("authentication options");
60
+ if (existing.results?.length) {
61
+ console.log("📋 Already researched:", existing.results[0].title);
62
+ return; // No duplicate work!
63
+ }
64
+
65
+ // Store findings
66
+ await researcher.store({
67
+ slug: "finding-clerk-pricing",
68
+ title: "Clerk pricing: free to 10K MAU",
69
+ body: "Clerk offers free tier up to 10,000 monthly active users. After that $0.02/MAU. Auth0 starts charging at 7,500 MAU at $23/mo. For our expected 5K users, Clerk is free vs Auth0 $23/mo.",
70
+ cardType: "event",
71
+ stack: "general",
72
+ keywords: ["clerk", "auth0", "pricing", "authentication"],
73
+ links: [{ target: "project-root", relation: "related" }],
74
+ });
75
+
76
+ await researcher.store({
77
+ slug: "finding-nextauth-deprecated",
78
+ title: "NextAuth.js → Auth.js migration ongoing",
79
+ body: "NextAuth.js is being renamed to Auth.js. Migration guide exists but community reports rough edges with App Router. Not recommended for new projects right now.",
80
+ cardType: "event",
81
+ stack: "general",
82
+ keywords: ["nextauth", "authjs", "migration", "risk"],
83
+ links: [{ target: "project-root", relation: "related" }],
84
+ });
85
+
86
+ console.log("✅ Researcher stored 2 findings");
87
+ }
88
+
89
+ // ─── Step 3: Builder makes a decision based on research ─
90
+
91
+ async function builderFlow() {
92
+ const builder = new HyperStackClient({
93
+ workspace: "my-project",
94
+ agentId: "builder",
95
+ });
96
+
97
+ // Search for research findings before deciding
98
+ const research = await builder.search("authentication pricing comparison");
99
+ console.log(`📋 Builder found ${research.results?.length || 0} relevant cards`);
100
+
101
+ // Make and record a decision
102
+ await builder.decide({
103
+ slug: "decision-use-clerk",
104
+ title: "Use Clerk for authentication",
105
+ body: "Based on research: Clerk free at our scale (<10K MAU), better Next.js integration than Auth0, and NextAuth.js has migration risks. Clerk is the clear choice.",
106
+ decidedBy: "agent-builder",
107
+ affects: ["project-root"],
108
+ keywords: ["clerk", "auth", "decision"],
109
+ });
110
+
111
+ // Record the implementation task
112
+ await builder.store({
113
+ slug: "task-clerk-integration",
114
+ title: "Integrate Clerk with Next.js App Router",
115
+ body: "Install @clerk/nextjs, configure middleware, add sign-in/sign-up pages, protect API routes.",
116
+ cardType: "workflow",
117
+ stack: "workflows",
118
+ keywords: ["clerk", "integration", "nextjs", "task"],
119
+ links: [
120
+ { target: "decision-use-clerk", relation: "depends_on" },
121
+ { target: "agent-builder", relation: "assigned_to" },
122
+ ],
123
+ });
124
+
125
+ // Record a blocker
126
+ await builder.store({
127
+ slug: "blocker-env-vars",
128
+ title: "Need Clerk API keys from team lead",
129
+ body: "Cannot proceed with Clerk integration until CLERK_SECRET_KEY and NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY are provided.",
130
+ cardType: "event",
131
+ stack: "general",
132
+ keywords: ["blocker", "clerk", "env", "keys"],
133
+ links: [
134
+ { target: "task-clerk-integration", relation: "blocks" },
135
+ ],
136
+ });
137
+
138
+ console.log("✅ Builder: decision recorded, task created, blocker flagged");
139
+ }
140
+
141
+ // ─── Step 4: Coordinator checks status ─────────────────
142
+
143
+ async function coordinatorFlow() {
144
+ const coordinator = new HyperStackClient({
145
+ workspace: "my-project",
146
+ agentId: "coordinator",
147
+ });
148
+
149
+ // What blocks the Clerk integration?
150
+ const blockers = await coordinator.blockers("task-clerk-integration");
151
+ console.log(`\n🚧 Blockers for Clerk integration: ${blockers.blockers?.length || 0}`);
152
+ for (const b of blockers.blockers || []) {
153
+ console.log(` [${b.slug}] ${b.title}`);
154
+ }
155
+
156
+ // Full graph from project root
157
+ try {
158
+ const graph = await coordinator.graph("project-root", { depth: 2 });
159
+ console.log(`\n📊 Project graph: ${graph.nodes?.length || 0} nodes, ${graph.edges?.length || 0} edges`);
160
+ for (const e of graph.edges || []) {
161
+ console.log(` ${e.from} --${e.relation}--> ${e.to}`);
162
+ }
163
+ } catch (err) {
164
+ // Graph traversal needs Pro plan
165
+ if (err.status === 403) {
166
+ console.log("\n📊 Graph traversal requires Pro plan. Using search fallback.");
167
+ const all = await coordinator.list();
168
+ console.log(` Total cards: ${all.count}`);
169
+ }
170
+ }
171
+
172
+ // Check: has anyone already done research on databases?
173
+ const dbResearch = await coordinator.search("database postgresql selection");
174
+ if (!dbResearch.results?.length) {
175
+ console.log("\n⚠️ No database research found. Assigning to researcher.");
176
+ await coordinator.store({
177
+ slug: "task-research-database",
178
+ title: "Research database options (Neon vs Supabase vs PlanetScale)",
179
+ body: "Need comparison of managed PostgreSQL options for our Next.js project. Consider: pricing, connection pooling, branching, edge compatibility.",
180
+ cardType: "workflow",
181
+ stack: "workflows",
182
+ keywords: ["database", "research", "neon", "supabase", "planetscale"],
183
+ links: [
184
+ { target: "agent-researcher", relation: "assigned_to" },
185
+ { target: "project-root", relation: "related" },
186
+ ],
187
+ });
188
+ }
189
+ }
190
+
191
+ // ─── Run the full flow ─────────────────────────────────
192
+
193
+ async function main() {
194
+ console.log("🃏 HyperStack Multi-Agent Demo\n");
195
+
196
+ await setup();
197
+ console.log();
198
+
199
+ await researcherFlow();
200
+ console.log();
201
+
202
+ await builderFlow();
203
+ console.log();
204
+
205
+ await coordinatorFlow();
206
+
207
+ console.log("\n✅ Demo complete. All agents share one typed graph.");
208
+ console.log(" No DECISIONS.md. No GOALS.md. Just queryable cards + relations.");
209
+ }
210
+
211
+ main().catch(err => {
212
+ console.error("Error:", err.message);
213
+ process.exit(1);
214
+ });
package/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * hyperstack-core
3
+ *
4
+ * Typed graph memory for AI agents.
5
+ *
6
+ * Usage:
7
+ * import { HyperStackClient } from "hyperstack-core";
8
+ * const hs = new HyperStackClient({ apiKey: "hs_..." });
9
+ * await hs.store({ slug: "use-clerk", title: "Use Clerk for auth", cardType: "decision" });
10
+ * await hs.blockers("deploy-prod"); // → typed blockers
11
+ *
12
+ * OpenClaw:
13
+ * import { createOpenClawAdapter } from "hyperstack-core/adapters/openclaw";
14
+ * const adapter = createOpenClawAdapter({ agentId: "researcher" });
15
+ */
16
+
17
+ export { HyperStackClient } from "./src/client.js";
18
+ export { createOpenClawAdapter } from "./adapters/openclaw.js";
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "hyperstack-core",
3
+ "version": "1.0.0",
4
+ "description": "Typed graph memory for AI agents. Replace GOALS.md with queryable cards + relations. Works with OpenClaw, Claude Desktop, Cursor.",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "hyperstack-core": "./cli.js"
9
+ },
10
+ "exports": {
11
+ ".": "./index.js",
12
+ "./client": "./src/client.js",
13
+ "./adapters/openclaw": "./adapters/openclaw.js"
14
+ },
15
+ "files": [
16
+ "index.js",
17
+ "cli.js",
18
+ "src/",
19
+ "adapters/",
20
+ "templates/",
21
+ "examples/",
22
+ "SKILL.md",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "keywords": [
27
+ "hyperstack",
28
+ "knowledge-graph",
29
+ "ai-agents",
30
+ "memory",
31
+ "openclaw",
32
+ "multi-agent",
33
+ "mcp",
34
+ "typed-relations",
35
+ "graph-memory",
36
+ "agent-coordination"
37
+ ],
38
+ "author": "CascadeAI <deeq.yaqub1@gmail.com>",
39
+ "license": "MIT",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/deeqyaqub1-cmd/hyperstack-core"
43
+ },
44
+ "homepage": "https://cascadeai.dev/hyperstack",
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "dependencies": {},
49
+ "devDependencies": {}
50
+ }
package/src/client.js ADDED
@@ -0,0 +1,267 @@
1
+ /**
2
+ * hyperstack-core — HyperStack SDK for typed graph memory
3
+ *
4
+ * Lightweight client for the HyperStack API. Works in Node.js 18+.
5
+ * No dependencies. Used by the OpenClaw adapter and CLI.
6
+ */
7
+
8
+ const DEFAULT_BASE = "https://hyperstack-cloud.vercel.app";
9
+
10
+ // ESM-compatible sync file reading
11
+ import { readFileSync, existsSync } from "fs";
12
+ import { join } from "path";
13
+ import { homedir } from "os";
14
+
15
+ function loadCredApiKey() {
16
+ try {
17
+ const credFile = join(homedir(), ".hyperstack", "credentials.json");
18
+ if (existsSync(credFile)) {
19
+ const creds = JSON.parse(readFileSync(credFile, "utf-8"));
20
+ if (creds.api_key) return creds.api_key;
21
+ }
22
+ } catch {}
23
+ return "";
24
+ }
25
+
26
+ class HyperStackClient {
27
+ /**
28
+ * @param {object} opts
29
+ * @param {string} opts.apiKey — HyperStack API key (hs_...)
30
+ * @param {string} [opts.workspace="default"] — workspace slug
31
+ * @param {string} [opts.baseUrl] — API base URL
32
+ * @param {string} [opts.agentId] — agent identifier for multi-agent setups
33
+ */
34
+ constructor(opts = {}) {
35
+ this.apiKey = opts.apiKey || process.env.HYPERSTACK_API_KEY || loadCredApiKey();
36
+ this.workspace = opts.workspace || process.env.HYPERSTACK_WORKSPACE || "default";
37
+ this.baseUrl = opts.baseUrl || process.env.HYPERSTACK_BASE_URL || DEFAULT_BASE;
38
+ this.agentId = opts.agentId || null;
39
+
40
+ if (!this.apiKey) {
41
+ throw new Error(
42
+ "HYPERSTACK_API_KEY required.\n" +
43
+ "Run: npx hyperstack-core login\n" +
44
+ "Or: export HYPERSTACK_API_KEY=hs_your_key\n" +
45
+ "Get a free account: https://cascadeai.dev/hyperstack"
46
+ );
47
+ }
48
+ }
49
+
50
+ /** @private */
51
+ async _request(method, path, body = null) {
52
+ const url = `${this.baseUrl}${path}`;
53
+ const opts = {
54
+ method,
55
+ headers: {
56
+ "X-API-Key": this.apiKey,
57
+ "Content-Type": "application/json",
58
+ "User-Agent": "hyperstack-core/1.0.0",
59
+ },
60
+ };
61
+ if (body) opts.body = JSON.stringify(body);
62
+
63
+ const res = await fetch(url, opts);
64
+ const data = await res.json();
65
+ if (!res.ok) {
66
+ const err = new Error(data.error || `HTTP ${res.status}`);
67
+ err.status = res.status;
68
+ err.body = data;
69
+ throw err;
70
+ }
71
+ return data;
72
+ }
73
+
74
+ // ─── Cards ───────────────────────────────────────────
75
+
76
+ /**
77
+ * Create or update a card (upsert by slug).
78
+ * @param {object} card
79
+ * @param {string} card.slug — unique identifier
80
+ * @param {string} card.title — short title
81
+ * @param {string} [card.body] — description (2-5 sentences)
82
+ * @param {string} [card.cardType] — person|project|decision|preference|workflow|event|general
83
+ * @param {string} [card.stack] — projects|people|decisions|preferences|workflows|general
84
+ * @param {string[]} [card.keywords] — search terms
85
+ * @param {Array<{target: string, relation: string}>} [card.links] — typed relations
86
+ * @param {object} [card.meta] — freeform metadata
87
+ * @returns {Promise<{slug: string, updated: boolean}>}
88
+ */
89
+ async store(card) {
90
+ if (!card.slug) throw new Error("card.slug required");
91
+ if (!card.title) throw new Error("card.title required");
92
+
93
+ // Auto-tag with agentId if set
94
+ if (this.agentId) {
95
+ card.meta = card.meta || {};
96
+ card.meta.agentId = this.agentId;
97
+ card.keywords = card.keywords || [];
98
+ if (!card.keywords.includes(`agent:${this.agentId}`)) {
99
+ card.keywords.push(`agent:${this.agentId}`);
100
+ }
101
+ }
102
+
103
+ return this._request("POST", `/api/cards?workspace=${this.workspace}`, card);
104
+ }
105
+
106
+ /**
107
+ * Search cards by query (hybrid semantic + keyword).
108
+ * @param {string} query
109
+ * @returns {Promise<{results: Array}>}
110
+ */
111
+ async search(query) {
112
+ return this._request("GET", `/api/search?workspace=${this.workspace}&q=${encodeURIComponent(query)}`);
113
+ }
114
+
115
+ /**
116
+ * List all cards in workspace.
117
+ * @returns {Promise<{cards: Array, count: number, plan: string}>}
118
+ */
119
+ async list() {
120
+ return this._request("GET", `/api/cards?workspace=${this.workspace}`);
121
+ }
122
+
123
+ /**
124
+ * Delete a card by slug.
125
+ * @param {string} slug
126
+ * @returns {Promise<{deleted: boolean}>}
127
+ */
128
+ async delete(slug) {
129
+ return this._request("DELETE", `/api/cards?workspace=${this.workspace}&id=${slug}`);
130
+ }
131
+
132
+ // ─── Graph ───────────────────────────────────────────
133
+
134
+ /**
135
+ * Traverse the knowledge graph from a starting card.
136
+ * @param {string} from — starting card slug
137
+ * @param {object} [opts]
138
+ * @param {number} [opts.depth=1] — hops to traverse (1-3)
139
+ * @param {string} [opts.relation] — filter by relation type
140
+ * @param {string} [opts.type] — filter by card type
141
+ * @param {string} [opts.at] — ISO timestamp for time-travel
142
+ * @returns {Promise<{nodes: Array, edges: Array}>}
143
+ */
144
+ async graph(from, opts = {}) {
145
+ let url = `/api/graph?workspace=${this.workspace}&from=${from}`;
146
+ if (opts.depth) url += `&depth=${opts.depth}`;
147
+ if (opts.relation) url += `&relation=${opts.relation}`;
148
+ if (opts.type) url += `&type=${opts.type}`;
149
+ if (opts.at) url += `&at=${encodeURIComponent(opts.at)}`;
150
+ return this._request("GET", url);
151
+ }
152
+
153
+ // ─── Multi-Agent Helpers ──────────────────────────────
154
+
155
+ /**
156
+ * Query cards that block a specific card/task.
157
+ * Shorthand for graph traversal with relation="blocks".
158
+ * @param {string} slug — card being blocked
159
+ * @returns {Promise<{blockers: Array}>}
160
+ */
161
+ async blockers(slug) {
162
+ try {
163
+ const result = await this.graph(slug, { depth: 2, relation: "blocks" });
164
+ const blockers = (result.edges || [])
165
+ .filter(e => e.relation === "blocks" && e.to === slug)
166
+ .map(e => {
167
+ const node = (result.nodes || []).find(n => n.slug === e.from);
168
+ return node || { slug: e.from };
169
+ });
170
+ return { blockers, graph: result };
171
+ } catch (err) {
172
+ // If graph API not available (free tier), fallback to search
173
+ if (err.status === 403) {
174
+ const searchResult = await this.search(`blocks ${slug}`);
175
+ return {
176
+ blockers: (searchResult.results || []).filter(c =>
177
+ c.links?.some(l => l.relation === "blocks" && l.target === slug)
178
+ ),
179
+ fallback: true,
180
+ };
181
+ }
182
+ throw err;
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Find cards owned by a specific agent.
188
+ * @param {string} [agentId] — defaults to this.agentId
189
+ * @returns {Promise<{cards: Array}>}
190
+ */
191
+ async agentCards(agentId) {
192
+ const id = agentId || this.agentId;
193
+ if (!id) throw new Error("agentId required");
194
+ return this.search(`agent:${id}`);
195
+ }
196
+
197
+ /**
198
+ * Record a decision with full provenance.
199
+ * Creates a decision card + links to who decided and what it affects.
200
+ * @param {object} decision
201
+ * @param {string} decision.slug
202
+ * @param {string} decision.title
203
+ * @param {string} decision.body — rationale
204
+ * @param {string} [decision.decidedBy] — agent/person slug
205
+ * @param {string[]} [decision.affects] — slugs of affected cards
206
+ * @param {string[]} [decision.blocks] — slugs of things this blocks
207
+ * @param {object} [decision.meta]
208
+ */
209
+ async decide(decision) {
210
+ const links = [];
211
+ if (decision.decidedBy) {
212
+ links.push({ target: decision.decidedBy, relation: "decided" });
213
+ }
214
+ if (decision.affects) {
215
+ for (const a of decision.affects) {
216
+ links.push({ target: a, relation: "triggers" });
217
+ }
218
+ }
219
+ if (decision.blocks) {
220
+ for (const b of decision.blocks) {
221
+ links.push({ target: b, relation: "blocks" });
222
+ }
223
+ }
224
+
225
+ return this.store({
226
+ slug: decision.slug,
227
+ title: decision.title,
228
+ body: decision.body,
229
+ cardType: "decision",
230
+ stack: "decisions",
231
+ links,
232
+ meta: { ...decision.meta, decidedAt: new Date().toISOString() },
233
+ keywords: decision.keywords || [],
234
+ });
235
+ }
236
+
237
+ /**
238
+ * Register an agent as a card in the graph.
239
+ * @param {object} agent
240
+ * @param {string} agent.id — unique agent ID
241
+ * @param {string} agent.name — display name
242
+ * @param {string} agent.role — what this agent does
243
+ * @param {string[]} [agent.owns] — slugs this agent owns
244
+ */
245
+ async registerAgent(agent) {
246
+ const links = [];
247
+ if (agent.owns) {
248
+ for (const o of agent.owns) {
249
+ links.push({ target: o, relation: "owns" });
250
+ }
251
+ }
252
+
253
+ return this.store({
254
+ slug: `agent-${agent.id}`,
255
+ title: `Agent: ${agent.name}`,
256
+ body: agent.role,
257
+ cardType: "person",
258
+ stack: "people",
259
+ links,
260
+ keywords: ["agent", agent.id, agent.name],
261
+ meta: { agentId: agent.id, registeredAt: new Date().toISOString() },
262
+ });
263
+ }
264
+ }
265
+
266
+ export { HyperStackClient };
267
+ export default HyperStackClient;
@@ -0,0 +1,98 @@
1
+ {
2
+ "name": "openclaw-multiagent",
3
+ "description": "Pre-configured typed cards and relations for OpenClaw multi-agent coordination. Replaces GOALS.md + DECISIONS.md with structured, queryable graph memory.",
4
+ "version": "1.0.0",
5
+
6
+ "cardTypes": {
7
+ "task": {
8
+ "description": "A unit of work assigned to an agent",
9
+ "fields": ["status", "priority", "assignee", "deadline"],
10
+ "statuses": ["todo", "in-progress", "blocked", "done", "cancelled"]
11
+ },
12
+ "decision": {
13
+ "description": "A choice made by an agent with rationale",
14
+ "fields": ["rationale", "alternatives", "decidedBy", "decidedAt"]
15
+ },
16
+ "blocker": {
17
+ "description": "Something preventing progress on a task",
18
+ "fields": ["severity", "resolvedBy", "resolvedAt"]
19
+ },
20
+ "goal": {
21
+ "description": "A high-level objective (replaces GOALS.md)",
22
+ "fields": ["deadline", "progress", "owner"]
23
+ },
24
+ "agent": {
25
+ "description": "An agent registration card",
26
+ "fields": ["role", "model", "capabilities"]
27
+ },
28
+ "context": {
29
+ "description": "Shared context that multiple agents need",
30
+ "fields": ["scope", "expiresAt"]
31
+ }
32
+ },
33
+
34
+ "relationTypes": {
35
+ "owns": "Agent/person owns a task or project",
36
+ "assigned_to": "Task is assigned to an agent",
37
+ "blocks": "This card blocks another card",
38
+ "blocked_by": "This card is blocked by another card",
39
+ "depends_on": "This card depends on another card",
40
+ "decided": "Agent/person made this decision",
41
+ "triggers": "Change to this card triggers effects on target",
42
+ "subtask_of": "This task is a subtask of a larger task",
43
+ "related": "General association"
44
+ },
45
+
46
+ "rules": [
47
+ {
48
+ "name": "auto-blocked",
49
+ "description": "If a task has incoming 'blocks' relations, mark it as blocked",
50
+ "when": "card.cardType === 'task' && card.incomingLinks.some(l => l.relation === 'blocks')",
51
+ "suggest": "Consider updating task status to 'blocked'"
52
+ },
53
+ {
54
+ "name": "unowned-task",
55
+ "description": "Tasks without an owner should be flagged",
56
+ "when": "card.cardType === 'task' && !card.links.some(l => l.relation === 'assigned_to')",
57
+ "suggest": "This task has no assigned agent"
58
+ },
59
+ {
60
+ "name": "decision-needs-rationale",
61
+ "description": "Decisions should include rationale",
62
+ "when": "card.cardType === 'decision' && (!card.body || card.body.length < 20)",
63
+ "suggest": "Decision lacks rationale — add a body explaining why"
64
+ }
65
+ ],
66
+
67
+ "starterCards": [
68
+ {
69
+ "slug": "project-root",
70
+ "title": "Project Root",
71
+ "body": "Root node for the project knowledge graph. All agents, goals, and major decisions link here.",
72
+ "cardType": "project",
73
+ "stack": "projects",
74
+ "keywords": ["project", "root", "main"]
75
+ }
76
+ ],
77
+
78
+ "agentSetup": {
79
+ "description": "Recommended agent configuration for multi-agent OpenClaw + HyperStack",
80
+ "agents": {
81
+ "coordinator": {
82
+ "role": "Routes tasks, monitors blockers, ensures no duplicate work",
83
+ "model": "claude-sonnet-4-20250514",
84
+ "tools": ["hs_search", "hs_blockers", "hs_graph", "hs_decide"]
85
+ },
86
+ "researcher": {
87
+ "role": "Investigates questions, stores findings as context cards",
88
+ "model": "claude-sonnet-4-20250514",
89
+ "tools": ["hs_search", "hs_store", "hs_my_cards"]
90
+ },
91
+ "builder": {
92
+ "role": "Implements code, records technical decisions",
93
+ "model": "claude-sonnet-4-20250514",
94
+ "tools": ["hs_search", "hs_store", "hs_decide", "hs_blockers"]
95
+ }
96
+ }
97
+ }
98
+ }