@productbrain/mcp 0.0.1-beta.63 → 0.0.1-beta.65
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-DM5DZC7B.js → chunk-UWILSX73.js} +59 -1
- package/dist/chunk-UWILSX73.js.map +1 -0
- package/dist/{chunk-GKMXGRJW.js → chunk-VBKAAFR6.js} +125 -166
- package/dist/chunk-VBKAAFR6.js.map +1 -0
- package/dist/http.js +22 -5
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{smart-capture-NYOKGAFS.js → smart-capture-EGTUM4XP.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-DM5DZC7B.js.map +0 -1
- package/dist/chunk-GKMXGRJW.js.map +0 -1
- /package/dist/{smart-capture-NYOKGAFS.js.map → smart-capture-EGTUM4XP.js.map} +0 -0
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
captureSchema,
|
|
4
4
|
checkEntryQuality,
|
|
5
5
|
classifyCollection,
|
|
6
|
+
clearSessionGaps,
|
|
6
7
|
closeAgentSession,
|
|
7
8
|
deriveEpistemicStatus,
|
|
8
9
|
extractPreview,
|
|
@@ -14,6 +15,8 @@ import {
|
|
|
14
15
|
getAgentSessionId,
|
|
15
16
|
getApiKeyScope,
|
|
16
17
|
getAuditLog,
|
|
18
|
+
getSessionGaps,
|
|
19
|
+
getTopGaps,
|
|
17
20
|
getWorkspaceContext,
|
|
18
21
|
getWorkspaceId,
|
|
19
22
|
initToolSurface,
|
|
@@ -23,6 +26,7 @@ import {
|
|
|
23
26
|
mcpQuery,
|
|
24
27
|
notFoundResult,
|
|
25
28
|
parseOrFail,
|
|
29
|
+
recordGap,
|
|
26
30
|
recordSessionActivity,
|
|
27
31
|
registerSmartCaptureTools,
|
|
28
32
|
requireActiveSession,
|
|
@@ -38,7 +42,7 @@ import {
|
|
|
38
42
|
unknownAction,
|
|
39
43
|
validationResult,
|
|
40
44
|
withEnvelope
|
|
41
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-UWILSX73.js";
|
|
42
46
|
import {
|
|
43
47
|
trackKnowledgeGap,
|
|
44
48
|
trackQualityCheck,
|
|
@@ -233,7 +237,7 @@ ${formatted}` }],
|
|
|
233
237
|
},
|
|
234
238
|
withEnvelope(async ({ entryId }) => {
|
|
235
239
|
requireWriteAccess();
|
|
236
|
-
const { runContradictionCheck } = await import("./smart-capture-
|
|
240
|
+
const { runContradictionCheck } = await import("./smart-capture-EGTUM4XP.js");
|
|
237
241
|
const entry = await mcpQuery("chain.getEntry", { entryId });
|
|
238
242
|
if (!entry) {
|
|
239
243
|
return notFoundResult(entryId, `Entry '${entryId}' not found. Try search to find the right ID.`);
|
|
@@ -353,43 +357,6 @@ ${formatted}` }],
|
|
|
353
357
|
|
|
354
358
|
// src/tools/entries.ts
|
|
355
359
|
import { z as z2 } from "zod";
|
|
356
|
-
|
|
357
|
-
// src/gap-store.ts
|
|
358
|
-
var SESSION_GAPS = [];
|
|
359
|
-
var DEDUP_WINDOW_MS = 6e4;
|
|
360
|
-
function isDuplicate(gap) {
|
|
361
|
-
const cutoff = gap.timestamp - DEDUP_WINDOW_MS;
|
|
362
|
-
return SESSION_GAPS.some(
|
|
363
|
-
(existing) => existing.query === gap.query && existing.tool === gap.tool && existing.action === gap.action && existing.timestamp >= cutoff
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
function recordGap(gap) {
|
|
367
|
-
const timestamped = { ...gap, timestamp: Date.now() };
|
|
368
|
-
if (isDuplicate(timestamped)) return false;
|
|
369
|
-
SESSION_GAPS.push(timestamped);
|
|
370
|
-
return true;
|
|
371
|
-
}
|
|
372
|
-
function getSessionGaps() {
|
|
373
|
-
return SESSION_GAPS;
|
|
374
|
-
}
|
|
375
|
-
function getTopGaps(limit = 5) {
|
|
376
|
-
const counts = /* @__PURE__ */ new Map();
|
|
377
|
-
for (const gap of SESSION_GAPS) {
|
|
378
|
-
const key = gap.query.toLowerCase().trim();
|
|
379
|
-
const existing = counts.get(key);
|
|
380
|
-
if (existing) {
|
|
381
|
-
existing.count++;
|
|
382
|
-
} else {
|
|
383
|
-
counts.set(key, { count: 1, gapType: gap.gapType });
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
return [...counts.entries()].map(([query, { count, gapType }]) => ({ query, count, gapType })).sort((a, b) => b.count - a.count).slice(0, limit);
|
|
387
|
-
}
|
|
388
|
-
function clearSessionGaps() {
|
|
389
|
-
SESSION_GAPS.length = 0;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// src/tools/entries.ts
|
|
393
360
|
function sanitizeEntryData(data) {
|
|
394
361
|
if (!data || typeof data !== "object") return void 0;
|
|
395
362
|
const filtered = Object.fromEntries(
|
|
@@ -6305,7 +6272,7 @@ async function handleCommitConstellation(args) {
|
|
|
6305
6272
|
}
|
|
6306
6273
|
let contradictionWarnings = [];
|
|
6307
6274
|
try {
|
|
6308
|
-
const { runContradictionCheck } = await import("./smart-capture-
|
|
6275
|
+
const { runContradictionCheck } = await import("./smart-capture-EGTUM4XP.js");
|
|
6309
6276
|
const descField = betData.problem ?? betData.description ?? "";
|
|
6310
6277
|
contradictionWarnings = await runContradictionCheck(
|
|
6311
6278
|
betEntry.name ?? betId,
|
|
@@ -6966,23 +6933,32 @@ function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
|
|
|
6966
6933
|
// src/tools/orient-shared.ts
|
|
6967
6934
|
function runAlignmentCheck(task, activeBets, taskContextHits) {
|
|
6968
6935
|
const betNames = activeBets.map((b) => b.name);
|
|
6969
|
-
const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length >
|
|
6936
|
+
const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
6970
6937
|
const matchingBet = activeBets.find((b) => {
|
|
6971
6938
|
const name = (b.name ?? "").toLowerCase();
|
|
6972
6939
|
return taskWords.some((w) => name.includes(w));
|
|
6973
6940
|
});
|
|
6941
|
+
if (matchingBet) {
|
|
6942
|
+
return { aligned: true, matchedBet: matchingBet.name, matchSource: "active_bet", betNames };
|
|
6943
|
+
}
|
|
6974
6944
|
const betHits = (taskContextHits ?? []).filter(
|
|
6975
6945
|
(e) => e.collectionSlug === "bets"
|
|
6976
6946
|
);
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6947
|
+
if (betHits.length > 0) {
|
|
6948
|
+
return { aligned: true, matchedBet: betHits[0]?.name ?? null, matchSource: "task_context", betNames };
|
|
6949
|
+
}
|
|
6950
|
+
return { aligned: false, matchedBet: null, matchSource: null, betNames };
|
|
6980
6951
|
}
|
|
6981
6952
|
function buildAlignmentCheckLines(result) {
|
|
6982
6953
|
const lines = ["## Alignment Check"];
|
|
6983
|
-
if (result.aligned) {
|
|
6954
|
+
if (result.aligned && result.matchSource === "active_bet") {
|
|
6984
6955
|
lines.push(
|
|
6985
|
-
`Task
|
|
6956
|
+
`Task aligns with active bet: **${result.matchedBet}**. Proceed.`
|
|
6957
|
+
);
|
|
6958
|
+
} else if (result.aligned && result.matchSource === "task_context") {
|
|
6959
|
+
const noActiveBets = result.betNames.length === 0;
|
|
6960
|
+
lines.push(
|
|
6961
|
+
`Task related to **${result.matchedBet}** via task context${noActiveBets ? " (no active bets in horizon=now)" : ""}. Proceed with caution \u2014 confirm scope with the user if uncertain.`
|
|
6986
6962
|
);
|
|
6987
6963
|
} else if (result.betNames.length === 0) {
|
|
6988
6964
|
lines.push(
|
|
@@ -7006,8 +6982,9 @@ var CORE_PROTOCOL = [
|
|
|
7006
6982
|
"**Validate against governance.** Before proposing any solution, check it against the Workspace Governance directives below. If your proposal conflicts with a principle, standard, or business rule \u2014 stop, name the conflict, and get explicit user confirmation before proceeding."
|
|
7007
6983
|
];
|
|
7008
6984
|
var MAX_ENTRIES_NO_TASK = 3;
|
|
6985
|
+
var MAX_ENTRIES_WITH_TASK = 5;
|
|
7009
6986
|
function extractKeywords(text) {
|
|
7010
|
-
return [...new Set(text.toLowerCase().split(/\s+/).filter((w) => w.length >
|
|
6987
|
+
return [...new Set(text.toLowerCase().split(/\s+/).filter((w) => w.length > 2))];
|
|
7011
6988
|
}
|
|
7012
6989
|
function scoreEntry(entry, keywords) {
|
|
7013
6990
|
const text = `${entry.name} ${entry.description ?? ""}`.toLowerCase();
|
|
@@ -7041,13 +7018,20 @@ function buildOperatingProtocol(governanceOrStandards, task) {
|
|
|
7041
7018
|
if (task) {
|
|
7042
7019
|
const keywords = extractKeywords(task);
|
|
7043
7020
|
let anyRelevant = false;
|
|
7021
|
+
let shownCount = 0;
|
|
7022
|
+
let totalRelevant = 0;
|
|
7023
|
+
let totalGovernance = 0;
|
|
7044
7024
|
for (const type of types) {
|
|
7045
7025
|
if (type.entries.length === 0) continue;
|
|
7026
|
+
totalGovernance += type.entries.length;
|
|
7046
7027
|
const scored = type.entries.map((e) => ({ entry: e, score: scoreEntry(e, keywords) })).filter((s) => s.score > 0).sort((a, b) => b.score - a.score);
|
|
7047
7028
|
if (scored.length > 0) {
|
|
7048
7029
|
anyRelevant = true;
|
|
7030
|
+
totalRelevant += scored.length;
|
|
7031
|
+
const capped = scored.slice(0, MAX_ENTRIES_WITH_TASK);
|
|
7032
|
+
shownCount += capped.length;
|
|
7049
7033
|
lines.push(type.header);
|
|
7050
|
-
for (const s of
|
|
7034
|
+
for (const s of capped) {
|
|
7051
7035
|
lines.push(formatGovernanceEntry(s.entry));
|
|
7052
7036
|
}
|
|
7053
7037
|
lines.push("");
|
|
@@ -7062,10 +7046,15 @@ function buildOperatingProtocol(governanceOrStandards, task) {
|
|
|
7062
7046
|
}
|
|
7063
7047
|
lines.push("");
|
|
7064
7048
|
}
|
|
7049
|
+
lines.push(
|
|
7050
|
+
`_No governance matched task. Showing top ${MAX_ENTRIES_NO_TASK} per type. Run \`orient\` without a task to see all._`
|
|
7051
|
+
);
|
|
7052
|
+
} else {
|
|
7053
|
+
const overflow = totalRelevant > shownCount ? ` ${totalRelevant - shownCount} more matched \u2014 use \`entries action=search query="..."\` before proposing.` : "";
|
|
7054
|
+
lines.push(
|
|
7055
|
+
`_${shownCount} of ${totalGovernance} governance entries shown, filtered by task.${overflow}_`
|
|
7056
|
+
);
|
|
7065
7057
|
}
|
|
7066
|
-
lines.push(
|
|
7067
|
-
"_Governance filtered by task relevance. Run `orient` without a task to see all._"
|
|
7068
|
-
);
|
|
7069
7058
|
} else {
|
|
7070
7059
|
for (const type of types) {
|
|
7071
7060
|
if (type.entries.length === 0) continue;
|
|
@@ -7155,12 +7144,12 @@ function getInterviewInstructions(workspaceName) {
|
|
|
7155
7144
|
return {
|
|
7156
7145
|
systemPrompt: `You are activating the **${workspaceName}** Product Brain. Ask 1\u20132 focused questions, then extract structured knowledge and feed it to batch-capture. In Open governance mode, entries are committed automatically. In consensus/role mode, they stay as drafts for review.`,
|
|
7157
7146
|
question1: `**Q1 \u2014 What are you building and for whom?** Describe your product in 1\u20132 sentences and who it's for. (This becomes your Product Vision and primary Audience.)`,
|
|
7158
|
-
question2: `**Q2 (optional)
|
|
7147
|
+
question2: `**Q2 (optional)** What's one word or phrase that would trip someone up if they didn't know your context? (This becomes your first glossary term \u2014 the one that matters most.)`,
|
|
7159
7148
|
extractionGuidance: `After the user answers, extract:
|
|
7160
7149
|
- vision: the core product purpose (required)
|
|
7161
7150
|
- audience: who it's for (optional)
|
|
7162
7151
|
- techStack: technologies mentioned (optional, array)
|
|
7163
|
-
- keyTerms: domain terms (optional, array)
|
|
7152
|
+
- keyTerms: domain terms \u2014 Q2 answer (the word/phrase that trips people up) goes here (optional, array)
|
|
7164
7153
|
- keyDecisions: any decisions stated (optional, array)
|
|
7165
7154
|
- tensions: any pain points stated (optional, array)
|
|
7166
7155
|
|
|
@@ -7174,19 +7163,22 @@ Map to batch-capture entries:
|
|
|
7174
7163
|
captureInstructions: `Call batch-capture with the extracted entries. Omit \`autoCommit\` to follow workspace governance automatically, or pass \`autoCommit: false\` if the user wants a review-first pass. Keep descriptions concise (1\u20132 sentences each). Prefer 8 good entries over 15 mediocre ones \u2014 quality over volume. If batch-capture returns failedEntries, tell the user and retry those individually.`,
|
|
7175
7164
|
qualityNote: (
|
|
7176
7165
|
// FEAT-149: Retrieval-First Proof Moment.
|
|
7177
|
-
//
|
|
7178
|
-
//
|
|
7166
|
+
// The aha is friction elimination ("I'll never have to explain this again"),
|
|
7167
|
+
// not recognition ("it remembered me"). Simulate a FUTURE session, not a recap.
|
|
7179
7168
|
`After capturing, silently call graph action=suggest on the strategy or architecture entry to build connections.
|
|
7180
7169
|
|
|
7181
7170
|
**Then demonstrate the proof moment \u2014 this is the most important step:**
|
|
7182
7171
|
Based on what the user told you, invent a specific, plausible development task they might actually do next (e.g. "add user authentication" for a SaaS app, "build the recommendation engine" for a marketplace, "set up the API layer" for a developer tool). Call \`context action=gather task="<your invented task>"\` to load their captured knowledge.
|
|
7183
7172
|
|
|
7184
|
-
Present the
|
|
7185
|
-
"
|
|
7173
|
+
Present with the "tomorrow" framing \u2014 simulate a future session, not a recap:
|
|
7174
|
+
"Okay \u2014 let me show you what just changed. Tomorrow, if you open a new tab and ask me to help with **<task>**, I'd already know:
|
|
7175
|
+
- [vision applied to that task \u2014 one sentence]
|
|
7176
|
+
- [ICP applied \u2014 who this feature is for in their context]
|
|
7177
|
+
- [any key term that matters for this task]
|
|
7186
7178
|
|
|
7187
|
-
|
|
7179
|
+
That's context you won't have to explain again."
|
|
7188
7180
|
|
|
7189
|
-
End with: "**Try it \u2014 ask me
|
|
7181
|
+
End with: "**Try it right now** \u2014 ask me something you'd normally have to re-explain first. I'll answer like I've known your product for months."`
|
|
7190
7182
|
),
|
|
7191
7183
|
scanOffer: `After the proof moment, offer the codebase scan as a way to deepen the knowledge:
|
|
7192
7184
|
"Want me to learn even more? I can scan your project files (README, package.json, source structure) and pick up technical decisions, conventions, and architecture that I missed. Takes about 2 minutes."`
|
|
@@ -7221,6 +7213,9 @@ async function tryMarkOriented(agentSessionId) {
|
|
|
7221
7213
|
var startSchema = z15.object({
|
|
7222
7214
|
preset: z15.string().optional().describe(
|
|
7223
7215
|
"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."
|
|
7216
|
+
),
|
|
7217
|
+
task: z15.string().optional().describe(
|
|
7218
|
+
"What you're about to work on (e.g. 'implementing auth middleware', 'refactoring the API layer'). Filters governance to show only relevant principles, standards, and business rules."
|
|
7224
7219
|
)
|
|
7225
7220
|
});
|
|
7226
7221
|
function registerStartTools(server) {
|
|
@@ -7232,7 +7227,7 @@ function registerStartTools(server) {
|
|
|
7232
7227
|
inputSchema: startSchema,
|
|
7233
7228
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
7234
7229
|
},
|
|
7235
|
-
withEnvelope(async ({ preset }) => {
|
|
7230
|
+
withEnvelope(async ({ preset, task }) => {
|
|
7236
7231
|
const errors = [];
|
|
7237
7232
|
const agentSessionId = getAgentSessionId();
|
|
7238
7233
|
let wsCtx = null;
|
|
@@ -7292,7 +7287,7 @@ function registerStartTools(server) {
|
|
|
7292
7287
|
void mcpMutation("chain.setOnboardingCompleted", {}).catch(() => {
|
|
7293
7288
|
});
|
|
7294
7289
|
}
|
|
7295
|
-
const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors);
|
|
7290
|
+
const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors, task);
|
|
7296
7291
|
return {
|
|
7297
7292
|
content: [{ type: "text", text: orientResult.text }],
|
|
7298
7293
|
structuredContent: orientResult.structuredContent
|
|
@@ -7302,73 +7297,83 @@ function registerStartTools(server) {
|
|
|
7302
7297
|
}
|
|
7303
7298
|
function buildBlankResponse(wsCtx, sessionCtx) {
|
|
7304
7299
|
const instructions = getInterviewInstructions(wsCtx.workspaceName);
|
|
7300
|
+
const presetList = listPresets();
|
|
7301
|
+
const presets = presetList.map((p) => p.id).join(", ");
|
|
7305
7302
|
const text = [
|
|
7303
|
+
// ── User-facing welcome — present this to the user ─────────────────
|
|
7306
7304
|
`# Welcome to ${wsCtx.workspaceName}`,
|
|
7307
7305
|
"",
|
|
7308
|
-
"Your workspace is fresh. Let's
|
|
7306
|
+
"Your workspace is fresh. Let's bring it to life.",
|
|
7309
7307
|
"",
|
|
7310
|
-
"
|
|
7308
|
+
"Tell me what you're building \u2014 paste a doc, describe it in a sentence, or say **scan my codebase**.",
|
|
7311
7309
|
"",
|
|
7312
|
-
"
|
|
7310
|
+
"---",
|
|
7313
7311
|
"",
|
|
7314
|
-
|
|
7315
|
-
"
|
|
7316
|
-
`> ${instructions.question1}`,
|
|
7312
|
+
// ── Agent instructions — follow these exactly, do not present them ──
|
|
7313
|
+
"## Agent Instructions",
|
|
7317
7314
|
"",
|
|
7318
|
-
"
|
|
7315
|
+
"### Step 1 \u2014 Open with their name (FLO-1)",
|
|
7316
|
+
`Ask: _"What should I call you?"_ before asking about the product. Use their name throughout.`,
|
|
7319
7317
|
"",
|
|
7320
|
-
"
|
|
7321
|
-
"Skip the questions and jump straight to reading the project files.",
|
|
7318
|
+
"### Step 2 \u2014 Detect path from their first response (no menu, no numbered options)",
|
|
7322
7319
|
"",
|
|
7323
|
-
"
|
|
7324
|
-
`Seed collections for a domain. Available: software-product, content-business, agency, saas-api, general.`,
|
|
7320
|
+
"Read signals from the user's first substantive message and branch silently:",
|
|
7325
7321
|
"",
|
|
7326
|
-
"
|
|
7322
|
+
"**document** \u2014 paste >200 words, markdown headers, or says 'here's my doc/brief/spec'",
|
|
7323
|
+
"\u2192 Extract vision, audience, keyTerms, keyDecisions, tensions from the document.",
|
|
7324
|
+
"\u2192 Present a numbered confirmation list before capturing. Capture only confirmed entries.",
|
|
7325
|
+
"\u2192 Fallback: if sparse or unclear, switch to interview path.",
|
|
7327
7326
|
"",
|
|
7328
|
-
"
|
|
7327
|
+
"**codebase** \u2014 mentions 'scan', 'codebase', README, or provides a file path",
|
|
7328
|
+
`\u2192 First ask: _"Tell me one thing about this product that's not obvious from the code \u2014 what's it actually for?"_`,
|
|
7329
|
+
"\u2192 Then read: README.md, package.json / pyproject.toml / Cargo.toml, top-level source dirs.",
|
|
7330
|
+
"\u2192 Infer 5\u20138 entries. Present numbered list for confirmation. Capture only confirmed entries.",
|
|
7331
|
+
"\u2192 Fallback: if no README or empty project, immediately switch to interview path.",
|
|
7329
7332
|
"",
|
|
7330
|
-
|
|
7333
|
+
`**preset** \u2014 mentions a domain keyword (SaaS, agency, content business, API)`,
|
|
7334
|
+
`\u2192 Confirm which preset fits. Available: ${presets}.`,
|
|
7335
|
+
"\u2192 Call `start` with `preset: '<chosen-preset>'`.",
|
|
7336
|
+
"\u2192 Fallback: if no preset matches or seeding fails, switch to interview path.",
|
|
7331
7337
|
"",
|
|
7332
|
-
"
|
|
7338
|
+
"**interview** (default \u2014 1\u20135 sentence description or anything else)",
|
|
7339
|
+
`\u2192 ${instructions.systemPrompt}`,
|
|
7340
|
+
`\u2192 Q1: ${instructions.question1}`,
|
|
7341
|
+
`\u2192 Q2 (optional): _"What's one word or phrase that would trip someone up if they didn't know your context?"_ (This becomes their first glossary term.)`,
|
|
7333
7342
|
instructions.extractionGuidance,
|
|
7343
|
+
`\u2192 ${instructions.captureInstructions}`,
|
|
7344
|
+
"\u2192 Omit `autoCommit` \u2014 workspace governance applies automatically.",
|
|
7334
7345
|
"",
|
|
7335
|
-
|
|
7336
|
-
"Omit `autoCommit` \u2014 capture tools auto-commit in Open mode and create drafts in consensus/role mode.",
|
|
7346
|
+
"### Step 3 \u2014 Proof moment (most important \u2014 run after every path)",
|
|
7337
7347
|
"",
|
|
7338
7348
|
instructions.qualityNote,
|
|
7339
7349
|
"",
|
|
7340
|
-
|
|
7350
|
+
"### Step 4 \u2014 Deepen (offer after proof moment)",
|
|
7341
7351
|
"",
|
|
7352
|
+
instructions.scanOffer,
|
|
7342
7353
|
"If they say yes, scan README.md, package.json/pyproject.toml/Cargo.toml, and top-level source dirs.",
|
|
7343
|
-
"Infer entries
|
|
7344
|
-
"
|
|
7345
|
-
"",
|
|
7346
|
-
"## Path 2: Scan first",
|
|
7347
|
-
"",
|
|
7348
|
-
"Read these files:",
|
|
7349
|
-
"1. README.md (or equivalent top-level docs)",
|
|
7350
|
-
"2. package.json / pyproject.toml / Cargo.toml (project manifest)",
|
|
7351
|
-
"3. Top-level source directories and their purpose",
|
|
7352
|
-
"",
|
|
7353
|
-
"Infer 5\u20138 product knowledge entries (product name, tech decisions, features, conventions).",
|
|
7354
|
-
"Present as a numbered list. Ask which to keep. Only after confirmation, capture the confirmed entries and let normal workspace governance apply.",
|
|
7355
|
-
"",
|
|
7356
|
-
"**If the codebase is sparse** (no README, empty project): fall back to Path 1 (ask the user about their product).",
|
|
7357
|
-
"",
|
|
7358
|
-
"_Prefer 5 specific entries over 8 generic ones. Skip anything that would apply to any codebase._",
|
|
7359
|
-
"",
|
|
7360
|
-
"**After scan capture, run the proof moment (same as Path 1):**",
|
|
7361
|
-
instructions.qualityNote,
|
|
7362
|
-
"",
|
|
7363
|
-
"## Path 3: Preset",
|
|
7364
|
-
"",
|
|
7365
|
-
"If the user picks a preset, call `start` again with `preset: '<chosen-preset>'`."
|
|
7354
|
+
"Infer entries with the product context you now have. Present as a numbered list for confirmation.",
|
|
7355
|
+
"Capture only confirmed entries."
|
|
7366
7356
|
].join("\n");
|
|
7367
7357
|
return {
|
|
7368
7358
|
text,
|
|
7369
7359
|
structuredContent: success(
|
|
7370
|
-
|
|
7371
|
-
{
|
|
7360
|
+
"Workspace is blank. Ready for activation \u2014 interview, document, codebase scan, or preset.",
|
|
7361
|
+
{
|
|
7362
|
+
stage: "blank",
|
|
7363
|
+
interviewSchema: {
|
|
7364
|
+
fields: ["vision", "audience", "techStack", "keyTerms", "keyDecisions", "tensions"],
|
|
7365
|
+
mapping: {
|
|
7366
|
+
vision: "strategy",
|
|
7367
|
+
audience: "audiences",
|
|
7368
|
+
techStack: "architecture + top 3 terms \u2192 glossary",
|
|
7369
|
+
keyTerms: "glossary",
|
|
7370
|
+
keyDecisions: "decisions",
|
|
7371
|
+
tensions: "tensions"
|
|
7372
|
+
}
|
|
7373
|
+
},
|
|
7374
|
+
availablePresets: presetList.map((p) => ({ id: p.id, name: p.name })),
|
|
7375
|
+
...sessionCtx
|
|
7376
|
+
},
|
|
7372
7377
|
[
|
|
7373
7378
|
{ tool: "capture", description: "Capture product knowledge", parameters: {} },
|
|
7374
7379
|
{ tool: "start", description: "Seed with a preset", parameters: { preset: "software-product" } }
|
|
@@ -7524,7 +7529,7 @@ function pickNextAction(gaps, openTensions, priorSessions) {
|
|
|
7524
7529
|
}
|
|
7525
7530
|
return null;
|
|
7526
7531
|
}
|
|
7527
|
-
async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
7532
|
+
async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
7528
7533
|
const wsFullCtx = await getWorkspaceContext();
|
|
7529
7534
|
const { ageDays, isNeglected } = computeWorkspaceAge(wsFullCtx.createdAt);
|
|
7530
7535
|
let priorSessions = [];
|
|
@@ -7583,42 +7588,6 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
7583
7588
|
lines.push("_Need a collection that doesn't exist yet (e.g. audiences, personas, metrics)? Just ask \u2014 I'll set it up._");
|
|
7584
7589
|
lines.push("");
|
|
7585
7590
|
} else if (isHighReadiness) {
|
|
7586
|
-
let wsPrinciples = [];
|
|
7587
|
-
let wsStandards = [];
|
|
7588
|
-
let wsBusinessRules = [];
|
|
7589
|
-
try {
|
|
7590
|
-
const govSlugs = ["principles", "standards", "business-rules"];
|
|
7591
|
-
const govLabels = {
|
|
7592
|
-
principles: "Principles",
|
|
7593
|
-
standards: "Standards",
|
|
7594
|
-
"business-rules": "Business Rules"
|
|
7595
|
-
};
|
|
7596
|
-
const govEntries = [];
|
|
7597
|
-
for (const slug of govSlugs) {
|
|
7598
|
-
const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
|
|
7599
|
-
const active = (entries ?? []).filter((e) => e.status === "active");
|
|
7600
|
-
if (active.length > 0) {
|
|
7601
|
-
govEntries.push({ slug, entries: active });
|
|
7602
|
-
if (slug === "principles") wsPrinciples = active;
|
|
7603
|
-
if (slug === "standards") wsStandards = active;
|
|
7604
|
-
if (slug === "business-rules") wsBusinessRules = active;
|
|
7605
|
-
}
|
|
7606
|
-
}
|
|
7607
|
-
if (govEntries.length > 0) {
|
|
7608
|
-
lines.push("");
|
|
7609
|
-
lines.push("## Workspace Governance \u2014 constraints");
|
|
7610
|
-
lines.push("_These constrain what you build. If your proposal conflicts with any of these, stop and flag it._");
|
|
7611
|
-
lines.push("");
|
|
7612
|
-
for (const g of govEntries) {
|
|
7613
|
-
lines.push(`**${govLabels[g.slug]}:**`);
|
|
7614
|
-
for (const e of g.entries.slice(0, 8)) {
|
|
7615
|
-
lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
|
|
7616
|
-
}
|
|
7617
|
-
lines.push("");
|
|
7618
|
-
}
|
|
7619
|
-
}
|
|
7620
|
-
} catch {
|
|
7621
|
-
}
|
|
7622
7591
|
let activeBets = [];
|
|
7623
7592
|
try {
|
|
7624
7593
|
const betEntries = await mcpQuery("chain.listEntries", { collectionSlug: "bets" });
|
|
@@ -7635,6 +7604,19 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
7635
7604
|
}
|
|
7636
7605
|
} catch {
|
|
7637
7606
|
}
|
|
7607
|
+
let wsPrinciples = [];
|
|
7608
|
+
let wsStandards = [];
|
|
7609
|
+
let wsBusinessRules = [];
|
|
7610
|
+
try {
|
|
7611
|
+
for (const slug of ["principles", "standards", "business-rules"]) {
|
|
7612
|
+
const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
|
|
7613
|
+
const active = (entries ?? []).filter((e) => e.status === "active");
|
|
7614
|
+
if (slug === "principles") wsPrinciples = active;
|
|
7615
|
+
if (slug === "standards") wsStandards = active;
|
|
7616
|
+
if (slug === "business-rules") wsBusinessRules = active;
|
|
7617
|
+
}
|
|
7618
|
+
} catch {
|
|
7619
|
+
}
|
|
7638
7620
|
const mapGovEntry = (e) => ({
|
|
7639
7621
|
entryId: e.entryId,
|
|
7640
7622
|
name: e.name,
|
|
@@ -7644,7 +7626,7 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
7644
7626
|
principles: wsPrinciples.map(mapGovEntry),
|
|
7645
7627
|
standards: wsStandards.map(mapGovEntry),
|
|
7646
7628
|
businessRules: wsBusinessRules.map(mapGovEntry)
|
|
7647
|
-
}));
|
|
7629
|
+
}, task));
|
|
7648
7630
|
const plannedWork = await queryPlannedWork();
|
|
7649
7631
|
if (hasPlannedWork(plannedWork)) {
|
|
7650
7632
|
lines.push("");
|
|
@@ -10241,29 +10223,6 @@ function registerHealthTools(server) {
|
|
|
10241
10223
|
orientEntries.staleEntries.forEach((e) => lines.push(fmt(e)));
|
|
10242
10224
|
lines.push("");
|
|
10243
10225
|
}
|
|
10244
|
-
const hasPrinciples = orientEntries.principles?.length > 0;
|
|
10245
|
-
const hasStandards = orientEntries.standards?.length > 0;
|
|
10246
|
-
const hasBusinessRules = orientEntries.businessRules.length > 0;
|
|
10247
|
-
if (hasPrinciples || hasStandards || hasBusinessRules) {
|
|
10248
|
-
lines.push("## Workspace Governance \u2014 constraints");
|
|
10249
|
-
lines.push("_These constrain what you build. If your proposal conflicts with any of these, stop and flag it. Do not design around them without explicit user confirmation._");
|
|
10250
|
-
lines.push("");
|
|
10251
|
-
if (hasPrinciples) {
|
|
10252
|
-
lines.push("**Principles** \u2014 beliefs that guide decisions:");
|
|
10253
|
-
orientEntries.principles.forEach((e) => lines.push(fmt(e)));
|
|
10254
|
-
lines.push("");
|
|
10255
|
-
}
|
|
10256
|
-
if (hasStandards) {
|
|
10257
|
-
lines.push("**Standards** \u2014 conventions for how work is done:");
|
|
10258
|
-
orientEntries.standards.forEach((e) => lines.push(fmt(e)));
|
|
10259
|
-
lines.push("");
|
|
10260
|
-
}
|
|
10261
|
-
if (hasBusinessRules) {
|
|
10262
|
-
lines.push("**Business Rules** \u2014 system constraints:");
|
|
10263
|
-
orientEntries.businessRules.forEach((e) => lines.push(fmt(e)));
|
|
10264
|
-
lines.push("");
|
|
10265
|
-
}
|
|
10266
|
-
}
|
|
10267
10226
|
if (orientEntries.architectureNotes.length > 0) {
|
|
10268
10227
|
lines.push("## Architecture notes");
|
|
10269
10228
|
orientEntries.architectureNotes.forEach((e) => lines.push(fmt(e)));
|
|
@@ -11745,4 +11704,4 @@ export {
|
|
|
11745
11704
|
SERVER_VERSION,
|
|
11746
11705
|
createProductBrainServer
|
|
11747
11706
|
};
|
|
11748
|
-
//# sourceMappingURL=chunk-
|
|
11707
|
+
//# sourceMappingURL=chunk-VBKAAFR6.js.map
|