@productbrain/mcp 0.0.1-beta.64 → 0.0.1-beta.66
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-GKMXGRJW.js → chunk-4IQ7A5R4.js} +327 -397
- package/dist/chunk-4IQ7A5R4.js.map +1 -0
- package/dist/{chunk-DM5DZC7B.js → chunk-UWILSX73.js} +59 -1
- package/dist/chunk-UWILSX73.js.map +1 -0
- package/dist/http.js +5 -4
- 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(
|
|
@@ -3199,6 +3166,7 @@ var IMPLEMENTATION_REVIEW_WORKFLOW_DESCRIPTOR = {
|
|
|
3199
3166
|
## Review scope (CRITICAL)
|
|
3200
3167
|
|
|
3201
3168
|
- **Default scope is this conversation only.** Only the work discussed or produced in this conversation is in scope. Do not review or touch files, BETs, or work outside that scope unless the user explicitly expands it (e.g. "also review the auth module").
|
|
3169
|
+
- **Every file touched must be explicit.** Scope is not complete until you have listed every file that was touched (created, modified, or deleted) in the work under review. Use git status/diff and conversation context to enumerate them. The review then assesses holistic coherence: does this set of changes fit our system, bring us closer to our vision, and make our code and architecture cleaner?
|
|
3202
3170
|
- **Infer scope first.** Use conversation context, git status/diff, and recent edits to determine which BET, feature, or files are in scope. Call orient/start and \`context action=gather\` with the inferred scope; do not ask the user to pick what to review unless it's ambiguous.
|
|
3203
3171
|
- **Ask only to clarify boundaries.** If scope is ambiguous after inference, ask a single, focused question to clarify (e.g. "Should this review include only the changes we just made, or the whole feature?"). Do not ask open-ended "what should we review?" or "which work area?" unless inference failed.
|
|
3204
3172
|
- **State scope explicitly in Round 01.** After orient and context gather, state in one sentence what is in scope (e.g. "Scope: changes in this conversation for BET-72" or "Scope: files X, Y and BET-72"). Confirm only if the user might reasonably expect something else.
|
|
@@ -3233,11 +3201,11 @@ When reviewing test results: (a) test staleness \u2014 code changed, test not up
|
|
|
3233
3201
|
num: "01",
|
|
3234
3202
|
label: "Orient & Scope",
|
|
3235
3203
|
type: "open",
|
|
3236
|
-
instruction: "Apply the Review scope rules: infer scope from this conversation, git diff, and recent edits (default = work in this conversation only). Call orient or start, then context action=gather for the inferred BET/feature. State scope in one explicit sentence. Only ask the participant if scope is ambiguous.",
|
|
3237
|
-
facilitatorGuidance: "Infer scope from conversation context, git status/diff, and recent edits \u2014 default is work in this conversation only. Call orient or start to load governance and active bets. Use context action=gather task='implementation review for [inferred-BET-or-feature]' to load related entries. State scope explicitly in one sentence (e.g. 'Scope: changes in this conversation for BET-72'). Present: stated scope, relevant BETs/DECs/BRs, and any stale entries orient surfaces. Ask the participant only if scope is ambiguous (e.g. 'Only the files we changed, or the whole feature?').",
|
|
3204
|
+
instruction: "Apply the Review scope rules: infer scope from this conversation, git diff, and recent edits (default = work in this conversation only). Enumerate every file touched (created, modified, deleted). Call orient or start, then context action=gather for the inferred BET/feature. State scope in one explicit sentence and list all files in scope. Only ask the participant if scope is ambiguous. The review will assess: holistic coherence with our system, whether this brings us closer to our vision, and whether it makes our code and architecture cleaner.",
|
|
3205
|
+
facilitatorGuidance: "Infer scope from conversation context, git status/diff, and recent edits \u2014 default is work in this conversation only. List every file touched (created, modified, or deleted) in the work under review; scope is not complete without this list. Call orient or start to load governance and active bets. Use context action=gather task='implementation review for [inferred-BET-or-feature]' to load related entries. State scope explicitly in one sentence (e.g. 'Scope: changes in this conversation for BET-72'). Present: stated scope, the full list of files touched, relevant BETs/DECs/BRs, and any stale entries orient surfaces. Frame the review: we will assess holistic coherence with our system, whether this brings us closer to our vision, and whether it makes our code and architecture cleaner. Ask the participant only if scope is ambiguous (e.g. 'Only the files we changed, or the whole feature?').",
|
|
3238
3206
|
outputSchema: {
|
|
3239
3207
|
field: "scope",
|
|
3240
|
-
description: "Explicit scope statement
|
|
3208
|
+
description: "Explicit scope statement, every file touched, BET/feature IDs and related Chain context",
|
|
3241
3209
|
format: "structured"
|
|
3242
3210
|
},
|
|
3243
3211
|
maxDurationHint: "3 min"
|
|
@@ -3289,8 +3257,8 @@ When reviewing test results: (a) test staleness \u2014 code changed, test not up
|
|
|
3289
3257
|
num: "05",
|
|
3290
3258
|
label: "Synthesis & Sign-Off",
|
|
3291
3259
|
type: "commit",
|
|
3292
|
-
instruction: "Summarize the review. Refactoring needed? Ship or conditional? End with BET/chain IDs.",
|
|
3293
|
-
facilitatorGuidance: "Present the full synthesis: standards pass/fail, code grade, test honesty, chain validation. Recommend: ship, ship with conditions, or do not ship. CRITICAL: The output MUST end with one sentence: 'BET/feature IDs reviewed: [IDs]. [One-line summary].' Example: 'BET-xxx, STD-xxx reviewed. Conditional ship \u2014 fix [specific issues] and add [missing tests].'",
|
|
3260
|
+
instruction: "Summarize the review. Answer: Does this bring us closer to our vision and make our code and architecture cleaner? Refactoring needed? Ship or conditional? End with BET/chain IDs.",
|
|
3261
|
+
facilitatorGuidance: "Present the full synthesis: standards pass/fail, code grade, test honesty, chain validation. Explicitly answer: Does this bring us closer to our vision and make our code and architecture cleaner? Recommend: ship, ship with conditions, or do not ship. CRITICAL: The output MUST end with one sentence: 'BET/feature IDs reviewed: [IDs]. [One-line summary].' Example: 'BET-xxx, STD-xxx reviewed. Conditional ship \u2014 fix [specific issues] and add [missing tests].'",
|
|
3294
3262
|
outputSchema: {
|
|
3295
3263
|
field: "synthesis",
|
|
3296
3264
|
description: "Full review synthesis with BET/chain IDs",
|
|
@@ -6305,7 +6273,7 @@ async function handleCommitConstellation(args) {
|
|
|
6305
6273
|
}
|
|
6306
6274
|
let contradictionWarnings = [];
|
|
6307
6275
|
try {
|
|
6308
|
-
const { runContradictionCheck } = await import("./smart-capture-
|
|
6276
|
+
const { runContradictionCheck } = await import("./smart-capture-EGTUM4XP.js");
|
|
6309
6277
|
const descField = betData.problem ?? betData.description ?? "";
|
|
6310
6278
|
contradictionWarnings = await runContradictionCheck(
|
|
6311
6279
|
betEntry.name ?? betId,
|
|
@@ -6966,23 +6934,32 @@ function buildPlannedWorkSection(work, priorSessions, recoveryBlock) {
|
|
|
6966
6934
|
// src/tools/orient-shared.ts
|
|
6967
6935
|
function runAlignmentCheck(task, activeBets, taskContextHits) {
|
|
6968
6936
|
const betNames = activeBets.map((b) => b.name);
|
|
6969
|
-
const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length >
|
|
6937
|
+
const taskWords = task.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
6970
6938
|
const matchingBet = activeBets.find((b) => {
|
|
6971
6939
|
const name = (b.name ?? "").toLowerCase();
|
|
6972
6940
|
return taskWords.some((w) => name.includes(w));
|
|
6973
6941
|
});
|
|
6942
|
+
if (matchingBet) {
|
|
6943
|
+
return { aligned: true, matchedBet: matchingBet.name, matchSource: "active_bet", betNames };
|
|
6944
|
+
}
|
|
6974
6945
|
const betHits = (taskContextHits ?? []).filter(
|
|
6975
6946
|
(e) => e.collectionSlug === "bets"
|
|
6976
6947
|
);
|
|
6977
|
-
|
|
6978
|
-
|
|
6979
|
-
|
|
6948
|
+
if (betHits.length > 0) {
|
|
6949
|
+
return { aligned: true, matchedBet: betHits[0]?.name ?? null, matchSource: "task_context", betNames };
|
|
6950
|
+
}
|
|
6951
|
+
return { aligned: false, matchedBet: null, matchSource: null, betNames };
|
|
6980
6952
|
}
|
|
6981
6953
|
function buildAlignmentCheckLines(result) {
|
|
6982
6954
|
const lines = ["## Alignment Check"];
|
|
6983
|
-
if (result.aligned) {
|
|
6955
|
+
if (result.aligned && result.matchSource === "active_bet") {
|
|
6984
6956
|
lines.push(
|
|
6985
|
-
`Task
|
|
6957
|
+
`Task aligns with active bet: **${result.matchedBet}**. Proceed.`
|
|
6958
|
+
);
|
|
6959
|
+
} else if (result.aligned && result.matchSource === "task_context") {
|
|
6960
|
+
const noActiveBets = result.betNames.length === 0;
|
|
6961
|
+
lines.push(
|
|
6962
|
+
`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
6963
|
);
|
|
6987
6964
|
} else if (result.betNames.length === 0) {
|
|
6988
6965
|
lines.push(
|
|
@@ -6998,16 +6975,18 @@ function buildAlignmentCheckLines(result) {
|
|
|
6998
6975
|
lines.push("");
|
|
6999
6976
|
return lines;
|
|
7000
6977
|
}
|
|
7001
|
-
var
|
|
6978
|
+
var CORE_PROTOCOL_BASE = [
|
|
7002
6979
|
'**Search before proposing.** Before suggesting new features, architecture, or changes, search the Chain: `entries action=search query="<relevant terms>"`. Build on what exists.',
|
|
7003
6980
|
"**Reference by ID.** When discussing a topic that has Chain entries, cite them by entry ID (e.g. `DEC-50`, `PRI-3`). This keeps conversations grounded in shared knowledge.",
|
|
7004
6981
|
"**Check scope.** If the proposed work doesn't fall under an active bet, stop and say so. Do not design implementation for out-of-scope work without explicit user go-ahead.",
|
|
7005
|
-
`**Capture continuously.** When a decision, tension, insight, or new term surfaces during work, capture it as a draft immediately. Don't defer to "later."
|
|
7006
|
-
"**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."
|
|
6982
|
+
`**Capture continuously.** When a decision, tension, insight, or new term surfaces during work, capture it as a draft immediately. Don't defer to "later."`
|
|
7007
6983
|
];
|
|
6984
|
+
var RULE5_WITH_GOVERNANCE = "**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.";
|
|
6985
|
+
var RULE5_COMPACT = '**Validate against governance.** Before proposing or building anything, call `orient task="<your task>"` to load relevant governance. If your proposal conflicts with a principle, standard, or business rule \u2014 stop, name the conflict, and get explicit user confirmation.';
|
|
7008
6986
|
var MAX_ENTRIES_NO_TASK = 3;
|
|
6987
|
+
var MAX_ENTRIES_WITH_TASK = 5;
|
|
7009
6988
|
function extractKeywords(text) {
|
|
7010
|
-
return [...new Set(text.toLowerCase().split(/\s+/).filter((w) => w.length >
|
|
6989
|
+
return [...new Set(text.toLowerCase().split(/\s+/).filter((w) => w.length > 2))];
|
|
7011
6990
|
}
|
|
7012
6991
|
function scoreEntry(entry, keywords) {
|
|
7013
6992
|
const text = `${entry.name} ${entry.description ?? ""}`.toLowerCase();
|
|
@@ -7020,16 +6999,18 @@ function formatGovernanceEntry(entry) {
|
|
|
7020
6999
|
}
|
|
7021
7000
|
function buildOperatingProtocol(governanceOrStandards, task) {
|
|
7022
7001
|
const governance = Array.isArray(governanceOrStandards) ? { standards: governanceOrStandards } : governanceOrStandards ?? {};
|
|
7002
|
+
const { principles = [], standards = [], businessRules = [] } = governance;
|
|
7003
|
+
const hasGovernance = principles.length > 0 || standards.length > 0 || businessRules.length > 0;
|
|
7004
|
+
const rule5 = hasGovernance ? RULE5_WITH_GOVERNANCE : RULE5_COMPACT;
|
|
7005
|
+
const protocol = [...CORE_PROTOCOL_BASE, rule5];
|
|
7023
7006
|
const lines = [
|
|
7024
7007
|
"## Operating Protocol",
|
|
7025
7008
|
"_How to work in this workspace. Follow these before and during every task._",
|
|
7026
7009
|
""
|
|
7027
7010
|
];
|
|
7028
|
-
for (let i = 0; i <
|
|
7029
|
-
lines.push(`${i + 1}. ${
|
|
7011
|
+
for (let i = 0; i < protocol.length; i++) {
|
|
7012
|
+
lines.push(`${i + 1}. ${protocol[i]}`);
|
|
7030
7013
|
}
|
|
7031
|
-
const { principles = [], standards = [], businessRules = [] } = governance;
|
|
7032
|
-
const hasGovernance = principles.length > 0 || standards.length > 0 || businessRules.length > 0;
|
|
7033
7014
|
if (hasGovernance) {
|
|
7034
7015
|
lines.push("");
|
|
7035
7016
|
lines.push("### Workspace governance directives");
|
|
@@ -7041,13 +7022,20 @@ function buildOperatingProtocol(governanceOrStandards, task) {
|
|
|
7041
7022
|
if (task) {
|
|
7042
7023
|
const keywords = extractKeywords(task);
|
|
7043
7024
|
let anyRelevant = false;
|
|
7025
|
+
let shownCount = 0;
|
|
7026
|
+
let totalRelevant = 0;
|
|
7027
|
+
let totalGovernance = 0;
|
|
7044
7028
|
for (const type of types) {
|
|
7045
7029
|
if (type.entries.length === 0) continue;
|
|
7030
|
+
totalGovernance += type.entries.length;
|
|
7046
7031
|
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
7032
|
if (scored.length > 0) {
|
|
7048
7033
|
anyRelevant = true;
|
|
7034
|
+
totalRelevant += scored.length;
|
|
7035
|
+
const capped = scored.slice(0, MAX_ENTRIES_WITH_TASK);
|
|
7036
|
+
shownCount += capped.length;
|
|
7049
7037
|
lines.push(type.header);
|
|
7050
|
-
for (const s of
|
|
7038
|
+
for (const s of capped) {
|
|
7051
7039
|
lines.push(formatGovernanceEntry(s.entry));
|
|
7052
7040
|
}
|
|
7053
7041
|
lines.push("");
|
|
@@ -7062,10 +7050,15 @@ function buildOperatingProtocol(governanceOrStandards, task) {
|
|
|
7062
7050
|
}
|
|
7063
7051
|
lines.push("");
|
|
7064
7052
|
}
|
|
7053
|
+
lines.push(
|
|
7054
|
+
`_No governance matched task. Showing top ${MAX_ENTRIES_NO_TASK} per type. Run \`orient\` without a task to see all._`
|
|
7055
|
+
);
|
|
7056
|
+
} else {
|
|
7057
|
+
const overflow = totalRelevant > shownCount ? ` ${totalRelevant - shownCount} more matched \u2014 use \`entries action=search query="..."\` before proposing.` : "";
|
|
7058
|
+
lines.push(
|
|
7059
|
+
`_${shownCount} of ${totalGovernance} governance entries shown, filtered by task.${overflow}_`
|
|
7060
|
+
);
|
|
7065
7061
|
}
|
|
7066
|
-
lines.push(
|
|
7067
|
-
"_Governance filtered by task relevance. Run `orient` without a task to see all._"
|
|
7068
|
-
);
|
|
7069
7062
|
} else {
|
|
7070
7063
|
for (const type of types) {
|
|
7071
7064
|
if (type.entries.length === 0) continue;
|
|
@@ -7155,12 +7148,12 @@ function getInterviewInstructions(workspaceName) {
|
|
|
7155
7148
|
return {
|
|
7156
7149
|
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
7150
|
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)
|
|
7151
|
+
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
7152
|
extractionGuidance: `After the user answers, extract:
|
|
7160
7153
|
- vision: the core product purpose (required)
|
|
7161
7154
|
- audience: who it's for (optional)
|
|
7162
7155
|
- techStack: technologies mentioned (optional, array)
|
|
7163
|
-
- keyTerms: domain terms (optional, array)
|
|
7156
|
+
- keyTerms: domain terms \u2014 Q2 answer (the word/phrase that trips people up) goes here (optional, array)
|
|
7164
7157
|
- keyDecisions: any decisions stated (optional, array)
|
|
7165
7158
|
- tensions: any pain points stated (optional, array)
|
|
7166
7159
|
|
|
@@ -7174,19 +7167,22 @@ Map to batch-capture entries:
|
|
|
7174
7167
|
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
7168
|
qualityNote: (
|
|
7176
7169
|
// FEAT-149: Retrieval-First Proof Moment.
|
|
7177
|
-
//
|
|
7178
|
-
//
|
|
7170
|
+
// The aha is friction elimination ("I'll never have to explain this again"),
|
|
7171
|
+
// not recognition ("it remembered me"). Simulate a FUTURE session, not a recap.
|
|
7179
7172
|
`After capturing, silently call graph action=suggest on the strategy or architecture entry to build connections.
|
|
7180
7173
|
|
|
7181
7174
|
**Then demonstrate the proof moment \u2014 this is the most important step:**
|
|
7182
7175
|
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
7176
|
|
|
7184
|
-
Present the
|
|
7185
|
-
"
|
|
7177
|
+
Present with the "tomorrow" framing \u2014 simulate a future session, not a recap:
|
|
7178
|
+
"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:
|
|
7179
|
+
- [vision applied to that task \u2014 one sentence]
|
|
7180
|
+
- [ICP applied \u2014 who this feature is for in their context]
|
|
7181
|
+
- [any key term that matters for this task]
|
|
7186
7182
|
|
|
7187
|
-
|
|
7183
|
+
That's context you won't have to explain again."
|
|
7188
7184
|
|
|
7189
|
-
End with: "**Try it \u2014 ask me
|
|
7185
|
+
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
7186
|
),
|
|
7191
7187
|
scanOffer: `After the proof moment, offer the codebase scan as a way to deepen the knowledge:
|
|
7192
7188
|
"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 +7217,9 @@ async function tryMarkOriented(agentSessionId) {
|
|
|
7221
7217
|
var startSchema = z15.object({
|
|
7222
7218
|
preset: z15.string().optional().describe(
|
|
7223
7219
|
"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."
|
|
7220
|
+
),
|
|
7221
|
+
task: z15.string().optional().describe(
|
|
7222
|
+
"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
7223
|
)
|
|
7225
7224
|
});
|
|
7226
7225
|
function registerStartTools(server) {
|
|
@@ -7232,7 +7231,7 @@ function registerStartTools(server) {
|
|
|
7232
7231
|
inputSchema: startSchema,
|
|
7233
7232
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
7234
7233
|
},
|
|
7235
|
-
withEnvelope(async ({ preset }) => {
|
|
7234
|
+
withEnvelope(async ({ preset, task }) => {
|
|
7236
7235
|
const errors = [];
|
|
7237
7236
|
const agentSessionId = getAgentSessionId();
|
|
7238
7237
|
let wsCtx = null;
|
|
@@ -7292,7 +7291,7 @@ function registerStartTools(server) {
|
|
|
7292
7291
|
void mcpMutation("chain.setOnboardingCompleted", {}).catch(() => {
|
|
7293
7292
|
});
|
|
7294
7293
|
}
|
|
7295
|
-
const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors);
|
|
7294
|
+
const orientResult = await buildOrientResponse(wsCtx, agentSessionId, errors, task);
|
|
7296
7295
|
return {
|
|
7297
7296
|
content: [{ type: "text", text: orientResult.text }],
|
|
7298
7297
|
structuredContent: orientResult.structuredContent
|
|
@@ -7302,73 +7301,83 @@ function registerStartTools(server) {
|
|
|
7302
7301
|
}
|
|
7303
7302
|
function buildBlankResponse(wsCtx, sessionCtx) {
|
|
7304
7303
|
const instructions = getInterviewInstructions(wsCtx.workspaceName);
|
|
7304
|
+
const presetList = listPresets();
|
|
7305
|
+
const presets = presetList.map((p) => p.id).join(", ");
|
|
7305
7306
|
const text = [
|
|
7307
|
+
// ── User-facing welcome — present this to the user ─────────────────
|
|
7306
7308
|
`# Welcome to ${wsCtx.workspaceName}`,
|
|
7307
7309
|
"",
|
|
7308
|
-
"Your workspace is fresh. Let's
|
|
7310
|
+
"Your workspace is fresh. Let's bring it to life.",
|
|
7309
7311
|
"",
|
|
7310
|
-
"
|
|
7312
|
+
"Tell me what you're building \u2014 paste a doc, describe it in a sentence, or say **scan my codebase**.",
|
|
7311
7313
|
"",
|
|
7312
|
-
"
|
|
7314
|
+
"---",
|
|
7313
7315
|
"",
|
|
7314
|
-
|
|
7315
|
-
"
|
|
7316
|
-
`> ${instructions.question1}`,
|
|
7316
|
+
// ── Agent instructions — follow these exactly, do not present them ──
|
|
7317
|
+
"## Agent Instructions",
|
|
7317
7318
|
"",
|
|
7318
|
-
"
|
|
7319
|
+
"### Step 1 \u2014 Open with their name (FLO-1)",
|
|
7320
|
+
`Ask: _"What should I call you?"_ before asking about the product. Use their name throughout.`,
|
|
7319
7321
|
"",
|
|
7320
|
-
"
|
|
7321
|
-
"Skip the questions and jump straight to reading the project files.",
|
|
7322
|
+
"### Step 2 \u2014 Detect path from their first response (no menu, no numbered options)",
|
|
7322
7323
|
"",
|
|
7323
|
-
"
|
|
7324
|
-
`Seed collections for a domain. Available: software-product, content-business, agency, saas-api, general.`,
|
|
7324
|
+
"Read signals from the user's first substantive message and branch silently:",
|
|
7325
7325
|
"",
|
|
7326
|
-
"
|
|
7326
|
+
"**document** \u2014 paste >200 words, markdown headers, or says 'here's my doc/brief/spec'",
|
|
7327
|
+
"\u2192 Extract vision, audience, keyTerms, keyDecisions, tensions from the document.",
|
|
7328
|
+
"\u2192 Present a numbered confirmation list before capturing. Capture only confirmed entries.",
|
|
7329
|
+
"\u2192 Fallback: if sparse or unclear, switch to interview path.",
|
|
7327
7330
|
"",
|
|
7328
|
-
"
|
|
7331
|
+
"**codebase** \u2014 mentions 'scan', 'codebase', README, or provides a file path",
|
|
7332
|
+
`\u2192 First ask: _"Tell me one thing about this product that's not obvious from the code \u2014 what's it actually for?"_`,
|
|
7333
|
+
"\u2192 Then read: README.md, package.json / pyproject.toml / Cargo.toml, top-level source dirs.",
|
|
7334
|
+
"\u2192 Infer 5\u20138 entries. Present numbered list for confirmation. Capture only confirmed entries.",
|
|
7335
|
+
"\u2192 Fallback: if no README or empty project, immediately switch to interview path.",
|
|
7329
7336
|
"",
|
|
7330
|
-
|
|
7337
|
+
`**preset** \u2014 mentions a domain keyword (SaaS, agency, content business, API)`,
|
|
7338
|
+
`\u2192 Confirm which preset fits. Available: ${presets}.`,
|
|
7339
|
+
"\u2192 Call `start` with `preset: '<chosen-preset>'`.",
|
|
7340
|
+
"\u2192 Fallback: if no preset matches or seeding fails, switch to interview path.",
|
|
7331
7341
|
"",
|
|
7332
|
-
"
|
|
7342
|
+
"**interview** (default \u2014 1\u20135 sentence description or anything else)",
|
|
7343
|
+
`\u2192 ${instructions.systemPrompt}`,
|
|
7344
|
+
`\u2192 Q1: ${instructions.question1}`,
|
|
7345
|
+
`\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
7346
|
instructions.extractionGuidance,
|
|
7347
|
+
`\u2192 ${instructions.captureInstructions}`,
|
|
7348
|
+
"\u2192 Omit `autoCommit` \u2014 workspace governance applies automatically.",
|
|
7334
7349
|
"",
|
|
7335
|
-
|
|
7336
|
-
"Omit `autoCommit` \u2014 capture tools auto-commit in Open mode and create drafts in consensus/role mode.",
|
|
7350
|
+
"### Step 3 \u2014 Proof moment (most important \u2014 run after every path)",
|
|
7337
7351
|
"",
|
|
7338
7352
|
instructions.qualityNote,
|
|
7339
7353
|
"",
|
|
7340
|
-
|
|
7354
|
+
"### Step 4 \u2014 Deepen (offer after proof moment)",
|
|
7341
7355
|
"",
|
|
7356
|
+
instructions.scanOffer,
|
|
7342
7357
|
"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>'`."
|
|
7358
|
+
"Infer entries with the product context you now have. Present as a numbered list for confirmation.",
|
|
7359
|
+
"Capture only confirmed entries."
|
|
7366
7360
|
].join("\n");
|
|
7367
7361
|
return {
|
|
7368
7362
|
text,
|
|
7369
7363
|
structuredContent: success(
|
|
7370
|
-
|
|
7371
|
-
{
|
|
7364
|
+
"Workspace is blank. Ready for activation \u2014 interview, document, codebase scan, or preset.",
|
|
7365
|
+
{
|
|
7366
|
+
stage: "blank",
|
|
7367
|
+
interviewSchema: {
|
|
7368
|
+
fields: ["vision", "audience", "techStack", "keyTerms", "keyDecisions", "tensions"],
|
|
7369
|
+
mapping: {
|
|
7370
|
+
vision: "strategy",
|
|
7371
|
+
audience: "audiences",
|
|
7372
|
+
techStack: "architecture + top 3 terms \u2192 glossary",
|
|
7373
|
+
keyTerms: "glossary",
|
|
7374
|
+
keyDecisions: "decisions",
|
|
7375
|
+
tensions: "tensions"
|
|
7376
|
+
}
|
|
7377
|
+
},
|
|
7378
|
+
availablePresets: presetList.map((p) => ({ id: p.id, name: p.name })),
|
|
7379
|
+
...sessionCtx
|
|
7380
|
+
},
|
|
7372
7381
|
[
|
|
7373
7382
|
{ tool: "capture", description: "Capture product knowledge", parameters: {} },
|
|
7374
7383
|
{ tool: "start", description: "Seed with a preset", parameters: { preset: "software-product" } }
|
|
@@ -7524,7 +7533,7 @@ function pickNextAction(gaps, openTensions, priorSessions) {
|
|
|
7524
7533
|
}
|
|
7525
7534
|
return null;
|
|
7526
7535
|
}
|
|
7527
|
-
async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
7536
|
+
async function buildOrientResponse(wsCtx, agentSessionId, errors, task) {
|
|
7528
7537
|
const wsFullCtx = await getWorkspaceContext();
|
|
7529
7538
|
const { ageDays, isNeglected } = computeWorkspaceAge(wsFullCtx.createdAt);
|
|
7530
7539
|
let priorSessions = [];
|
|
@@ -7583,125 +7592,96 @@ async function buildOrientResponse(wsCtx, agentSessionId, errors) {
|
|
|
7583
7592
|
lines.push("_Need a collection that doesn't exist yet (e.g. audiences, personas, metrics)? Just ask \u2014 I'll set it up._");
|
|
7584
7593
|
lines.push("");
|
|
7585
7594
|
} else if (isHighReadiness) {
|
|
7586
|
-
let
|
|
7587
|
-
let wsStandards = [];
|
|
7588
|
-
let wsBusinessRules = [];
|
|
7595
|
+
let activeBets = [];
|
|
7589
7596
|
try {
|
|
7590
|
-
const
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7597
|
+
const betEntries = await mcpQuery("chain.listEntries", { collectionSlug: "bets" });
|
|
7598
|
+
activeBets = (betEntries ?? []).filter((e) => e.status === "active" && e.data?.horizon === "now").slice(0, 8);
|
|
7599
|
+
} catch {
|
|
7600
|
+
}
|
|
7601
|
+
if (task) {
|
|
7602
|
+
if (activeBets.length > 0) {
|
|
7603
|
+
lines.push("");
|
|
7604
|
+
lines.push("## Active bets \u2014 current scope");
|
|
7605
|
+
lines.push("_Work outside these bets requires explicit user confirmation._");
|
|
7606
|
+
lines.push("");
|
|
7607
|
+
for (const e of activeBets) {
|
|
7608
|
+
lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
|
|
7609
|
+
}
|
|
7610
|
+
lines.push("");
|
|
7611
|
+
}
|
|
7612
|
+
let wsPrinciples = [];
|
|
7613
|
+
let wsStandards = [];
|
|
7614
|
+
let wsBusinessRules = [];
|
|
7615
|
+
try {
|
|
7616
|
+
for (const slug of ["principles", "standards", "business-rules"]) {
|
|
7617
|
+
const entries = await mcpQuery("chain.listEntries", { collectionSlug: slug });
|
|
7618
|
+
const active = (entries ?? []).filter((e) => e.status === "active");
|
|
7602
7619
|
if (slug === "principles") wsPrinciples = active;
|
|
7603
7620
|
if (slug === "standards") wsStandards = active;
|
|
7604
7621
|
if (slug === "business-rules") wsBusinessRules = active;
|
|
7605
7622
|
}
|
|
7623
|
+
} catch {
|
|
7606
7624
|
}
|
|
7607
|
-
|
|
7625
|
+
const mapGovEntry = (e) => ({
|
|
7626
|
+
entryId: e.entryId,
|
|
7627
|
+
name: e.name,
|
|
7628
|
+
description: typeof e.data?.description === "string" ? e.data.description : typeof e.description === "string" ? e.description : void 0
|
|
7629
|
+
});
|
|
7630
|
+
lines.push(...buildOperatingProtocol({
|
|
7631
|
+
principles: wsPrinciples.map(mapGovEntry),
|
|
7632
|
+
standards: wsStandards.map(mapGovEntry),
|
|
7633
|
+
businessRules: wsBusinessRules.map(mapGovEntry)
|
|
7634
|
+
}, task));
|
|
7635
|
+
const plannedWork = await queryPlannedWork();
|
|
7636
|
+
if (hasPlannedWork(plannedWork)) {
|
|
7608
7637
|
lines.push("");
|
|
7609
|
-
lines.push(
|
|
7610
|
-
|
|
7638
|
+
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
|
|
7639
|
+
} else if (recoveryBlock) {
|
|
7611
7640
|
lines.push("");
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7616
|
-
|
|
7641
|
+
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
7642
|
+
}
|
|
7643
|
+
try {
|
|
7644
|
+
const allEntries = await mcpQuery("chain.listEntries", {});
|
|
7645
|
+
const committed = (allEntries ?? []).filter(
|
|
7646
|
+
(e) => e.status === "active" && !e.seededByPlatform
|
|
7647
|
+
);
|
|
7648
|
+
const committedCollections = new Set(committed.map((e) => e.collectionId ?? e.collection));
|
|
7649
|
+
if (committed.length >= 10 && committedCollections.size >= 3) {
|
|
7617
7650
|
lines.push("");
|
|
7651
|
+
lines.push(`_${committed.length} entries across ${committedCollections.size} collections on the Chain._`);
|
|
7618
7652
|
}
|
|
7653
|
+
} catch {
|
|
7619
7654
|
}
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
|
|
7623
|
-
|
|
7624
|
-
const betEntries = await mcpQuery("chain.listEntries", { collectionSlug: "bets" });
|
|
7625
|
-
activeBets = (betEntries ?? []).filter((e) => e.status === "active" && e.data?.horizon === "now").slice(0, 8);
|
|
7655
|
+
lines.push("");
|
|
7656
|
+
lines.push(`Working on: **${task}**`);
|
|
7657
|
+
lines.push("");
|
|
7658
|
+
} else {
|
|
7626
7659
|
if (activeBets.length > 0) {
|
|
7627
7660
|
lines.push("");
|
|
7628
7661
|
lines.push("## Active bets \u2014 current scope");
|
|
7629
|
-
lines.push("
|
|
7662
|
+
lines.push("_Work outside these bets requires explicit user confirmation._");
|
|
7630
7663
|
lines.push("");
|
|
7631
7664
|
for (const e of activeBets) {
|
|
7632
7665
|
lines.push(`- \`${e.entryId ?? e.name}\` ${e.name}`);
|
|
7633
7666
|
}
|
|
7634
7667
|
lines.push("");
|
|
7635
7668
|
}
|
|
7636
|
-
|
|
7637
|
-
}
|
|
7638
|
-
const mapGovEntry = (e) => ({
|
|
7639
|
-
entryId: e.entryId,
|
|
7640
|
-
name: e.name,
|
|
7641
|
-
description: typeof e.data?.description === "string" ? e.data.description : typeof e.description === "string" ? e.description : void 0
|
|
7642
|
-
});
|
|
7643
|
-
lines.push(...buildOperatingProtocol({
|
|
7644
|
-
principles: wsPrinciples.map(mapGovEntry),
|
|
7645
|
-
standards: wsStandards.map(mapGovEntry),
|
|
7646
|
-
businessRules: wsBusinessRules.map(mapGovEntry)
|
|
7647
|
-
}));
|
|
7648
|
-
const plannedWork = await queryPlannedWork();
|
|
7649
|
-
if (hasPlannedWork(plannedWork)) {
|
|
7650
|
-
lines.push("");
|
|
7651
|
-
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
|
|
7652
|
-
} else {
|
|
7653
|
-
const briefingItems = [];
|
|
7669
|
+
lines.push(...buildOperatingProtocol());
|
|
7654
7670
|
if (priorSessions.length > 0 && !recoveryBlock) {
|
|
7655
7671
|
const last = priorSessions[0];
|
|
7656
|
-
const date = new Date(last.startedAt).toISOString().split("T")[0];
|
|
7672
|
+
const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
|
|
7657
7673
|
const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
|
|
7658
7674
|
const modified = Array.isArray(last.entriesModified) ? last.entriesModified.length : last.entriesModified ?? 0;
|
|
7659
|
-
|
|
7660
|
-
}
|
|
7661
|
-
if (readiness?.gaps?.length > 0) {
|
|
7662
|
-
briefingItems.push(`**${readiness.gaps.length} gap${readiness.gaps.length === 1 ? "" : "s"}** remaining`);
|
|
7663
|
-
}
|
|
7664
|
-
if (briefingItems.length > 0) {
|
|
7665
|
-
lines.push("");
|
|
7666
|
-
lines.push("## Briefing");
|
|
7667
|
-
for (const item of briefingItems) {
|
|
7668
|
-
lines.push(`- ${item}`);
|
|
7669
|
-
}
|
|
7675
|
+
lines.push(`_Last session (${date}): ${created} created, ${modified} modified._`);
|
|
7670
7676
|
lines.push("");
|
|
7671
7677
|
}
|
|
7672
7678
|
if (recoveryBlock) {
|
|
7673
7679
|
lines.push("");
|
|
7674
7680
|
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
7675
7681
|
}
|
|
7682
|
+
lines.push("**What are you working on?** Tell me and I'll load relevant governance and context.");
|
|
7683
|
+
lines.push("");
|
|
7676
7684
|
}
|
|
7677
|
-
try {
|
|
7678
|
-
const allEntries = await mcpQuery("chain.listEntries", {});
|
|
7679
|
-
const committed = (allEntries ?? []).filter(
|
|
7680
|
-
(e) => e.status === "active" && !e.seededByPlatform
|
|
7681
|
-
);
|
|
7682
|
-
const committedCollections = new Set(committed.map((e) => e.collectionId ?? e.collection));
|
|
7683
|
-
if (committed.length >= 10 && committedCollections.size >= 3) {
|
|
7684
|
-
lines.push("");
|
|
7685
|
-
lines.push("## Your workspace is activated");
|
|
7686
|
-
lines.push(
|
|
7687
|
-
`**${committed.length} entries** across **${committedCollections.size} collections** \u2014 your knowledge graph is alive.`
|
|
7688
|
-
);
|
|
7689
|
-
lines.push("");
|
|
7690
|
-
lines.push(
|
|
7691
|
-
`**Try it:** Ask me anything about your product \u2014 architecture, decisions, tensions \u2014 and I'll pull context from the graph.`
|
|
7692
|
-
);
|
|
7693
|
-
lines.push("");
|
|
7694
|
-
if (wsCtx.workspaceSlug) {
|
|
7695
|
-
lines.push(`**View in Studio:** \`/w/${wsCtx.workspaceSlug}/studio\``);
|
|
7696
|
-
lines.push("");
|
|
7697
|
-
}
|
|
7698
|
-
lines.push("_Tip: Ask me to suggest connections between entries to unlock deeper context._");
|
|
7699
|
-
lines.push("");
|
|
7700
|
-
}
|
|
7701
|
-
} catch {
|
|
7702
|
-
}
|
|
7703
|
-
lines.push("What would you like to work on?");
|
|
7704
|
-
lines.push("");
|
|
7705
7685
|
}
|
|
7706
7686
|
if (errors.length > 0) {
|
|
7707
7687
|
lines.push("## Errors");
|
|
@@ -10060,23 +10040,27 @@ function registerHealthTools(server) {
|
|
|
10060
10040
|
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
10061
10041
|
} else if (priorSessions.length > 0) {
|
|
10062
10042
|
const last = priorSessions[0];
|
|
10063
|
-
const date = new Date(last.startedAt).toISOString().split("T")[0];
|
|
10043
|
+
const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
|
|
10064
10044
|
const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
|
|
10065
10045
|
const modified = Array.isArray(last.entriesModified) ? last.entriesModified.length : last.entriesModified ?? 0;
|
|
10066
10046
|
lines.push(`Last session (${date}): ${created} created, ${modified} modified`);
|
|
10067
10047
|
}
|
|
10068
10048
|
if (orientEntries) {
|
|
10069
|
-
const mapGovernanceEntry = (e) => ({
|
|
10070
|
-
entryId: e.entryId,
|
|
10071
|
-
name: e.name,
|
|
10072
|
-
description: typeof e.preview === "string" ? e.preview : void 0
|
|
10073
|
-
});
|
|
10074
10049
|
lines.push("");
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10050
|
+
if (task) {
|
|
10051
|
+
const mapGovernanceEntry = (e) => ({
|
|
10052
|
+
entryId: e.entryId,
|
|
10053
|
+
name: e.name,
|
|
10054
|
+
description: typeof e.preview === "string" ? e.preview : void 0
|
|
10055
|
+
});
|
|
10056
|
+
lines.push(...buildOperatingProtocol({
|
|
10057
|
+
principles: (orientEntries.principles ?? []).map(mapGovernanceEntry),
|
|
10058
|
+
standards: (orientEntries.standards ?? []).map(mapGovernanceEntry),
|
|
10059
|
+
businessRules: (orientEntries.businessRules ?? []).map(mapGovernanceEntry)
|
|
10060
|
+
}, task));
|
|
10061
|
+
} else {
|
|
10062
|
+
lines.push(...buildOperatingProtocol());
|
|
10063
|
+
}
|
|
10080
10064
|
}
|
|
10081
10065
|
if (agentSessionId) {
|
|
10082
10066
|
try {
|
|
@@ -10136,218 +10120,164 @@ function registerHealthTools(server) {
|
|
|
10136
10120
|
lines.push("_Use `collections action=create` to add it, or ask me to propose collections for your domain._");
|
|
10137
10121
|
lines.push("");
|
|
10138
10122
|
} else if (readiness) {
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
if (sc.productAreaCount != null && sc.productAreaCount > 0) {
|
|
10147
|
-
lines.push(`**Product areas (${sc.productAreaCount}):** ${(sc.productAreas ?? []).join(", ")}`);
|
|
10148
|
-
}
|
|
10149
|
-
const betLine = sc.currentBet ? `**Current bet:** ${sc.currentBet}. ${sc.activeBetCount} active bet(s).` : "No active bets.";
|
|
10150
|
-
lines.push(`${betLine} ${sc.activeTensionCount} open tension(s).`);
|
|
10151
|
-
lines.push("");
|
|
10152
|
-
}
|
|
10153
|
-
if (orientEntries?.continuingFrom && orientEntries.continuingFrom.length > 0) {
|
|
10154
|
-
lines.push("## Continuing from");
|
|
10155
|
-
lines.push("_Prior-session entries most relevant to your task._");
|
|
10156
|
-
lines.push("");
|
|
10157
|
-
for (const e of orientEntries.continuingFrom) {
|
|
10158
|
-
const id = e.entryId ?? e.name;
|
|
10159
|
-
const type = e.canonicalKey ?? "generic";
|
|
10160
|
-
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
10161
|
-
lines.push(`- \`${id}\` (score ${e.score}) [${type}]${coll} \u2014 ${e.name}`);
|
|
10162
|
-
if (e.reasoning) lines.push(` _${e.reasoning}_`);
|
|
10163
|
-
}
|
|
10164
|
-
lines.push("");
|
|
10165
|
-
}
|
|
10166
|
-
if (orientEntries?.lastSessionTouched && orientEntries.lastSessionTouched.length > 0) {
|
|
10167
|
-
lines.push("## Last session touched");
|
|
10168
|
-
lines.push("_Entries created or modified in your most recent session._");
|
|
10169
|
-
lines.push("");
|
|
10170
|
-
for (const e of orientEntries.lastSessionTouched) {
|
|
10171
|
-
const id = e.entryId ?? e.name;
|
|
10172
|
-
const type = e.canonicalKey ?? "generic";
|
|
10173
|
-
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
10174
|
-
lines.push(`- \`${id}\` [${type}]${coll} \u2014 ${e.name}`);
|
|
10175
|
-
}
|
|
10176
|
-
lines.push("");
|
|
10177
|
-
}
|
|
10178
|
-
if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
|
|
10179
|
-
const tc = orientEntries.taskContext;
|
|
10180
|
-
lines.push("## Task Context");
|
|
10181
|
-
lines.push(`_Task-scoped entries (${tc.confidence} confidence, ${tc.totalFound} relevant)`);
|
|
10123
|
+
const fmt = (e) => {
|
|
10124
|
+
const type = e.canonicalKey ?? "generic";
|
|
10125
|
+
const stratum = e.stratum ?? "?";
|
|
10126
|
+
return `- \`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}`;
|
|
10127
|
+
};
|
|
10128
|
+
if (task) {
|
|
10129
|
+
lines.push(`**Brain stage: ${orientStage}.** Working on: **${task}**`);
|
|
10182
10130
|
lines.push("");
|
|
10183
|
-
|
|
10184
|
-
const
|
|
10185
|
-
|
|
10186
|
-
|
|
10131
|
+
if (orientEntries?.strategicContext) {
|
|
10132
|
+
const sc = orientEntries.strategicContext;
|
|
10133
|
+
lines.push("## Strategic Context");
|
|
10134
|
+
if (sc.vision) lines.push(`**Vision:** ${sc.vision}`);
|
|
10135
|
+
if (sc.purpose) lines.push(`**Purpose:** ${sc.purpose}`);
|
|
10136
|
+
if (sc.productAreaCount != null && sc.productAreaCount > 0) {
|
|
10137
|
+
lines.push(`**Product areas (${sc.productAreaCount}):** ${(sc.productAreas ?? []).join(", ")}`);
|
|
10138
|
+
}
|
|
10139
|
+
const betLine = sc.currentBet ? `**Current bet:** ${sc.currentBet}. ${sc.activeBetCount} active bet(s).` : "No active bets.";
|
|
10140
|
+
lines.push(`${betLine} ${sc.activeTensionCount} open tension(s).`);
|
|
10141
|
+
lines.push("");
|
|
10187
10142
|
}
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
const result = runAlignmentCheck(
|
|
10192
|
-
task,
|
|
10193
|
-
orientEntries.activeBets ?? [],
|
|
10194
|
-
orientEntries.taskContext?.context
|
|
10195
|
-
);
|
|
10196
|
-
lines.push(...buildAlignmentCheckLines(result));
|
|
10197
|
-
}
|
|
10198
|
-
if (orientEntries) {
|
|
10199
|
-
const fmt = (e) => {
|
|
10200
|
-
const type = e.canonicalKey ?? "generic";
|
|
10201
|
-
const stratum = e.stratum ?? "?";
|
|
10202
|
-
return `- \`${e.entryId ?? e._id}\` [${type} \xB7 ${stratum}] ${e.name}`;
|
|
10203
|
-
};
|
|
10204
|
-
if (orientEntries.activeBets?.length > 0) {
|
|
10205
|
-
lines.push("## Active bets \u2014 current scope");
|
|
10206
|
-
lines.push("_These define what you're building now. Work outside these bets requires explicit user confirmation before designing._");
|
|
10143
|
+
if (orientEntries?.continuingFrom && orientEntries.continuingFrom.length > 0) {
|
|
10144
|
+
lines.push("## Continuing from");
|
|
10145
|
+
lines.push("_Prior-session entries most relevant to your task._");
|
|
10207
10146
|
lines.push("");
|
|
10208
|
-
for (const e of orientEntries.
|
|
10209
|
-
|
|
10210
|
-
const
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
return `\`${t.entryId ?? t.name}\` (${t.name}${meta ? `, ${meta}` : ""})`;
|
|
10215
|
-
});
|
|
10216
|
-
lines.push(` Tensions: ${tensionLines.join("; ")}`);
|
|
10217
|
-
} else {
|
|
10218
|
-
lines.push(` Tensions: No linked tensions`);
|
|
10219
|
-
}
|
|
10147
|
+
for (const e of orientEntries.continuingFrom) {
|
|
10148
|
+
const id = e.entryId ?? e.name;
|
|
10149
|
+
const type = e.canonicalKey ?? "generic";
|
|
10150
|
+
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
10151
|
+
lines.push(`- \`${id}\` (score ${e.score}) [${type}]${coll} \u2014 ${e.name}`);
|
|
10152
|
+
if (e.reasoning) lines.push(` _${e.reasoning}_`);
|
|
10220
10153
|
}
|
|
10221
10154
|
lines.push("");
|
|
10222
10155
|
}
|
|
10223
|
-
if (orientEntries.
|
|
10224
|
-
lines.push("##
|
|
10225
|
-
|
|
10156
|
+
if (orientEntries?.lastSessionTouched && orientEntries.lastSessionTouched.length > 0) {
|
|
10157
|
+
lines.push("## Last session touched");
|
|
10158
|
+
lines.push("_Entries created or modified in your most recent session._");
|
|
10226
10159
|
lines.push("");
|
|
10227
|
-
|
|
10228
|
-
|
|
10229
|
-
|
|
10230
|
-
|
|
10160
|
+
for (const e of orientEntries.lastSessionTouched) {
|
|
10161
|
+
const id = e.entryId ?? e.name;
|
|
10162
|
+
const type = e.canonicalKey ?? "generic";
|
|
10163
|
+
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
10164
|
+
lines.push(`- \`${id}\` [${type}]${coll} \u2014 ${e.name}`);
|
|
10165
|
+
}
|
|
10231
10166
|
lines.push("");
|
|
10232
10167
|
}
|
|
10233
|
-
if (orientEntries.
|
|
10234
|
-
|
|
10235
|
-
|
|
10168
|
+
if (orientEntries?.taskContext && orientEntries.taskContext.context.length > 0) {
|
|
10169
|
+
const tc = orientEntries.taskContext;
|
|
10170
|
+
lines.push("## Task Context");
|
|
10171
|
+
lines.push(`_Task-scoped entries (${tc.confidence} confidence, ${tc.totalFound} relevant)`);
|
|
10236
10172
|
lines.push("");
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10173
|
+
for (const e of tc.context) {
|
|
10174
|
+
const id = e.entryId ?? e.name;
|
|
10175
|
+
const coll = e.collectionSlug ? ` [${e.collectionSlug}]` : "";
|
|
10176
|
+
lines.push(`- \`${id}\` (score ${e.score})${coll}${e.name !== id ? ` \u2014 ${e.name}` : ""}`);
|
|
10177
|
+
}
|
|
10242
10178
|
lines.push("");
|
|
10243
10179
|
}
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
lines.push(
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10180
|
+
if (orientEntries) {
|
|
10181
|
+
const result = runAlignmentCheck(
|
|
10182
|
+
task,
|
|
10183
|
+
orientEntries.activeBets ?? [],
|
|
10184
|
+
orientEntries.taskContext?.context
|
|
10185
|
+
);
|
|
10186
|
+
lines.push(...buildAlignmentCheckLines(result));
|
|
10187
|
+
}
|
|
10188
|
+
if (orientEntries) {
|
|
10189
|
+
if (orientEntries.activeBets?.length > 0) {
|
|
10190
|
+
lines.push("## Active bets \u2014 current scope");
|
|
10191
|
+
lines.push("_Work outside these bets requires explicit user confirmation._");
|
|
10192
|
+
lines.push("");
|
|
10193
|
+
for (const e of orientEntries.activeBets) {
|
|
10194
|
+
lines.push(fmt(e));
|
|
10195
|
+
const tensions = e.linkedTensions;
|
|
10196
|
+
if (tensions?.length) {
|
|
10197
|
+
const tensionLines = tensions.map((t) => {
|
|
10198
|
+
const meta = [t.severity, t.priority].filter(Boolean).join(", ");
|
|
10199
|
+
return `\`${t.entryId ?? t.name}\` (${t.name}${meta ? `, ${meta}` : ""})`;
|
|
10200
|
+
});
|
|
10201
|
+
lines.push(` Tensions: ${tensionLines.join("; ")}`);
|
|
10202
|
+
}
|
|
10203
|
+
}
|
|
10204
|
+
lines.push("");
|
|
10205
|
+
}
|
|
10206
|
+
if (orientEntries.activeGoals?.length > 0) {
|
|
10207
|
+
lines.push("## Active goals");
|
|
10208
|
+
orientEntries.activeGoals.forEach((e) => lines.push(fmt(e)));
|
|
10209
|
+
lines.push("");
|
|
10210
|
+
}
|
|
10211
|
+
if (orientEntries.recentDecisions?.length > 0) {
|
|
10212
|
+
lines.push("## Recent decisions");
|
|
10213
|
+
orientEntries.recentDecisions.forEach((e) => lines.push(fmt(e)));
|
|
10254
10214
|
lines.push("");
|
|
10255
10215
|
}
|
|
10256
|
-
if (
|
|
10257
|
-
lines.push("
|
|
10258
|
-
orientEntries.
|
|
10216
|
+
if (orientEntries.recentlySuperseded?.length > 0) {
|
|
10217
|
+
lines.push("## Recently superseded");
|
|
10218
|
+
orientEntries.recentlySuperseded.forEach((e) => lines.push(fmt(e)));
|
|
10259
10219
|
lines.push("");
|
|
10260
10220
|
}
|
|
10261
|
-
if (
|
|
10262
|
-
lines.push("
|
|
10263
|
-
|
|
10221
|
+
if (orientEntries.staleEntries?.length > 0) {
|
|
10222
|
+
lines.push("## Needs confirmation");
|
|
10223
|
+
lines.push(`_Domain stratum entries not confirmed in ${orientEntries.stalenessThresholdDays} days._`);
|
|
10224
|
+
orientEntries.staleEntries.forEach((e) => lines.push(fmt(e)));
|
|
10264
10225
|
lines.push("");
|
|
10265
10226
|
}
|
|
10227
|
+
if (orientEntries.architectureNotes?.length > 0) {
|
|
10228
|
+
lines.push("## Architecture notes");
|
|
10229
|
+
orientEntries.architectureNotes.forEach((e) => lines.push(fmt(e)));
|
|
10230
|
+
lines.push("");
|
|
10231
|
+
}
|
|
10232
|
+
const mapGovernanceEntry = (e) => ({
|
|
10233
|
+
entryId: e.entryId,
|
|
10234
|
+
name: e.name,
|
|
10235
|
+
description: typeof e.preview === "string" ? e.preview : void 0
|
|
10236
|
+
});
|
|
10237
|
+
lines.push(...buildOperatingProtocol({
|
|
10238
|
+
principles: (orientEntries.principles ?? []).map(mapGovernanceEntry),
|
|
10239
|
+
standards: (orientEntries.standards ?? []).map(mapGovernanceEntry),
|
|
10240
|
+
businessRules: (orientEntries.businessRules ?? []).map(mapGovernanceEntry)
|
|
10241
|
+
}, task));
|
|
10266
10242
|
}
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10243
|
+
let allEntries = [];
|
|
10244
|
+
try {
|
|
10245
|
+
allEntries = await mcpQuery("chain.listEntries", {}) ?? [];
|
|
10246
|
+
} catch {
|
|
10247
|
+
}
|
|
10248
|
+
const plannedWork = buildPlannedWork(allEntries);
|
|
10249
|
+
if (hasPlannedWork(plannedWork)) {
|
|
10250
|
+
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
|
|
10251
|
+
} else if (recoveryBlock) {
|
|
10252
|
+
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
10271
10253
|
}
|
|
10272
|
-
const mapGovernanceEntry = (e) => ({
|
|
10273
|
-
entryId: e.entryId,
|
|
10274
|
-
name: e.name,
|
|
10275
|
-
description: typeof e.preview === "string" ? e.preview : void 0
|
|
10276
|
-
});
|
|
10277
|
-
lines.push(...buildOperatingProtocol({
|
|
10278
|
-
principles: (orientEntries.principles ?? []).map(mapGovernanceEntry),
|
|
10279
|
-
standards: (orientEntries.standards ?? []).map(mapGovernanceEntry),
|
|
10280
|
-
businessRules: (orientEntries.businessRules ?? []).map(mapGovernanceEntry)
|
|
10281
|
-
}, task));
|
|
10282
|
-
}
|
|
10283
|
-
let allEntries = [];
|
|
10284
|
-
try {
|
|
10285
|
-
allEntries = await mcpQuery("chain.listEntries", {}) ?? [];
|
|
10286
|
-
} catch {
|
|
10287
|
-
}
|
|
10288
|
-
const plannedWork = buildPlannedWork(allEntries);
|
|
10289
|
-
if (hasPlannedWork(plannedWork)) {
|
|
10290
|
-
lines.push(...buildPlannedWorkSection(plannedWork, priorSessions, recoveryBlock));
|
|
10291
10254
|
} else {
|
|
10292
|
-
|
|
10255
|
+
lines.push(`**Brain stage: ${orientStage}.**`);
|
|
10256
|
+
lines.push("");
|
|
10257
|
+
if (orientEntries?.activeBets?.length) {
|
|
10258
|
+
lines.push("## Active bets \u2014 current scope");
|
|
10259
|
+
lines.push("_Work outside these bets requires explicit user confirmation._");
|
|
10260
|
+
lines.push("");
|
|
10261
|
+
for (const e of orientEntries.activeBets) {
|
|
10262
|
+
lines.push(fmt(e));
|
|
10263
|
+
}
|
|
10264
|
+
lines.push("");
|
|
10265
|
+
}
|
|
10266
|
+
lines.push(...buildOperatingProtocol());
|
|
10293
10267
|
if (priorSessions.length > 0 && !recoveryBlock) {
|
|
10294
10268
|
const last = priorSessions[0];
|
|
10295
|
-
const date = new Date(last.startedAt).toISOString().split("T")[0];
|
|
10269
|
+
const date = last.startedAt ? new Date(last.startedAt).toISOString().split("T")[0] : "unknown";
|
|
10296
10270
|
const created = Array.isArray(last.entriesCreated) ? last.entriesCreated.length : last.entriesCreated ?? 0;
|
|
10297
10271
|
const modified = Array.isArray(last.entriesModified) ? last.entriesModified.length : last.entriesModified ?? 0;
|
|
10298
|
-
|
|
10299
|
-
}
|
|
10300
|
-
if (readiness.gaps?.length > 0) {
|
|
10301
|
-
briefingItems.push(`**${readiness.gaps.length} gap${readiness.gaps.length === 1 ? "" : "s"}** remaining`);
|
|
10302
|
-
}
|
|
10303
|
-
if (briefingItems.length > 0) {
|
|
10304
|
-
lines.push("## Briefing");
|
|
10305
|
-
for (const item of briefingItems) {
|
|
10306
|
-
lines.push(`- ${item}`);
|
|
10307
|
-
}
|
|
10272
|
+
lines.push(`_Last session (${date}): ${created} created, ${modified} modified._`);
|
|
10308
10273
|
lines.push("");
|
|
10309
10274
|
}
|
|
10310
10275
|
if (recoveryBlock) {
|
|
10311
|
-
lines.push("");
|
|
10312
10276
|
lines.push(...formatRecoveryBlock(recoveryBlock));
|
|
10313
10277
|
}
|
|
10314
|
-
|
|
10315
|
-
const activeEntries = allEntries.filter((e) => e.status === "active");
|
|
10316
|
-
if (activeEntries.length > 0) {
|
|
10317
|
-
const orgHealth = computeOrganisationHealth(activeEntries);
|
|
10318
|
-
if (orgHealth.disagreements > 0) {
|
|
10319
|
-
lines.push("## Organisation Health");
|
|
10320
|
-
lines.push(...formatOrgHealthLines(orgHealth, 3));
|
|
10321
|
-
lines.push("");
|
|
10322
|
-
}
|
|
10323
|
-
}
|
|
10324
|
-
const epistemicEntries = activeEntries.filter(
|
|
10325
|
-
(e) => e.collectionSlug === "insights" || e.collectionSlug === "assumptions"
|
|
10326
|
-
);
|
|
10327
|
-
if (epistemicEntries.length > 0) {
|
|
10328
|
-
let validated = 0;
|
|
10329
|
-
let evidenced = 0;
|
|
10330
|
-
let hypotheses = 0;
|
|
10331
|
-
let untested = 0;
|
|
10332
|
-
for (const e of epistemicEntries) {
|
|
10333
|
-
const ws = e.workflowStatus;
|
|
10334
|
-
if (ws === "validated") validated++;
|
|
10335
|
-
else if (ws === "evidenced") evidenced++;
|
|
10336
|
-
else if (ws === "testing") evidenced++;
|
|
10337
|
-
else if (ws === "invalidated") validated++;
|
|
10338
|
-
else if (e.collectionSlug === "assumptions") untested++;
|
|
10339
|
-
else hypotheses++;
|
|
10340
|
-
}
|
|
10341
|
-
const parts = [];
|
|
10342
|
-
if (validated > 0) parts.push(`${validated} validated`);
|
|
10343
|
-
if (evidenced > 0) parts.push(`${evidenced} evidenced`);
|
|
10344
|
-
if (hypotheses > 0) parts.push(`${hypotheses} hypotheses`);
|
|
10345
|
-
if (untested > 0) parts.push(`${untested} untested`);
|
|
10346
|
-
lines.push(`**Epistemic health:** ${parts.join(" \xB7 ")} _(${epistemicEntries.length} claim-carrying entries)_`);
|
|
10278
|
+
lines.push("**What are you working on?** Tell me and I'll load relevant governance and context.");
|
|
10347
10279
|
lines.push("");
|
|
10348
10280
|
}
|
|
10349
|
-
lines.push("What would you like to work on?");
|
|
10350
|
-
lines.push("");
|
|
10351
10281
|
}
|
|
10352
10282
|
if (errors.length > 0) {
|
|
10353
10283
|
lines.push("## Errors");
|
|
@@ -11745,4 +11675,4 @@ export {
|
|
|
11745
11675
|
SERVER_VERSION,
|
|
11746
11676
|
createProductBrainServer
|
|
11747
11677
|
};
|
|
11748
|
-
//# sourceMappingURL=chunk-
|
|
11678
|
+
//# sourceMappingURL=chunk-4IQ7A5R4.js.map
|