@beastmode-develeap/beastmode 0.1.13 → 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 +166 -10
- package/dist/index.js.map +1 -1
- package/dist/methodologies/design-thinking.json +47 -0
- package/dist/methodologies/superpowers.json +7 -0
- package/dist/web/board.html +194 -85
- package/package.json +1 -1
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;
|
|
@@ -4363,7 +4365,7 @@ IMPORTANT RULES:
|
|
|
4363
4365
|
- You have tools available. You can execute bash commands. Use curl to interact with the board API.
|
|
4364
4366
|
- NEVER ask for permissions. You have full access. Just do it.
|
|
4365
4367
|
${scopeContext}
|
|
4366
|
-
|
|
4368
|
+
${session.sessionTypePrompt ? "\nSESSION ROLE:\n" + session.sessionTypePrompt + "\n" : ""}
|
|
4367
4369
|
BOARD API (use curl to manage tasks):
|
|
4368
4370
|
- Base URL: ${boardApiUrl}
|
|
4369
4371
|
- List items: curl -s '${boardApiUrl}/api/items${boardParam}'
|
|
@@ -4371,6 +4373,11 @@ BOARD API (use curl to manage tasks):
|
|
|
4371
4373
|
(status="" means Draft/New. Valid statuses: "", "New", "Ready", "Working on it", "Done", etc.)
|
|
4372
4374
|
- Update item: curl -s -X PATCH '${boardApiUrl}/api/items/ITEM_ID${boardParam}' -H 'Content-Type: application/json' -d '{"status":"Ready"}'
|
|
4373
4375
|
|
|
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
|
+
|
|
4374
4381
|
Current board items:
|
|
4375
4382
|
${boardContext}
|
|
4376
4383
|
|
|
@@ -4379,7 +4386,8 @@ ${historyBlock}
|
|
|
4379
4386
|
USER MESSAGE: ${content}
|
|
4380
4387
|
|
|
4381
4388
|
Respond concisely. Continue the conversation naturally.`;
|
|
4382
|
-
const
|
|
4389
|
+
const isRoot = process.getuid?.() === 0;
|
|
4390
|
+
const claudeArgs = [
|
|
4383
4391
|
"-p",
|
|
4384
4392
|
prompt,
|
|
4385
4393
|
"--output-format",
|
|
@@ -4387,11 +4395,21 @@ Respond concisely. Continue the conversation naturally.`;
|
|
|
4387
4395
|
"--verbose",
|
|
4388
4396
|
"--dangerously-skip-permissions"
|
|
4389
4397
|
];
|
|
4390
|
-
|
|
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, {
|
|
4391
4408
|
cwd: session.factoryPath,
|
|
4392
4409
|
env: {
|
|
4393
4410
|
...process.env,
|
|
4394
|
-
BEASTMODE_FACTORY_PATH: session.factoryPath
|
|
4411
|
+
BEASTMODE_FACTORY_PATH: session.factoryPath,
|
|
4412
|
+
...isRoot ? { HOME: "/home/node" } : {}
|
|
4395
4413
|
},
|
|
4396
4414
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4397
4415
|
});
|
|
@@ -4422,9 +4440,6 @@ Respond concisely. Continue the conversation naturally.`;
|
|
|
4422
4440
|
const transformed = transformCliEvent(parsed);
|
|
4423
4441
|
if (transformed) sendToClient(session, transformed);
|
|
4424
4442
|
} catch {
|
|
4425
|
-
if (line.trim() && !line.startsWith("\u256D") && !line.startsWith("\u2570") && !line.startsWith("\u2502") && line.length > 2) {
|
|
4426
|
-
sendToClient(session, { type: "text", content: line });
|
|
4427
|
-
}
|
|
4428
4443
|
}
|
|
4429
4444
|
}
|
|
4430
4445
|
});
|
|
@@ -5877,7 +5892,7 @@ Path: ${projConfig.path}
|
|
|
5877
5892
|
method: "POST",
|
|
5878
5893
|
pattern: "/api/strategy/:project/sessions",
|
|
5879
5894
|
handler: async (body, params) => {
|
|
5880
|
-
const { name, idea, methodology, interactionMode, buildOnSession } = body;
|
|
5895
|
+
const { name, idea, methodology, interactionMode, buildOnSession, sessionType, approach } = body;
|
|
5881
5896
|
if (!name || !idea || !methodology) throw new Error("Missing: name, idea, methodology");
|
|
5882
5897
|
const { createStrategySession: createStrategySession2 } = await Promise.resolve().then(() => (init_strategy(), strategy_exports));
|
|
5883
5898
|
return createStrategySession2(factoryDir, params.project, {
|
|
@@ -5885,7 +5900,9 @@ Path: ${projConfig.path}
|
|
|
5885
5900
|
idea,
|
|
5886
5901
|
methodology,
|
|
5887
5902
|
interactionMode: interactionMode || "interactive",
|
|
5888
|
-
buildOnSession
|
|
5903
|
+
buildOnSession,
|
|
5904
|
+
sessionType: sessionType || "free-form",
|
|
5905
|
+
approach: approach || "auto"
|
|
5889
5906
|
});
|
|
5890
5907
|
}
|
|
5891
5908
|
},
|
|
@@ -5906,6 +5923,145 @@ Path: ${projConfig.path}
|
|
|
5906
5923
|
if (!content) throw new Error(`Artifact not found: ${params.artifact}`);
|
|
5907
5924
|
return { content };
|
|
5908
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
|
+
}
|
|
5909
6065
|
}
|
|
5910
6066
|
];
|
|
5911
6067
|
}
|