@beastmode-develeap/beastmode 0.1.12 → 0.1.14

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/dist/index.js CHANGED
@@ -3462,7 +3462,9 @@ function createStrategySession(factoryDir, projectName, opts) {
3462
3462
  })),
3463
3463
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3464
3464
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3465
- buildOnSession: opts.buildOnSession
3465
+ buildOnSession: opts.buildOnSession,
3466
+ sessionType: opts.sessionType || "free-form",
3467
+ approach: opts.approach || "auto"
3466
3468
  };
3467
3469
  writeFileSync10(join11(sessionDir, "inception.json"), JSON.stringify(session, null, 2) + "\n");
3468
3470
  return session;
@@ -4352,30 +4354,62 @@ ${formatted}
4352
4354
  ---
4353
4355
  `;
4354
4356
  }
4355
- const prompt = `IMPORTANT CONTEXT \u2014 READ BEFORE RESPONDING:
4356
- You are the BeastMode chat assistant. You have ALL the data you need below. Do NOT use curl, do NOT reference Monday.com, do NOT ask for permissions. The task board data is provided inline.
4357
+ const boardApiUrl = getBoardUrl(session.factoryPath);
4358
+ const boardParam = scope !== "factory" ? `?board=${scope}` : "";
4359
+ const prompt = `CRITICAL CONTEXT \u2014 YOU MUST READ THIS:
4360
+ You are the BeastMode chat assistant running inside a BeastMode factory.
4361
+
4362
+ IMPORTANT RULES:
4363
+ - Monday.com does NOT exist in this system. NEVER mention Monday.com. There is NO Monday.com integration.
4364
+ - This factory uses a LOCAL self-hosted BeastMode Board with a REST API.
4365
+ - You have tools available. You can execute bash commands. Use curl to interact with the board API.
4366
+ - NEVER ask for permissions. You have full access. Just do it.
4357
4367
  ${scopeContext}
4368
+ ${session.sessionTypePrompt ? "\nSESSION ROLE:\n" + session.sessionTypePrompt + "\n" : ""}
4369
+ BOARD API (use curl to manage tasks):
4370
+ - Base URL: ${boardApiUrl}
4371
+ - List items: curl -s '${boardApiUrl}/api/items${boardParam}'
4372
+ - Create item: curl -s -X POST '${boardApiUrl}/api/items${boardParam}' -H 'Content-Type: application/json' -d '{"name":"Task title","status":"","priority":"Medium","task_type":"deep-planning"}'
4373
+ (status="" means Draft/New. Valid statuses: "", "New", "Ready", "Working on it", "Done", etc.)
4374
+ - Update item: curl -s -X PATCH '${boardApiUrl}/api/items/ITEM_ID${boardParam}' -H 'Content-Type: application/json' -d '{"status":"Ready"}'
4358
4375
 
4359
- This factory uses a LOCAL self-hosted BeastMode Board (NOT Monday.com). All task data is below.
4376
+ DECISION API (use curl to log strategic decisions):
4377
+ - List: curl -s '${boardApiUrl}/api/strategy/${scope}/decisions'
4378
+ - Create: curl -s -X POST '${boardApiUrl}/api/strategy/${scope}/decisions' -H 'Content-Type: application/json' -d '{"title":"Decision title","context":"Why this was decided"}'
4379
+ - Update: curl -s -X PATCH '${boardApiUrl}/api/strategy/${scope}/decisions/ID' -H 'Content-Type: application/json' -d '{"status":"superseded","outcome":"What happened"}'
4380
+
4381
+ Current board items:
4360
4382
  ${boardContext}
4361
4383
 
4362
4384
  Factory path: ${session.factoryPath}
4363
4385
  ${historyBlock}
4364
4386
  USER MESSAGE: ${content}
4365
4387
 
4366
- Respond concisely. Continue the conversation naturally \u2014 you have full context of what was discussed above.`;
4367
- const args = [
4388
+ Respond concisely. Continue the conversation naturally.`;
4389
+ const isRoot = process.getuid?.() === 0;
4390
+ const claudeArgs = [
4368
4391
  "-p",
4369
4392
  prompt,
4370
4393
  "--output-format",
4371
4394
  "stream-json",
4372
- "--verbose"
4395
+ "--verbose",
4396
+ "--dangerously-skip-permissions"
4373
4397
  ];
4374
- const child = spawn("claude", args, {
4398
+ let spawnCmd;
4399
+ let spawnArgs;
4400
+ if (isRoot) {
4401
+ spawnCmd = "runuser";
4402
+ spawnArgs = ["-u", "node", "--", "claude", ...claudeArgs];
4403
+ } else {
4404
+ spawnCmd = "claude";
4405
+ spawnArgs = claudeArgs;
4406
+ }
4407
+ const child = spawn(spawnCmd, spawnArgs, {
4375
4408
  cwd: session.factoryPath,
4376
4409
  env: {
4377
4410
  ...process.env,
4378
- BEASTMODE_FACTORY_PATH: session.factoryPath
4411
+ BEASTMODE_FACTORY_PATH: session.factoryPath,
4412
+ ...isRoot ? { HOME: "/home/node" } : {}
4379
4413
  },
4380
4414
  stdio: ["ignore", "pipe", "pipe"]
4381
4415
  });
@@ -4406,9 +4440,6 @@ Respond concisely. Continue the conversation naturally \u2014 you have full cont
4406
4440
  const transformed = transformCliEvent(parsed);
4407
4441
  if (transformed) sendToClient(session, transformed);
4408
4442
  } catch {
4409
- if (line.trim() && !line.startsWith("\u256D") && !line.startsWith("\u2570") && !line.startsWith("\u2502") && line.length > 2) {
4410
- sendToClient(session, { type: "text", content: line });
4411
- }
4412
4443
  }
4413
4444
  }
4414
4445
  });
@@ -5861,7 +5892,7 @@ Path: ${projConfig.path}
5861
5892
  method: "POST",
5862
5893
  pattern: "/api/strategy/:project/sessions",
5863
5894
  handler: async (body, params) => {
5864
- const { name, idea, methodology, interactionMode, buildOnSession } = body;
5895
+ const { name, idea, methodology, interactionMode, buildOnSession, sessionType, approach } = body;
5865
5896
  if (!name || !idea || !methodology) throw new Error("Missing: name, idea, methodology");
5866
5897
  const { createStrategySession: createStrategySession2 } = await Promise.resolve().then(() => (init_strategy(), strategy_exports));
5867
5898
  return createStrategySession2(factoryDir, params.project, {
@@ -5869,7 +5900,9 @@ Path: ${projConfig.path}
5869
5900
  idea,
5870
5901
  methodology,
5871
5902
  interactionMode: interactionMode || "interactive",
5872
- buildOnSession
5903
+ buildOnSession,
5904
+ sessionType: sessionType || "free-form",
5905
+ approach: approach || "auto"
5873
5906
  });
5874
5907
  }
5875
5908
  },
@@ -5890,6 +5923,145 @@ Path: ${projConfig.path}
5890
5923
  if (!content) throw new Error(`Artifact not found: ${params.artifact}`);
5891
5924
  return { content };
5892
5925
  }
5926
+ },
5927
+ // ── Strategy Feed (aggregated activity) ──
5928
+ {
5929
+ method: "GET",
5930
+ pattern: "/api/strategy/:project/feed",
5931
+ handler: async (_body, params, query) => {
5932
+ const project = params.project;
5933
+ const limit = Math.min(Math.max(parseInt(query?.limit || "20", 10) || 20, 1), 50);
5934
+ const cursor = query?.cursor || null;
5935
+ const allItems = [];
5936
+ try {
5937
+ const { listStrategySessions: listStrategySessions2 } = await Promise.resolve().then(() => (init_strategy(), strategy_exports));
5938
+ const sessions = listStrategySessions2(factoryDir, project);
5939
+ for (const s of sessions) {
5940
+ const phasesDone = s.phases ? s.phases.filter((p) => p.status === "done").length : 0;
5941
+ const phasesTotal = s.phases ? s.phases.length : 0;
5942
+ const typeLabel = s.sessionType || "free-form";
5943
+ const methodLabel = s.methodology || "";
5944
+ let subtitle = `${typeLabel} (${methodLabel})`;
5945
+ if (phasesTotal > 0) {
5946
+ subtitle += ` \u2014 phase ${phasesDone}/${phasesTotal}`;
5947
+ }
5948
+ allItems.push({
5949
+ type: "session",
5950
+ title: s.name,
5951
+ subtitle,
5952
+ timestamp: s.updatedAt || s.createdAt,
5953
+ link: `#/strategy-session/${s.sessionId}`,
5954
+ id: `session-${s.sessionId}`
5955
+ });
5956
+ }
5957
+ } catch {
5958
+ }
5959
+ try {
5960
+ const boardUrl = getBoardUrl2(factoryDir);
5961
+ const resp = await proxyToBoard(boardUrl, "GET", `/api/strategy/${project}/decisions`);
5962
+ const decisions = Array.isArray(resp) ? resp : resp?.decisions || [];
5963
+ for (const d of decisions) {
5964
+ allItems.push({
5965
+ type: "decision",
5966
+ title: String(d.title || "Untitled decision"),
5967
+ subtitle: `Status: ${d.status || "active"}`,
5968
+ timestamp: String(d.updated_at || d.created_at || (/* @__PURE__ */ new Date()).toISOString()),
5969
+ id: `decision-${d.id}`
5970
+ });
5971
+ }
5972
+ } catch {
5973
+ }
5974
+ try {
5975
+ const boardUrl = getBoardUrl2(factoryDir);
5976
+ const resp = await proxyToBoard(boardUrl, "GET", "/api/items", void 0, { board: project });
5977
+ const tasks = Array.isArray(resp) ? resp : resp?.items || [];
5978
+ for (const t of tasks) {
5979
+ const parts = [];
5980
+ if (t.status) parts.push(String(t.status));
5981
+ if (t.priority) parts.push(`P${t.priority}`);
5982
+ if (t.task_type) parts.push(String(t.task_type));
5983
+ allItems.push({
5984
+ type: "task",
5985
+ title: String(t.name || "Untitled task"),
5986
+ subtitle: parts.join(" / ") || void 0,
5987
+ timestamp: String(t.updated_at || t.created_at || (/* @__PURE__ */ new Date()).toISOString()),
5988
+ link: "#/board",
5989
+ id: `task-${t.id}`
5990
+ });
5991
+ }
5992
+ } catch {
5993
+ }
5994
+ try {
5995
+ const { buildArtifactIndex: buildArtifactIndex2 } = await Promise.resolve().then(() => (init_strategy(), strategy_exports));
5996
+ const index = buildArtifactIndex2(factoryDir, project);
5997
+ const learnings = index.artifacts.filter((a) => a.type === "learning");
5998
+ for (const l of learnings) {
5999
+ allItems.push({
6000
+ type: "learning",
6001
+ title: l.summary,
6002
+ timestamp: l.date,
6003
+ id: `learning-${l.path}`
6004
+ });
6005
+ }
6006
+ } catch {
6007
+ }
6008
+ try {
6009
+ const runsDir = join13(factoryDir, "runs", project);
6010
+ if (existsSync15(runsDir)) {
6011
+ const runDirs = readdirSync6(runsDir).filter((d) => d.startsWith("run-"));
6012
+ for (const runId of runDirs) {
6013
+ const ckptPath = join13(runsDir, runId, "checkpoint.json");
6014
+ let subtitle = "";
6015
+ let timestamp = "";
6016
+ try {
6017
+ const st = statSync5(join13(runsDir, runId));
6018
+ timestamp = st.mtime.toISOString();
6019
+ } catch {
6020
+ timestamp = (/* @__PURE__ */ new Date()).toISOString();
6021
+ }
6022
+ if (existsSync15(ckptPath)) {
6023
+ try {
6024
+ const ckpt = JSON.parse(readFileSync12(ckptPath, "utf-8"));
6025
+ const history = ckpt.satisfaction_history;
6026
+ if (history && history.length > 0) {
6027
+ const latest = history[history.length - 1];
6028
+ subtitle = `Satisfaction: ${Math.round(latest * 100)}%`;
6029
+ }
6030
+ } catch {
6031
+ }
6032
+ }
6033
+ allItems.push({
6034
+ type: "run",
6035
+ title: runId,
6036
+ subtitle: subtitle || void 0,
6037
+ timestamp,
6038
+ link: "#/runs",
6039
+ id: `run-${runId}`
6040
+ });
6041
+ }
6042
+ }
6043
+ } catch {
6044
+ }
6045
+ allItems.sort((a, b) => {
6046
+ const ta = new Date(a.timestamp).getTime() || 0;
6047
+ const tb = new Date(b.timestamp).getTime() || 0;
6048
+ return tb - ta;
6049
+ });
6050
+ let startIndex = 0;
6051
+ if (cursor) {
6052
+ const cursorIdx = allItems.findIndex((item) => item.id === cursor);
6053
+ if (cursorIdx >= 0) {
6054
+ startIndex = cursorIdx + 1;
6055
+ }
6056
+ }
6057
+ const page = allItems.slice(startIndex, startIndex + limit);
6058
+ const nextCursor = page.length === limit && startIndex + limit < allItems.length ? page[page.length - 1].id : null;
6059
+ return {
6060
+ items: page,
6061
+ total: allItems.length,
6062
+ nextCursor
6063
+ };
6064
+ }
5893
6065
  }
5894
6066
  ];
5895
6067
  }