@productbrain/mcp 0.0.1-beta.43 → 0.0.1-beta.44
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/{chunk-PQP27A3R.js → chunk-E6ZTRA6N.js} +2 -2
- package/dist/{chunk-AWDD44CN.js → chunk-FKOP3WC7.js} +413 -280
- package/dist/chunk-FKOP3WC7.js.map +1 -0
- package/dist/{chunk-BIDMZOLE.js → chunk-MRIO53BY.js} +1 -1
- package/dist/{chunk-BIDMZOLE.js.map → chunk-MRIO53BY.js.map} +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/http.js +3 -3
- package/dist/index.js +3 -3
- package/dist/{setup-QT5KCX5Q.js → setup-LSCFKMW7.js} +2 -2
- package/dist/{smart-capture-W2IALMJ5.js → smart-capture-3BWER4RY.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-AWDD44CN.js.map +0 -1
- /package/dist/{chunk-PQP27A3R.js.map → chunk-E6ZTRA6N.js.map} +0 -0
- /package/dist/{setup-QT5KCX5Q.js.map → setup-LSCFKMW7.js.map} +0 -0
- /package/dist/{smart-capture-W2IALMJ5.js.map → smart-capture-3BWER4RY.js.map} +0 -0
|
@@ -25,11 +25,11 @@ import {
|
|
|
25
25
|
startAgentSession,
|
|
26
26
|
trackWriteTool,
|
|
27
27
|
translateStaleToolNames
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-E6ZTRA6N.js";
|
|
29
29
|
import {
|
|
30
30
|
trackQualityCheck,
|
|
31
31
|
trackQualityVerdict
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-MRIO53BY.js";
|
|
33
33
|
|
|
34
34
|
// src/server.ts
|
|
35
35
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -203,7 +203,7 @@ ${formatted}` }]
|
|
|
203
203
|
},
|
|
204
204
|
async ({ entryId }) => {
|
|
205
205
|
requireWriteAccess();
|
|
206
|
-
const { runContradictionCheck } = await import("./smart-capture-
|
|
206
|
+
const { runContradictionCheck } = await import("./smart-capture-3BWER4RY.js");
|
|
207
207
|
const entry = await mcpQuery("chain.getEntry", { entryId });
|
|
208
208
|
if (!entry) {
|
|
209
209
|
return {
|
|
@@ -3724,10 +3724,11 @@ function computeCommitBlockers(opts) {
|
|
|
3724
3724
|
}
|
|
3725
3725
|
for (const draft of sessionDrafts) {
|
|
3726
3726
|
if (!draft.name || draft.name.trim().length === 0) {
|
|
3727
|
+
const draftRef = draft.entryId && draft.entryId.trim().length > 0 ? draft.entryId : "<draftId>";
|
|
3727
3728
|
blockers.push({
|
|
3728
3729
|
entryId: betEntryId,
|
|
3729
3730
|
blocker: `Draft constellation entry is missing a name`,
|
|
3730
|
-
fix: `update-entry entryId="
|
|
3731
|
+
fix: `update-entry entryId="${draftRef}" name="<name>"`
|
|
3731
3732
|
});
|
|
3732
3733
|
}
|
|
3733
3734
|
}
|
|
@@ -3778,7 +3779,7 @@ var captureItemSchema = z12.object({
|
|
|
3778
3779
|
});
|
|
3779
3780
|
var facilitateSchema = z12.object({
|
|
3780
3781
|
action: z12.enum(FACILITATE_ACTIONS).describe(
|
|
3781
|
-
"'start':
|
|
3782
|
+
"'start': begin shaping session context (entry created on first respond). 'respond': process user input, score, capture, return coaching. 'score': return current scorecard without advancing. 'resume': reconstruct session from existing bet entry. 'commit-constellation': atomically commit a bet and all its linked draft entries in one call. Requires betEntryId."
|
|
3782
3783
|
),
|
|
3783
3784
|
sessionType: z12.enum(["shape"]).default("shape").optional().describe("Session type. Only 'shape' in v1."),
|
|
3784
3785
|
betEntryId: z12.string().optional().describe("Bet entry ID. Required for resume/score. For respond: omit on first call after start (entry created automatically); required for subsequent calls."),
|
|
@@ -3797,7 +3798,7 @@ function registerFacilitateTools(server) {
|
|
|
3797
3798
|
"facilitate",
|
|
3798
3799
|
{
|
|
3799
3800
|
title: "Facilitate \u2014 Coached Shaping",
|
|
3800
|
-
description: "Server-controlled coached shaping session with real-time Chain capture.\n\n- **start**:
|
|
3801
|
+
description: "Server-controlled coached shaping session with real-time Chain capture.\n\n- **start**: Begin a coached session context only. No bet entry is created yet.\n- **first respond**: Call respond with `betName` to create the draft bet and capture initial problem context.\n- **respond**: Process user input \u2014 scores against 7 shaping rubrics (problem, appetite, elements, architecture, risks, boundaries, done-when), searches for overlap, captures to Chain, returns structured coaching response with phase tracking.\n- **score**: Return the current scorecard without advancing the session.\n- **resume**: Reconstruct session state from an existing bet entry on the Chain.\n- **commit-constellation**: Atomically commit a bet and all its linked draft entries (features, tensions, decisions) in one call. Validates strategy link and required fields before committing anything. Replaces 9-13 sequential commit-entry calls.\n\nThe structured response separates judgment (server) from coaching (agent) per DEC-56. Read the phase, scorecard, and coaching fields to determine what to say next. When captureReady is true, buildContract is auto-generated from Chain governance.",
|
|
3801
3802
|
inputSchema: facilitateSchema,
|
|
3802
3803
|
annotations: {
|
|
3803
3804
|
readOnlyHint: false,
|
|
@@ -3989,8 +3990,12 @@ async function loadConstellationState(betEntryId, betInternalId) {
|
|
|
3989
3990
|
const elementCount = relations.filter(
|
|
3990
3991
|
(r) => r.type === "part_of" && r.toId === internalId
|
|
3991
3992
|
).length;
|
|
3992
|
-
const riskCount = relations.filter(
|
|
3993
|
-
|
|
3993
|
+
const riskCount = relations.filter(
|
|
3994
|
+
(r) => r.type === "constrains" && r.toId === internalId
|
|
3995
|
+
).length;
|
|
3996
|
+
const decisionCount = relations.filter(
|
|
3997
|
+
(r) => r.type === "informs" && r.toId === internalId
|
|
3998
|
+
).length;
|
|
3994
3999
|
return { relations, elementCount, riskCount, decisionCount };
|
|
3995
4000
|
} catch {
|
|
3996
4001
|
return { relations: [], elementCount: 0, riskCount: 0, decisionCount: 0 };
|
|
@@ -4173,208 +4178,166 @@ async function handleStart2(args) {
|
|
|
4173
4178
|
structuredContent: response
|
|
4174
4179
|
};
|
|
4175
4180
|
}
|
|
4176
|
-
async function
|
|
4177
|
-
|
|
4178
|
-
const { userInput, betEntryId: argBetId, dimension: argDimension, capture: rawCapture, source: argSource, betName: argBetName } = args;
|
|
4179
|
-
const source = argSource ?? "user";
|
|
4180
|
-
const captureItems = rawCapture ? Array.isArray(rawCapture) ? rawCapture : [rawCapture] : [];
|
|
4181
|
-
if (!userInput) {
|
|
4182
|
-
return {
|
|
4183
|
-
content: [{ type: "text", text: "`userInput` is required for respond action." }]
|
|
4184
|
-
};
|
|
4185
|
-
}
|
|
4181
|
+
async function processCaptures(opts) {
|
|
4182
|
+
const { captureItems, betEntryId, betDocId, betData } = opts;
|
|
4186
4183
|
const captureErrors = [];
|
|
4187
4184
|
const entriesCreated = [];
|
|
4188
4185
|
let relationsCreated = 0;
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4186
|
+
const capAgentId = getAgentSessionId();
|
|
4187
|
+
const collectionMap = {
|
|
4188
|
+
element: { slug: "features", dataField: "description", relationType: "part_of" },
|
|
4189
|
+
risk: { slug: "tensions", dataField: "description", relationType: "constrains" },
|
|
4190
|
+
decision: { slug: "decisions", dataField: "rationale", relationType: "informs" }
|
|
4191
|
+
};
|
|
4192
|
+
let runningBetData = { ...betData };
|
|
4193
|
+
for (const item of captureItems) {
|
|
4194
|
+
if (item.type === "noGo") {
|
|
4195
|
+
const updatedNoGos = appendNoGo(
|
|
4196
|
+
runningBetData.noGos,
|
|
4197
|
+
{ title: item.name, explanation: item.description }
|
|
4198
|
+
);
|
|
4199
|
+
runningBetData.noGos = updatedNoGos;
|
|
4200
|
+
try {
|
|
4201
|
+
await mcpMutation("chain.updateEntry", {
|
|
4202
|
+
entryId: betEntryId,
|
|
4203
|
+
data: { noGos: updatedNoGos },
|
|
4204
|
+
changeNote: `Added no-go: ${item.name}`
|
|
4205
|
+
});
|
|
4206
|
+
await recordSessionActivity({ entryModified: betDocId });
|
|
4207
|
+
} catch (updErr) {
|
|
4208
|
+
captureErrors.push({
|
|
4209
|
+
operation: "update",
|
|
4210
|
+
detail: `noGos field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
|
|
4211
|
+
});
|
|
4212
|
+
}
|
|
4213
|
+
continue;
|
|
4214
|
+
}
|
|
4215
|
+
const mapping = collectionMap[item.type];
|
|
4216
|
+
if (!mapping) continue;
|
|
4217
|
+
let capturedEntryId = null;
|
|
4193
4218
|
try {
|
|
4194
4219
|
const result = await mcpMutation(
|
|
4195
4220
|
"chain.createEntry",
|
|
4196
4221
|
{
|
|
4197
|
-
collectionSlug:
|
|
4198
|
-
name:
|
|
4222
|
+
collectionSlug: mapping.slug,
|
|
4223
|
+
name: item.name,
|
|
4199
4224
|
status: "draft",
|
|
4200
|
-
data: {
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
elements: "",
|
|
4204
|
-
rabbitHoles: "",
|
|
4205
|
-
noGos: "",
|
|
4206
|
-
architecture: "",
|
|
4207
|
-
buildContract: "",
|
|
4208
|
-
description: `Shaping session for: ${betName2}`,
|
|
4209
|
-
status: "shaping",
|
|
4210
|
-
shapingSessionActive: true
|
|
4211
|
-
},
|
|
4212
|
-
createdBy: agentId ? `agent:${agentId}` : "facilitate",
|
|
4213
|
-
sessionId: agentId ?? void 0
|
|
4225
|
+
data: { [mapping.dataField]: item.description },
|
|
4226
|
+
createdBy: capAgentId ? `agent:${capAgentId}` : "facilitate",
|
|
4227
|
+
sessionId: capAgentId ?? void 0
|
|
4214
4228
|
}
|
|
4215
4229
|
);
|
|
4216
|
-
|
|
4230
|
+
capturedEntryId = result.entryId;
|
|
4231
|
+
entriesCreated.push(result.entryId);
|
|
4217
4232
|
await recordSessionActivity({ entryCreated: result.docId });
|
|
4218
|
-
} catch (
|
|
4219
|
-
const msg =
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
const [betEntry, constellation, chainSurfaced] = await Promise.all([
|
|
4227
|
-
loadBetEntry(betId),
|
|
4228
|
-
loadConstellationState(betId),
|
|
4229
|
-
searchChain(userInput, { maxResults: 8 })
|
|
4230
|
-
]);
|
|
4231
|
-
if (!betEntry) {
|
|
4232
|
-
return {
|
|
4233
|
-
content: [{ type: "text", text: `Bet \`${betId}\` not found. Use start to create a session.` }]
|
|
4234
|
-
};
|
|
4235
|
-
}
|
|
4236
|
-
const betData = betEntry.data ?? {};
|
|
4237
|
-
if (captureItems.length > 0) {
|
|
4238
|
-
const capAgentId = getAgentSessionId();
|
|
4239
|
-
const collectionMap = {
|
|
4240
|
-
element: { slug: "features", dataField: "description", relationType: "part_of" },
|
|
4241
|
-
risk: { slug: "tensions", dataField: "description", relationType: "constrains" },
|
|
4242
|
-
decision: { slug: "decisions", dataField: "rationale", relationType: "informs" }
|
|
4243
|
-
};
|
|
4244
|
-
let runningBetData = { ...betData };
|
|
4245
|
-
for (const item of captureItems) {
|
|
4246
|
-
if (item.type === "noGo") {
|
|
4247
|
-
const updatedNoGos = appendNoGo(
|
|
4248
|
-
runningBetData.noGos,
|
|
4249
|
-
{ title: item.name, explanation: item.description }
|
|
4233
|
+
} catch (createErr) {
|
|
4234
|
+
const msg = createErr instanceof Error ? createErr.message : String(createErr);
|
|
4235
|
+
if (msg.includes("Duplicate entry") || msg.includes("already exists")) {
|
|
4236
|
+
const fallback = await findAndLinkExisting(
|
|
4237
|
+
item.name,
|
|
4238
|
+
mapping.slug,
|
|
4239
|
+
betEntryId,
|
|
4240
|
+
mapping.relationType
|
|
4250
4241
|
);
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
data: { noGos: updatedNoGos },
|
|
4256
|
-
changeNote: `Added no-go: ${item.name}`
|
|
4257
|
-
});
|
|
4258
|
-
await recordSessionActivity({ entryModified: betEntry._id });
|
|
4259
|
-
} catch (updErr) {
|
|
4242
|
+
if (fallback) {
|
|
4243
|
+
capturedEntryId = fallback.entryId;
|
|
4244
|
+
entriesCreated.push(fallback.entryId);
|
|
4245
|
+
if (fallback.linked) relationsCreated++;
|
|
4260
4246
|
captureErrors.push({
|
|
4261
|
-
operation: "
|
|
4262
|
-
detail: `
|
|
4247
|
+
operation: "info",
|
|
4248
|
+
detail: `Linked existing ${mapping.slug} entry \`${fallback.entryId}\` instead of creating duplicate.`
|
|
4263
4249
|
});
|
|
4264
|
-
}
|
|
4265
|
-
continue;
|
|
4266
|
-
}
|
|
4267
|
-
const mapping = collectionMap[item.type];
|
|
4268
|
-
if (!mapping) continue;
|
|
4269
|
-
let capturedEntryId = null;
|
|
4270
|
-
try {
|
|
4271
|
-
const result = await mcpMutation(
|
|
4272
|
-
"chain.createEntry",
|
|
4273
|
-
{
|
|
4274
|
-
collectionSlug: mapping.slug,
|
|
4275
|
-
name: item.name,
|
|
4276
|
-
status: "draft",
|
|
4277
|
-
data: { [mapping.dataField]: item.description },
|
|
4278
|
-
createdBy: capAgentId ? `agent:${capAgentId}` : "facilitate",
|
|
4279
|
-
sessionId: capAgentId ?? void 0
|
|
4280
|
-
}
|
|
4281
|
-
);
|
|
4282
|
-
capturedEntryId = result.entryId;
|
|
4283
|
-
entriesCreated.push(result.entryId);
|
|
4284
|
-
await recordSessionActivity({ entryCreated: result.docId });
|
|
4285
|
-
} catch (createErr) {
|
|
4286
|
-
const msg = createErr instanceof Error ? createErr.message : String(createErr);
|
|
4287
|
-
if (msg.includes("Duplicate entry") || msg.includes("already exists")) {
|
|
4288
|
-
const fallback = await findAndLinkExisting(
|
|
4289
|
-
item.name,
|
|
4290
|
-
mapping.slug,
|
|
4291
|
-
betId,
|
|
4292
|
-
mapping.relationType
|
|
4293
|
-
);
|
|
4294
|
-
if (fallback) {
|
|
4295
|
-
capturedEntryId = fallback.entryId;
|
|
4296
|
-
entriesCreated.push(fallback.entryId);
|
|
4297
|
-
if (fallback.linked) relationsCreated++;
|
|
4298
|
-
captureErrors.push({
|
|
4299
|
-
operation: "info",
|
|
4300
|
-
detail: `Linked existing ${mapping.slug} entry \`${fallback.entryId}\` instead of creating duplicate.`
|
|
4301
|
-
});
|
|
4302
|
-
} else {
|
|
4303
|
-
captureErrors.push({ operation: "capture", detail: `${item.type}: ${msg}` });
|
|
4304
|
-
}
|
|
4305
4250
|
} else {
|
|
4306
4251
|
captureErrors.push({ operation: "capture", detail: `${item.type}: ${msg}` });
|
|
4307
4252
|
}
|
|
4253
|
+
} else {
|
|
4254
|
+
captureErrors.push({ operation: "capture", detail: `${item.type}: ${msg}` });
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
if (!capturedEntryId) continue;
|
|
4258
|
+
try {
|
|
4259
|
+
await mcpMutation("chain.createEntryRelation", {
|
|
4260
|
+
fromEntryId: capturedEntryId,
|
|
4261
|
+
toEntryId: betEntryId,
|
|
4262
|
+
type: mapping.relationType
|
|
4263
|
+
});
|
|
4264
|
+
relationsCreated++;
|
|
4265
|
+
await recordSessionActivity({ relationCreated: true });
|
|
4266
|
+
} catch (relErr) {
|
|
4267
|
+
const msg = relErr instanceof Error ? relErr.message : String(relErr);
|
|
4268
|
+
if (!msg.includes("already exists") && !msg.includes("Duplicate")) {
|
|
4269
|
+
captureErrors.push({
|
|
4270
|
+
operation: "relation",
|
|
4271
|
+
detail: `${mapping.relationType} from ${capturedEntryId} to ${betEntryId}: ${msg}`
|
|
4272
|
+
});
|
|
4308
4273
|
}
|
|
4309
|
-
|
|
4274
|
+
}
|
|
4275
|
+
if (item.type === "element") {
|
|
4276
|
+
const updatedElements = appendElement(
|
|
4277
|
+
runningBetData.elements,
|
|
4278
|
+
{ name: item.name, description: item.description, entryId: capturedEntryId }
|
|
4279
|
+
);
|
|
4280
|
+
runningBetData.elements = updatedElements;
|
|
4310
4281
|
try {
|
|
4311
|
-
await mcpMutation("chain.
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4282
|
+
await mcpMutation("chain.updateEntry", {
|
|
4283
|
+
entryId: betEntryId,
|
|
4284
|
+
data: { elements: updatedElements },
|
|
4285
|
+
changeNote: `Added element: ${item.name}`
|
|
4286
|
+
});
|
|
4287
|
+
await recordSessionActivity({ entryModified: betDocId });
|
|
4288
|
+
} catch (updErr) {
|
|
4289
|
+
captureErrors.push({
|
|
4290
|
+
operation: "update",
|
|
4291
|
+
detail: `elements field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
|
|
4315
4292
|
});
|
|
4316
|
-
relationsCreated++;
|
|
4317
|
-
await recordSessionActivity({ relationCreated: true });
|
|
4318
|
-
} catch (relErr) {
|
|
4319
|
-
const msg = relErr instanceof Error ? relErr.message : String(relErr);
|
|
4320
|
-
if (!msg.includes("already exists") && !msg.includes("Duplicate")) {
|
|
4321
|
-
captureErrors.push({
|
|
4322
|
-
operation: "relation",
|
|
4323
|
-
detail: `${mapping.relationType} from ${capturedEntryId} to ${betId}: ${msg}`
|
|
4324
|
-
});
|
|
4325
|
-
}
|
|
4326
4293
|
}
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
}
|
|
4346
|
-
} else if (item.type === "risk") {
|
|
4347
|
-
const updatedRisks = appendRabbitHole(
|
|
4348
|
-
runningBetData.rabbitHoles,
|
|
4349
|
-
{ name: item.name, description: item.description, theme: item.theme, entryId: capturedEntryId }
|
|
4350
|
-
);
|
|
4351
|
-
runningBetData.rabbitHoles = updatedRisks;
|
|
4352
|
-
try {
|
|
4353
|
-
await mcpMutation("chain.updateEntry", {
|
|
4354
|
-
entryId: betId,
|
|
4355
|
-
data: { rabbitHoles: updatedRisks },
|
|
4356
|
-
changeNote: `Added risk: ${item.name}`
|
|
4357
|
-
});
|
|
4358
|
-
await recordSessionActivity({ entryModified: betEntry._id });
|
|
4359
|
-
} catch (updErr) {
|
|
4360
|
-
captureErrors.push({
|
|
4361
|
-
operation: "update",
|
|
4362
|
-
detail: `rabbitHoles field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
|
|
4363
|
-
});
|
|
4364
|
-
}
|
|
4294
|
+
} else if (item.type === "risk") {
|
|
4295
|
+
const updatedRisks = appendRabbitHole(
|
|
4296
|
+
runningBetData.rabbitHoles,
|
|
4297
|
+
{ name: item.name, description: item.description, theme: item.theme, entryId: capturedEntryId }
|
|
4298
|
+
);
|
|
4299
|
+
runningBetData.rabbitHoles = updatedRisks;
|
|
4300
|
+
try {
|
|
4301
|
+
await mcpMutation("chain.updateEntry", {
|
|
4302
|
+
entryId: betEntryId,
|
|
4303
|
+
data: { rabbitHoles: updatedRisks },
|
|
4304
|
+
changeNote: `Added risk: ${item.name}`
|
|
4305
|
+
});
|
|
4306
|
+
await recordSessionActivity({ entryModified: betDocId });
|
|
4307
|
+
} catch (updErr) {
|
|
4308
|
+
captureErrors.push({
|
|
4309
|
+
operation: "update",
|
|
4310
|
+
detail: `rabbitHoles field: ${updErr instanceof Error ? updErr.message : String(updErr)}`
|
|
4311
|
+
});
|
|
4365
4312
|
}
|
|
4366
4313
|
}
|
|
4367
4314
|
}
|
|
4368
|
-
|
|
4315
|
+
return { entriesCreated, relationsCreated, captureErrors };
|
|
4316
|
+
}
|
|
4317
|
+
async function computeAndUpdateScores(opts) {
|
|
4318
|
+
const {
|
|
4319
|
+
betEntryId,
|
|
4320
|
+
betDocId,
|
|
4321
|
+
betData,
|
|
4322
|
+
betEntry,
|
|
4323
|
+
userInput,
|
|
4324
|
+
argDimension,
|
|
4325
|
+
source,
|
|
4326
|
+
captureItems,
|
|
4327
|
+
chainSurfaced,
|
|
4328
|
+
constellation
|
|
4329
|
+
} = opts;
|
|
4330
|
+
const captureErrors = [];
|
|
4331
|
+
const refreshedBet = await loadBetEntry(betEntryId);
|
|
4369
4332
|
const refreshedData = refreshedBet?.data ?? betData;
|
|
4370
|
-
const refreshedConstellation = captureItems.length > 0 ? await loadConstellationState(
|
|
4371
|
-
const sessionDrafts = await loadSessionDrafts(
|
|
4333
|
+
const refreshedConstellation = captureItems.length > 0 ? await loadConstellationState(betEntryId, refreshedBet?._id ?? betEntry._id) : constellation;
|
|
4334
|
+
const sessionDrafts = await loadSessionDrafts(betEntryId, refreshedBet?._id ?? betEntry._id);
|
|
4372
4335
|
const constellationEntryIds = sessionDrafts.map((d) => d.entryId);
|
|
4373
4336
|
const alreadyCheckedOverlap = typeof refreshedData._overlapIds === "string";
|
|
4374
4337
|
let overlap = [];
|
|
4375
4338
|
if (!alreadyCheckedOverlap && captureItems.length === 0) {
|
|
4376
4339
|
const problemText = refreshedData.problem ?? userInput;
|
|
4377
|
-
const overlapResults = await searchChain(problemText, { maxResults: 5, excludeIds: [
|
|
4340
|
+
const overlapResults = await searchChain(problemText, { maxResults: 5, excludeIds: [betEntryId, ...constellationEntryIds] });
|
|
4378
4341
|
overlap = overlapResults.map((r) => ({
|
|
4379
4342
|
entryId: r.entryId,
|
|
4380
4343
|
similarity: "text_match",
|
|
@@ -4428,7 +4391,7 @@ async function handleRespond(args) {
|
|
|
4428
4391
|
}
|
|
4429
4392
|
if (Object.keys(fieldUpdates).length > 0) {
|
|
4430
4393
|
await mcpMutation("chain.updateEntry", {
|
|
4431
|
-
entryId:
|
|
4394
|
+
entryId: betEntryId,
|
|
4432
4395
|
data: fieldUpdates,
|
|
4433
4396
|
changeNote: `Updated ${Object.keys(fieldUpdates).join(", ")} from shaping session`
|
|
4434
4397
|
});
|
|
@@ -4451,12 +4414,10 @@ async function handleRespond(args) {
|
|
|
4451
4414
|
const observation = dimensionResult.satisfied.length > 0 ? `${DIMENSION_LABELS[activeDimension]}: ${dimensionResult.satisfied.join("; ")}` : `${DIMENSION_LABELS[activeDimension]}: needs more detail`;
|
|
4452
4415
|
const pushback = captureItems.length === 0 && overlap.length > 0 ? `${overlap[0].summary} already exists on the Chain. How does your bet differ?` : dimensionResult.missing.length > 0 ? dimensionResult.missing[0] : "";
|
|
4453
4416
|
const suggestedQuestion = dimensionResult.missing.length > 1 ? dimensionResult.missing[1] : dimensionResult.missing.length > 0 ? dimensionResult.missing[0] : `${DIMENSION_LABELS[nextDimension]} is next \u2014 ready to move on?`;
|
|
4454
|
-
const wsCtx = await getWorkspaceContext();
|
|
4455
|
-
const studioUrl = buildStudioUrl(wsCtx.workspaceSlug, betId);
|
|
4456
4417
|
let buildContract;
|
|
4457
4418
|
if (captureReady) {
|
|
4458
4419
|
const contractCtx = {
|
|
4459
|
-
betEntryId
|
|
4420
|
+
betEntryId,
|
|
4460
4421
|
governanceEntries: chainSurfaced.filter((e) => ["principles", "standards", "business-rules"].includes(e.collection)).map((e) => ({ entryId: e.entryId, name: e.name, collection: e.collection })),
|
|
4461
4422
|
relatedDecisions: chainSurfaced.filter((e) => e.collection === "decisions").map((e) => ({ entryId: e.entryId, name: e.name })),
|
|
4462
4423
|
relatedTensions: chainSurfaced.filter((e) => e.collection === "tensions").map((e) => ({ entryId: e.entryId, name: e.name }))
|
|
@@ -4465,7 +4426,7 @@ async function handleRespond(args) {
|
|
|
4465
4426
|
if (refreshedData.buildContract !== buildContract) {
|
|
4466
4427
|
try {
|
|
4467
4428
|
await mcpMutation("chain.updateEntry", {
|
|
4468
|
-
entryId:
|
|
4429
|
+
entryId: betEntryId,
|
|
4469
4430
|
data: { buildContract },
|
|
4470
4431
|
changeNote: "Updated build contract from shaping session"
|
|
4471
4432
|
});
|
|
@@ -4481,7 +4442,7 @@ async function handleRespond(args) {
|
|
|
4481
4442
|
let commitBlockers;
|
|
4482
4443
|
if (captureReady) {
|
|
4483
4444
|
commitBlockers = computeCommitBlockers({
|
|
4484
|
-
betEntryId
|
|
4445
|
+
betEntryId,
|
|
4485
4446
|
betDocId: refreshedBet?._id ?? betEntry._id,
|
|
4486
4447
|
relations: refreshedConstellation.relations,
|
|
4487
4448
|
betData: refreshedData,
|
|
@@ -4489,11 +4450,59 @@ async function handleRespond(args) {
|
|
|
4489
4450
|
});
|
|
4490
4451
|
if (commitBlockers.length === 0) commitBlockers = void 0;
|
|
4491
4452
|
}
|
|
4453
|
+
return {
|
|
4454
|
+
scorecard,
|
|
4455
|
+
scorecardCriteria,
|
|
4456
|
+
phase,
|
|
4457
|
+
activeDimension,
|
|
4458
|
+
nextDimension,
|
|
4459
|
+
dimensionResult,
|
|
4460
|
+
overlap,
|
|
4461
|
+
sessionDrafts,
|
|
4462
|
+
alignment,
|
|
4463
|
+
coaching: { observation, pushback, suggestedQuestion, captureReady },
|
|
4464
|
+
completed,
|
|
4465
|
+
isSmallBatch,
|
|
4466
|
+
refreshedData,
|
|
4467
|
+
scoringCtx,
|
|
4468
|
+
captureErrors,
|
|
4469
|
+
buildContract,
|
|
4470
|
+
commitBlockers
|
|
4471
|
+
};
|
|
4472
|
+
}
|
|
4473
|
+
function assembleResponse(opts) {
|
|
4474
|
+
const {
|
|
4475
|
+
betEntryId,
|
|
4476
|
+
betName,
|
|
4477
|
+
workspaceSlug,
|
|
4478
|
+
scorecard,
|
|
4479
|
+
scorecardCriteria,
|
|
4480
|
+
phase,
|
|
4481
|
+
activeDimension,
|
|
4482
|
+
nextDimension,
|
|
4483
|
+
dimensionResult,
|
|
4484
|
+
coaching,
|
|
4485
|
+
overlap,
|
|
4486
|
+
sessionDrafts,
|
|
4487
|
+
alignment,
|
|
4488
|
+
chainSurfaced,
|
|
4489
|
+
scoringCtx,
|
|
4490
|
+
isSmallBatch,
|
|
4491
|
+
completed,
|
|
4492
|
+
refreshedData,
|
|
4493
|
+
buildContract,
|
|
4494
|
+
commitBlockers,
|
|
4495
|
+
entriesCreated,
|
|
4496
|
+
relationsCreated,
|
|
4497
|
+
captureErrors,
|
|
4498
|
+
captureItems
|
|
4499
|
+
} = opts;
|
|
4500
|
+
const studioUrl = buildStudioUrl(workspaceSlug, betEntryId);
|
|
4492
4501
|
const suggested = suggestCaptures(scoringCtx, activeDimension);
|
|
4493
4502
|
const betProblem = refreshedData.problem ?? "";
|
|
4494
|
-
const
|
|
4503
|
+
const responseBetName = refreshedData.description ?? betName;
|
|
4495
4504
|
const elementNames = (refreshedData.elements ?? "").match(/###\s*Element\s*\d+:\s*(.+)/gi)?.map((h) => h.replace(/###\s*Element\s*\d+:\s*/i, "").trim()) ?? [];
|
|
4496
|
-
const investigationBrief = buildInvestigationBrief(phase,
|
|
4505
|
+
const investigationBrief = buildInvestigationBrief(phase, responseBetName, betEntryId, betProblem, elementNames) ?? void 0;
|
|
4497
4506
|
const response = {
|
|
4498
4507
|
version: 2,
|
|
4499
4508
|
phase,
|
|
@@ -4505,15 +4514,15 @@ async function handleRespond(args) {
|
|
|
4505
4514
|
suggestedCaptures: suggested,
|
|
4506
4515
|
sessionDrafts,
|
|
4507
4516
|
coaching: {
|
|
4508
|
-
observation,
|
|
4517
|
+
observation: coaching.observation,
|
|
4509
4518
|
missing: dimensionResult.missing,
|
|
4510
|
-
pushback,
|
|
4511
|
-
suggestedQuestion,
|
|
4512
|
-
captureReady,
|
|
4519
|
+
pushback: coaching.pushback,
|
|
4520
|
+
suggestedQuestion: coaching.suggestedQuestion,
|
|
4521
|
+
captureReady: coaching.captureReady,
|
|
4513
4522
|
nextDimension
|
|
4514
4523
|
},
|
|
4515
4524
|
captured: {
|
|
4516
|
-
betEntryId
|
|
4525
|
+
betEntryId,
|
|
4517
4526
|
studioUrl,
|
|
4518
4527
|
entriesCreated,
|
|
4519
4528
|
relationsCreated
|
|
@@ -4525,6 +4534,7 @@ async function handleRespond(args) {
|
|
|
4525
4534
|
suggestedOrder: activeDimensions(isSmallBatch)
|
|
4526
4535
|
},
|
|
4527
4536
|
buildContract,
|
|
4537
|
+
directive: buildDirective(scorecard, phase, betEntryId, coaching.captureReady, activeDimension, nextDimension) || void 0,
|
|
4528
4538
|
investigationBrief,
|
|
4529
4539
|
commitBlockers,
|
|
4530
4540
|
scorecardCriteria
|
|
@@ -4536,39 +4546,141 @@ async function handleRespond(args) {
|
|
|
4536
4546
|
};
|
|
4537
4547
|
const expectedCaptureType = STRUCTURED_DIMS[activeDimension];
|
|
4538
4548
|
const dataLossWarning = expectedCaptureType && captureItems.length === 0 ? `**WARNING:** You discussed ${activeDimension} but did not use the \`capture\` parameter \u2014 nothing was saved to the bet. Re-send with \`capture={type:"${expectedCaptureType}", name:"...", description:"..."}\` to persist this.` : "";
|
|
4539
|
-
const directive = buildDirective(scorecard, phase, betId, captureReady, activeDimension, nextDimension);
|
|
4540
4549
|
const summaryParts = [
|
|
4541
4550
|
`# ${PHASE_LABELS[phase]} \u2014 ${DIMENSION_LABELS[activeDimension]}`,
|
|
4542
4551
|
"",
|
|
4543
4552
|
dataLossWarning,
|
|
4544
4553
|
`**Score:** ${DIMENSION_LABELS[activeDimension]}: ${scorecard[activeDimension]}/10 \u2014 ${formatCriteriaLine(dimensionResult.criteria)}`,
|
|
4545
4554
|
`**Phase:** ${PHASE_LABELS[phase]}`,
|
|
4546
|
-
observation,
|
|
4547
|
-
pushback ? `**Pushback:** ${pushback}` : "",
|
|
4548
|
-
`**Next:** ${suggestedQuestion}`,
|
|
4549
|
-
captureReady ? "\n**Capture ready** \u2014 the bet has enough shape to finalize." : "",
|
|
4555
|
+
coaching.observation,
|
|
4556
|
+
coaching.pushback ? `**Pushback:** ${coaching.pushback}` : "",
|
|
4557
|
+
`**Next:** ${coaching.suggestedQuestion}`,
|
|
4558
|
+
coaching.captureReady ? "\n**Capture ready** \u2014 the bet has enough shape to finalize." : "",
|
|
4550
4559
|
commitBlockers?.length ? `
|
|
4551
4560
|
\u26A0 **Commit blockers (${commitBlockers.length}):** ${commitBlockers.map((b) => `${b.blocker} \u2192 \`${b.fix}\``).join("; ")}` : "",
|
|
4552
4561
|
entriesCreated.length > 0 ? `**Captured:** ${entriesCreated.join(", ")}` : "",
|
|
4553
4562
|
suggested.length > 0 ? `**Suggest capturing:** ${suggested.map((s) => `${s.type}: "${s.name}"`).join("; ")}` : "",
|
|
4554
4563
|
sessionDrafts.length > 0 ? `**Session drafts (${sessionDrafts.length}):** ${sessionDrafts.map((d) => `\`${d.entryId}\` ${d.name}`).join(", ")}` : "",
|
|
4555
|
-
captureErrors.length > 0 ? `**Warnings:** ${captureErrors.map((e) => e.detail).join("; ")}` : ""
|
|
4556
|
-
directive ? `
|
|
4557
|
-
---
|
|
4558
|
-
## Agent Directive
|
|
4559
|
-
${directive}` : "",
|
|
4560
|
-
investigationBrief ? `
|
|
4561
|
-
## Investigation Brief
|
|
4562
|
-
${investigationBrief.tasks.map((t) => `- **${t.target}:** ${t.query} \u2014 ${t.purpose}`).join("\n")}
|
|
4563
|
-
|
|
4564
|
-
**Proposal guidance:** ${investigationBrief.proposalGuidance}
|
|
4565
|
-
**Reaction prompt:** ${investigationBrief.reactionPrompt}` : ""
|
|
4564
|
+
captureErrors.length > 0 ? `**Warnings:** ${captureErrors.map((e) => e.detail).join("; ")}` : ""
|
|
4566
4565
|
];
|
|
4567
4566
|
return {
|
|
4568
4567
|
content: [{ type: "text", text: summaryParts.filter(Boolean).join("\n") }],
|
|
4569
4568
|
structuredContent: response
|
|
4570
4569
|
};
|
|
4571
4570
|
}
|
|
4571
|
+
async function handleRespond(args) {
|
|
4572
|
+
requireWriteAccess();
|
|
4573
|
+
const { userInput, betEntryId: argBetId, dimension: argDimension, capture: rawCapture, source: argSource, betName: argBetName } = args;
|
|
4574
|
+
const source = argSource ?? "user";
|
|
4575
|
+
const captureItems = rawCapture ? Array.isArray(rawCapture) ? rawCapture : [rawCapture] : [];
|
|
4576
|
+
if (!userInput) {
|
|
4577
|
+
return {
|
|
4578
|
+
content: [{ type: "text", text: "`userInput` is required for respond action." }]
|
|
4579
|
+
};
|
|
4580
|
+
}
|
|
4581
|
+
let betId = argBetId;
|
|
4582
|
+
if (!betId) {
|
|
4583
|
+
const betName = argBetName ?? "Untitled Bet (Shaping)";
|
|
4584
|
+
const agentId = getAgentSessionId();
|
|
4585
|
+
try {
|
|
4586
|
+
const result = await mcpMutation(
|
|
4587
|
+
"chain.createEntry",
|
|
4588
|
+
{
|
|
4589
|
+
collectionSlug: "bets",
|
|
4590
|
+
name: betName,
|
|
4591
|
+
status: "draft",
|
|
4592
|
+
data: {
|
|
4593
|
+
problem: userInput,
|
|
4594
|
+
appetite: "",
|
|
4595
|
+
elements: "",
|
|
4596
|
+
rabbitHoles: "",
|
|
4597
|
+
noGos: "",
|
|
4598
|
+
architecture: "",
|
|
4599
|
+
buildContract: "",
|
|
4600
|
+
description: `Shaping session for: ${betName}`,
|
|
4601
|
+
status: "shaping",
|
|
4602
|
+
shapingSessionActive: true
|
|
4603
|
+
},
|
|
4604
|
+
createdBy: agentId ? `agent:${agentId}` : "facilitate",
|
|
4605
|
+
sessionId: agentId ?? void 0
|
|
4606
|
+
}
|
|
4607
|
+
);
|
|
4608
|
+
betId = result.entryId;
|
|
4609
|
+
await recordSessionActivity({ entryCreated: result.docId });
|
|
4610
|
+
} catch (err) {
|
|
4611
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4612
|
+
return {
|
|
4613
|
+
content: [{ type: "text", text: `Failed to create bet entry: ${msg}` }],
|
|
4614
|
+
isError: true
|
|
4615
|
+
};
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
const [betEntry, constellation, chainSurfaced] = await Promise.all([
|
|
4619
|
+
loadBetEntry(betId),
|
|
4620
|
+
loadConstellationState(betId),
|
|
4621
|
+
searchChain(userInput, { maxResults: 8 })
|
|
4622
|
+
]);
|
|
4623
|
+
if (!betEntry) {
|
|
4624
|
+
return {
|
|
4625
|
+
content: [{ type: "text", text: `Bet \`${betId}\` not found. Use start to create a session.` }]
|
|
4626
|
+
};
|
|
4627
|
+
}
|
|
4628
|
+
const betData = betEntry.data ?? {};
|
|
4629
|
+
let entriesCreated = [];
|
|
4630
|
+
let relationsCreated = 0;
|
|
4631
|
+
const captureErrors = [];
|
|
4632
|
+
if (captureItems.length > 0) {
|
|
4633
|
+
const capResult = await processCaptures({
|
|
4634
|
+
captureItems,
|
|
4635
|
+
betEntryId: betId,
|
|
4636
|
+
betDocId: betEntry._id,
|
|
4637
|
+
betData
|
|
4638
|
+
});
|
|
4639
|
+
entriesCreated = capResult.entriesCreated;
|
|
4640
|
+
relationsCreated = capResult.relationsCreated;
|
|
4641
|
+
captureErrors.push(...capResult.captureErrors);
|
|
4642
|
+
}
|
|
4643
|
+
const scoreResult = await computeAndUpdateScores({
|
|
4644
|
+
betEntryId: betId,
|
|
4645
|
+
betDocId: betEntry._id,
|
|
4646
|
+
betData,
|
|
4647
|
+
betEntry,
|
|
4648
|
+
userInput,
|
|
4649
|
+
argDimension,
|
|
4650
|
+
source,
|
|
4651
|
+
captureItems,
|
|
4652
|
+
chainSurfaced,
|
|
4653
|
+
constellation
|
|
4654
|
+
});
|
|
4655
|
+
captureErrors.push(...scoreResult.captureErrors);
|
|
4656
|
+
const wsCtx = await getWorkspaceContext();
|
|
4657
|
+
return assembleResponse({
|
|
4658
|
+
betEntryId: betId,
|
|
4659
|
+
betName: betEntry.name,
|
|
4660
|
+
workspaceSlug: wsCtx.workspaceSlug,
|
|
4661
|
+
scorecard: scoreResult.scorecard,
|
|
4662
|
+
scorecardCriteria: scoreResult.scorecardCriteria,
|
|
4663
|
+
phase: scoreResult.phase,
|
|
4664
|
+
activeDimension: scoreResult.activeDimension,
|
|
4665
|
+
nextDimension: scoreResult.nextDimension,
|
|
4666
|
+
dimensionResult: scoreResult.dimensionResult,
|
|
4667
|
+
coaching: scoreResult.coaching,
|
|
4668
|
+
overlap: scoreResult.overlap,
|
|
4669
|
+
sessionDrafts: scoreResult.sessionDrafts,
|
|
4670
|
+
alignment: scoreResult.alignment,
|
|
4671
|
+
chainSurfaced,
|
|
4672
|
+
scoringCtx: scoreResult.scoringCtx,
|
|
4673
|
+
isSmallBatch: scoreResult.isSmallBatch,
|
|
4674
|
+
completed: scoreResult.completed,
|
|
4675
|
+
refreshedData: scoreResult.refreshedData,
|
|
4676
|
+
buildContract: scoreResult.buildContract,
|
|
4677
|
+
commitBlockers: scoreResult.commitBlockers,
|
|
4678
|
+
entriesCreated,
|
|
4679
|
+
relationsCreated,
|
|
4680
|
+
captureErrors,
|
|
4681
|
+
captureItems
|
|
4682
|
+
});
|
|
4683
|
+
}
|
|
4572
4684
|
async function handleScore(args) {
|
|
4573
4685
|
const betId = args.betEntryId;
|
|
4574
4686
|
if (!betId) {
|
|
@@ -4833,7 +4945,7 @@ async function handleCommitConstellation(args) {
|
|
|
4833
4945
|
}
|
|
4834
4946
|
let contradictionWarnings = [];
|
|
4835
4947
|
try {
|
|
4836
|
-
const { runContradictionCheck } = await import("./smart-capture-
|
|
4948
|
+
const { runContradictionCheck } = await import("./smart-capture-3BWER4RY.js");
|
|
4837
4949
|
const descField = betData.problem ?? betData.description ?? "";
|
|
4838
4950
|
contradictionWarnings = await runContradictionCheck(
|
|
4839
4951
|
betEntry.name ?? betId,
|
|
@@ -5676,6 +5788,16 @@ function buildInterviewResponse(workspaceName) {
|
|
|
5676
5788
|
}
|
|
5677
5789
|
|
|
5678
5790
|
// src/tools/start.ts
|
|
5791
|
+
async function tryMarkOriented(agentSessionId) {
|
|
5792
|
+
if (!agentSessionId) return { oriented: false, orientationStatus: "no_session" };
|
|
5793
|
+
try {
|
|
5794
|
+
await mcpCall("agent.markOriented", { sessionId: agentSessionId });
|
|
5795
|
+
setSessionOriented(true);
|
|
5796
|
+
return { oriented: true, orientationStatus: "complete" };
|
|
5797
|
+
} catch {
|
|
5798
|
+
return { oriented: false, orientationStatus: "failed" };
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5679
5801
|
var startSchema = z15.object({
|
|
5680
5802
|
preset: z15.string().optional().describe(
|
|
5681
5803
|
"Collection preset ID to seed (e.g. 'software-product', 'content-business', 'agency', 'saas-api', 'general'). Only used for fresh workspaces. If omitted on a fresh workspace, returns the preset menu."
|
|
@@ -5725,10 +5847,16 @@ function registerStartTools(server) {
|
|
|
5725
5847
|
};
|
|
5726
5848
|
}
|
|
5727
5849
|
if (stage === "blank") {
|
|
5728
|
-
const
|
|
5850
|
+
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
5851
|
+
const blankResult = buildBlankResponse(wsCtx);
|
|
5729
5852
|
return {
|
|
5730
|
-
content: [{ type: "text", text:
|
|
5731
|
-
structuredContent:
|
|
5853
|
+
content: [{ type: "text", text: blankResult.text }],
|
|
5854
|
+
structuredContent: {
|
|
5855
|
+
...blankResult.structuredContent,
|
|
5856
|
+
oriented,
|
|
5857
|
+
orientationStatus,
|
|
5858
|
+
...agentSessionId ? { sessionId: agentSessionId } : {}
|
|
5859
|
+
}
|
|
5732
5860
|
};
|
|
5733
5861
|
}
|
|
5734
5862
|
if (stage === "seeded") {
|
|
@@ -5752,44 +5880,71 @@ function registerStartTools(server) {
|
|
|
5752
5880
|
}
|
|
5753
5881
|
);
|
|
5754
5882
|
}
|
|
5755
|
-
function
|
|
5883
|
+
function buildBlankResponse(wsCtx) {
|
|
5884
|
+
const instructions = getInterviewInstructions(wsCtx.workspaceName);
|
|
5756
5885
|
const text = [
|
|
5757
5886
|
`# Welcome to ${wsCtx.workspaceName}`,
|
|
5758
5887
|
"",
|
|
5759
|
-
"Your workspace is fresh. Let
|
|
5888
|
+
"Your workspace is fresh. Let's set it up.",
|
|
5889
|
+
"",
|
|
5890
|
+
"## How would you like to start?",
|
|
5891
|
+
"",
|
|
5892
|
+
"Present these options to the user:",
|
|
5893
|
+
"",
|
|
5894
|
+
"**1. Tell me about your product** _(recommended)_",
|
|
5895
|
+
"Ask the user:",
|
|
5896
|
+
`> ${instructions.question1}`,
|
|
5897
|
+
"",
|
|
5898
|
+
"_A sentence or two is enough. This becomes the foundation \u2014 vision and audience \u2014 that makes everything else more relevant._",
|
|
5899
|
+
"",
|
|
5900
|
+
"**2. Scan my codebase**",
|
|
5901
|
+
"Skip the questions and jump straight to reading the project files.",
|
|
5902
|
+
"",
|
|
5903
|
+
"**3. Use a preset**",
|
|
5904
|
+
`Seed collections for a domain. Available: software-product, content-business, agency, saas-api, general.`,
|
|
5905
|
+
"",
|
|
5906
|
+
"---",
|
|
5907
|
+
"",
|
|
5908
|
+
"## Path 1: Interview first (default if user just starts talking)",
|
|
5909
|
+
"",
|
|
5910
|
+
instructions.systemPrompt,
|
|
5911
|
+
"",
|
|
5912
|
+
"After the user answers Q1, extract:",
|
|
5913
|
+
instructions.extractionGuidance,
|
|
5914
|
+
"",
|
|
5915
|
+
instructions.captureInstructions,
|
|
5916
|
+
"Use `autoCommit: false` for all captures \u2014 user confirms before anything is committed.",
|
|
5917
|
+
"",
|
|
5918
|
+
"**Then offer the codebase scan with context:**",
|
|
5919
|
+
`"Now that I know what you're building, I can scan your project files and find technical decisions, features, and conventions that are relevant to **your** product. Want me to?"`,
|
|
5920
|
+
"",
|
|
5921
|
+
"If they say yes, scan README.md, package.json/pyproject.toml/Cargo.toml, and top-level source dirs.",
|
|
5922
|
+
"Infer entries WITH the product context you now have. Present as a numbered list for confirmation.",
|
|
5923
|
+
"Capture confirmed entries with `autoCommit: false`.",
|
|
5924
|
+
"",
|
|
5925
|
+
"## Path 2: Scan first",
|
|
5760
5926
|
"",
|
|
5761
|
-
"
|
|
5762
|
-
"1.
|
|
5763
|
-
"2.
|
|
5927
|
+
"Read these files:",
|
|
5928
|
+
"1. README.md (or equivalent top-level docs)",
|
|
5929
|
+
"2. package.json / pyproject.toml / Cargo.toml (project manifest)",
|
|
5764
5930
|
"3. Top-level source directories and their purpose",
|
|
5765
5931
|
"",
|
|
5766
|
-
"
|
|
5767
|
-
"
|
|
5768
|
-
"- **Key technical decisions** already made (framework, DB, architecture)",
|
|
5769
|
-
"- **Core features** the codebase reveals",
|
|
5770
|
-
"- **Conventions** you notice (naming, patterns, standards)",
|
|
5932
|
+
"Infer 5\u20138 product knowledge entries (product name, tech decisions, features, conventions).",
|
|
5933
|
+
"Present as a numbered list. Ask which to keep. Capture confirmed entries with `autoCommit: false`.",
|
|
5771
5934
|
"",
|
|
5772
|
-
"
|
|
5773
|
-
"```",
|
|
5774
|
-
"1. [Entry name]: [1-sentence description]",
|
|
5775
|
-
"2. ...",
|
|
5776
|
-
"```",
|
|
5935
|
+
"**If the codebase is sparse** (no README, empty project): fall back to Path 1 (ask the user about their product).",
|
|
5777
5936
|
"",
|
|
5778
|
-
|
|
5937
|
+
"_Prefer 5 specific entries over 8 generic ones. Skip anything that would apply to any codebase._",
|
|
5779
5938
|
"",
|
|
5780
|
-
"
|
|
5939
|
+
"## Path 3: Preset",
|
|
5781
5940
|
"",
|
|
5782
|
-
|
|
5941
|
+
"If the user picks a preset, call `start` again with `preset: '<chosen-preset>'`.",
|
|
5783
5942
|
"",
|
|
5784
|
-
|
|
5943
|
+
instructions.qualityNote
|
|
5785
5944
|
].join("\n");
|
|
5786
5945
|
return {
|
|
5787
5946
|
text,
|
|
5788
|
-
structuredContent: {
|
|
5789
|
-
stage: "blank",
|
|
5790
|
-
oriented: false,
|
|
5791
|
-
orientationStatus: "no_session"
|
|
5792
|
-
}
|
|
5947
|
+
structuredContent: { stage: "blank" }
|
|
5793
5948
|
};
|
|
5794
5949
|
}
|
|
5795
5950
|
async function buildSeededResponse(wsCtx, readiness, agentSessionId) {
|
|
@@ -5815,18 +5970,7 @@ async function buildSeededResponse(wsCtx, readiness, agentSessionId) {
|
|
|
5815
5970
|
} else {
|
|
5816
5971
|
lines.push("No gaps detected \u2014 your workspace is filling up nicely. What would you like to work on?");
|
|
5817
5972
|
}
|
|
5818
|
-
|
|
5819
|
-
let orientationStatus = "no_session";
|
|
5820
|
-
if (agentSessionId) {
|
|
5821
|
-
try {
|
|
5822
|
-
await mcpCall("agent.markOriented", { sessionId: agentSessionId });
|
|
5823
|
-
setSessionOriented(true);
|
|
5824
|
-
oriented = true;
|
|
5825
|
-
orientationStatus = "complete";
|
|
5826
|
-
} catch {
|
|
5827
|
-
orientationStatus = "failed";
|
|
5828
|
-
}
|
|
5829
|
-
}
|
|
5973
|
+
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
5830
5974
|
return {
|
|
5831
5975
|
text: lines.join("\n"),
|
|
5832
5976
|
structuredContent: {
|
|
@@ -5868,18 +6012,7 @@ Available presets: ${listPresets().map((p) => `\`${p.id}\``).join(", ")}`,
|
|
|
5868
6012
|
skipped.push(`${col.name} (already exists)`);
|
|
5869
6013
|
}
|
|
5870
6014
|
}
|
|
5871
|
-
|
|
5872
|
-
let orientationStatus = "no_session";
|
|
5873
|
-
if (agentSessionId) {
|
|
5874
|
-
try {
|
|
5875
|
-
await mcpCall("agent.markOriented", { sessionId: agentSessionId });
|
|
5876
|
-
setSessionOriented(true);
|
|
5877
|
-
oriented = true;
|
|
5878
|
-
orientationStatus = "complete";
|
|
5879
|
-
} catch {
|
|
5880
|
-
orientationStatus = "failed";
|
|
5881
|
-
}
|
|
5882
|
-
}
|
|
6015
|
+
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
5883
6016
|
const lines = [
|
|
5884
6017
|
`# ${wsCtx.workspaceName} is ready`,
|
|
5885
6018
|
"",
|
|
@@ -6118,18 +6251,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
6118
6251
|
for (const err of errors) lines.push(`- ${err}`);
|
|
6119
6252
|
lines.push("");
|
|
6120
6253
|
}
|
|
6121
|
-
|
|
6122
|
-
let orientationStatus = "no_session";
|
|
6123
|
-
if (agentSessionId) {
|
|
6124
|
-
try {
|
|
6125
|
-
await mcpCall("agent.markOriented", { sessionId: agentSessionId });
|
|
6126
|
-
setSessionOriented(true);
|
|
6127
|
-
oriented = true;
|
|
6128
|
-
orientationStatus = "complete";
|
|
6129
|
-
} catch {
|
|
6130
|
-
orientationStatus = "failed";
|
|
6131
|
-
}
|
|
6132
|
-
}
|
|
6254
|
+
const { oriented, orientationStatus } = await tryMarkOriented(agentSessionId);
|
|
6133
6255
|
return {
|
|
6134
6256
|
text: lines.join("\n"),
|
|
6135
6257
|
structuredContent: {
|
|
@@ -8167,15 +8289,20 @@ function registerHealthTools(server) {
|
|
|
8167
8289
|
"Your workspace is fresh \u2014 let's set it up.",
|
|
8168
8290
|
"",
|
|
8169
8291
|
"**Recommended:** call the `start` tool instead of `orient` for the best first-run experience.",
|
|
8170
|
-
"It will guide you through
|
|
8292
|
+
"It will guide you through setting up your workspace \u2014 you can tell me about your product, scan your codebase, or use a preset.",
|
|
8171
8293
|
"",
|
|
8172
8294
|
"Or tell me about your product and I'll start capturing knowledge directly."
|
|
8173
8295
|
];
|
|
8296
|
+
let oriented = false;
|
|
8297
|
+
let orientationStatus = "no_session";
|
|
8174
8298
|
if (agentSessionId) {
|
|
8175
8299
|
try {
|
|
8176
8300
|
await mcpCall("agent.markOriented", { sessionId: agentSessionId });
|
|
8177
8301
|
setSessionOriented(true);
|
|
8302
|
+
oriented = true;
|
|
8303
|
+
orientationStatus = "complete";
|
|
8178
8304
|
} catch {
|
|
8305
|
+
orientationStatus = "failed";
|
|
8179
8306
|
}
|
|
8180
8307
|
}
|
|
8181
8308
|
return {
|
|
@@ -8183,7 +8310,8 @@ function registerHealthTools(server) {
|
|
|
8183
8310
|
structuredContent: {
|
|
8184
8311
|
stage: "blank",
|
|
8185
8312
|
readinessScore: readiness?.score ?? 0,
|
|
8186
|
-
oriented
|
|
8313
|
+
oriented,
|
|
8314
|
+
orientationStatus,
|
|
8187
8315
|
redirectHint: "Use the `start` tool for the full guided setup experience."
|
|
8188
8316
|
}
|
|
8189
8317
|
};
|
|
@@ -9320,9 +9448,14 @@ Each scored 0-10 by the server:
|
|
|
9320
9448
|
|
|
9321
9449
|
### Session Flow
|
|
9322
9450
|
1. **Start:** \`facilitate action=start betName="${idea}"\`
|
|
9323
|
-
|
|
9451
|
+
Starts shaping context only (no entry is created yet).
|
|
9452
|
+
|
|
9453
|
+
2. **First respond:** \`facilitate action=respond betName="${idea}" userInput="<user's input>"\`
|
|
9454
|
+
This creates the draft bet and captures the initial problem context.
|
|
9455
|
+
|
|
9456
|
+
Save \`captured.betEntryId\` from the response and reuse it for every subsequent call.
|
|
9324
9457
|
|
|
9325
|
-
|
|
9458
|
+
3. **Respond (repeat):** \`facilitate action=respond betEntryId="..." userInput="<user's input>"\`
|
|
9326
9459
|
Optionally add \`dimension="problem_clarity"\` to target a specific dimension.
|
|
9327
9460
|
|
|
9328
9461
|
**CRITICAL \u2014 Capture is REQUIRED for structured items:**
|
|
@@ -9332,10 +9465,10 @@ Each scored 0-10 by the server:
|
|
|
9332
9465
|
- No-gos: \`capture={type:"noGo", name:"...", description:"..."}\`
|
|
9333
9466
|
Each element, risk, and no-go needs its own \`respond\` call with \`capture\`.
|
|
9334
9467
|
|
|
9335
|
-
|
|
9468
|
+
4. **Score (anytime):** \`facilitate action=score betEntryId="..."\`
|
|
9336
9469
|
Check the scorecard without advancing.
|
|
9337
9470
|
|
|
9338
|
-
|
|
9471
|
+
5. **Resume:** \`facilitate action=resume betEntryId="..."\`
|
|
9339
9472
|
Reconstruct session from Chain state if returning to an existing bet.
|
|
9340
9473
|
|
|
9341
9474
|
### Reading the Response
|
|
@@ -9819,4 +9952,4 @@ export {
|
|
|
9819
9952
|
SERVER_VERSION,
|
|
9820
9953
|
createProductBrainServer
|
|
9821
9954
|
};
|
|
9822
|
-
//# sourceMappingURL=chunk-
|
|
9955
|
+
//# sourceMappingURL=chunk-FKOP3WC7.js.map
|