@productbrain/cli 0.1.0-beta.35 → 0.1.0-beta.36

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.
@@ -12,11 +12,12 @@
12
12
  * WP-303 Slice 1: migrated from readline to @clack/prompts via lib/prompts.ts.
13
13
  */
14
14
  import { getConfig } from '../lib/config.js';
15
- import { mcpCall, mcpCallWithSession } from '../lib/client.js';
15
+ import { mcpCall } from '../lib/client.js';
16
16
  import { readSession, writeSession } from '../lib/session.js';
17
17
  import { trackEvent } from '../lib/telemetry.js';
18
18
  import { runLogin } from './login.js';
19
- import { confirm as promptConfirm, ask as promptAsk } from '../lib/prompts.js';
19
+ import { runOnboardingConversation } from '../lib/onboarding.js';
20
+ import { confirm as promptConfirm } from '../lib/prompts.js';
20
21
  /** Mask an API key for display: show prefix + last 4 chars. */
21
22
  function maskKey(key) {
22
23
  if (key.length <= 10)
@@ -41,10 +42,14 @@ async function runSetupFlow() {
41
42
  console.log(`API endpoint: ${config.siteUrl}`);
42
43
  console.log('');
43
44
  // Verify workspace connectivity
45
+ let wsName = 'your workspace';
46
+ let wsId;
44
47
  try {
45
48
  const workspace = await mcpCall('resolveWorkspace', {});
46
49
  if (workspace?.name) {
47
50
  console.log(`Connected to workspace: ${workspace.name}`);
51
+ wsName = workspace.name;
52
+ wsId = workspace._id;
48
53
  }
49
54
  else {
50
55
  console.log('Workspace connection verified.');
@@ -54,15 +59,12 @@ async function runSetupFlow() {
54
59
  catch {
55
60
  console.log('Warning: Could not verify workspace connection. Your key may be invalid.');
56
61
  console.log('Run `pb login` to re-enter your API key.');
57
- }
58
- console.log('');
59
- const skipToCapture = await promptConfirm({ message: 'Skip to first capture?', initialValue: true });
60
- if (!skipToCapture) {
61
- console.log('');
62
- console.log('Setup complete. Try: pb orient -b');
63
- trackEvent('setup_completed');
64
62
  return;
65
63
  }
64
+ console.log('');
65
+ // Already configured — go straight to onboarding conversation (WP-304)
66
+ await ensureSessionAndOnboard(wsName, wsId);
67
+ return;
66
68
  }
67
69
  catch {
68
70
  // No valid config — continue to login
@@ -86,107 +88,54 @@ async function runSetupFlow() {
86
88
  await runSetupPostLogin();
87
89
  return;
88
90
  }
89
- // If we already had config, go straight to first capture
90
- await runFirstCapture();
91
91
  }
92
92
  async function runSetupPostLogin() {
93
- // Step 3: Workspace binding — the API key already binds to a workspace
94
93
  console.log('');
94
+ let wsName = 'your workspace';
95
+ let wsId;
95
96
  try {
96
97
  const workspace = await mcpCall('resolveWorkspace', {});
97
98
  if (workspace?.name) {
98
99
  console.log(`Connected to workspace: ${workspace.name}`);
99
- }
100
- else {
101
- console.log('Workspace connection verified.');
100
+ wsName = workspace.name;
101
+ wsId = workspace._id;
102
102
  }
103
103
  trackEvent('workspace_bound');
104
104
  }
105
- catch (err) {
106
- console.log(`Warning: Could not verify workspace: ${err instanceof Error ? err.message : String(err)}`);
107
- console.log('You can still try commands. Run `pb orient -b` to test.');
108
- }
109
- // Step 4: First capture
110
- await runFirstCapture();
111
- }
112
- async function runFirstCapture() {
113
- console.log('');
114
- const wantCapture = await promptConfirm({ message: 'Ready to capture your first piece of knowledge?', initialValue: true });
115
- trackEvent('first_capture_prompted');
116
- if (!wantCapture) {
117
- console.log('');
118
- console.log('Setup complete! Try: pb orient -b');
119
- trackEvent('setup_completed');
105
+ catch {
106
+ console.log('Could not reach workspace. Try: pb orient -b');
120
107
  return;
121
108
  }
122
- // Start a session if none exists
109
+ await ensureSessionAndOnboard(wsName, wsId);
110
+ }
111
+ /** Ensure a session exists, then run the onboarding conversation. */
112
+ async function ensureSessionAndOnboard(workspaceName, workspaceId) {
123
113
  let session = readSession();
124
114
  if (!session) {
125
115
  try {
126
- const workspace = await mcpCall('resolveWorkspace', {});
127
- if (!workspace?.keyId) {
128
- console.log('Your API key is read-only. You need a readwrite key for captures.');
129
- console.log('Generate one: Product Brain app > Settings > API Keys.');
130
- trackEvent('setup_completed');
116
+ const resolvedWs = workspaceId
117
+ ? { _id: workspaceId, keyId: null }
118
+ : await mcpCall('resolveWorkspace', {});
119
+ if (!resolvedWs)
131
120
  return;
132
- }
133
121
  const result = await mcpCall('agent.startSession', {
134
- workspaceId: workspace._id,
135
- apiKeyId: workspace.keyId,
122
+ workspaceId: resolvedWs._id,
123
+ apiKeyId: resolvedWs.keyId,
136
124
  clientKind: 'cli',
137
125
  });
138
126
  session = {
139
127
  sessionId: result.sessionId,
140
- workspaceId: workspace._id,
141
- workspaceName: result.workspaceName,
128
+ workspaceId: resolvedWs._id,
129
+ workspaceName: workspaceName,
142
130
  startedAt: new Date().toISOString(),
143
131
  entriesCaptured: [],
144
132
  };
145
133
  writeSession(session);
146
134
  }
147
- catch (err) {
148
- console.log(`Could not start session: ${err instanceof Error ? err.message : String(err)}`);
149
- console.log('You can start a session manually: pb session start');
150
- trackEvent('setup_completed');
151
- return;
135
+ catch {
136
+ // Session start failed onboarding can still work for reads
152
137
  }
153
138
  }
154
- // Prompt for capture text
155
- console.log('');
156
- console.log('Type a short insight, decision, or tension:');
157
- const captureText = await promptAsk({ message: '>', placeholder: 'e.g. DEC: We chose Svelte because...' });
158
- if (!captureText) {
159
- console.log('No text entered. You can capture later: pb capture "your insight here"');
160
- console.log('');
161
- console.log('Setup complete! Try: pb orient -b');
162
- trackEvent('setup_completed');
163
- return;
164
- }
165
- // Capture via MCP — use mcpCallWithSession which reads the session file
166
- try {
167
- const result = await mcpCallWithSession('chain.createEntry', {
168
- collectionSlug: 'insights',
169
- name: captureText,
170
- status: 'draft',
171
- data: { description: captureText },
172
- sessionId: session.sessionId,
173
- createdBy: `agent:${session.sessionId}`,
174
- });
175
- console.log('');
176
- console.log(`Captured: ${result.entryId} (draft)`);
177
- console.log('Your knowledge is on the Chain. Review it in Product Brain.');
178
- }
179
- catch (err) {
180
- console.log(`Capture failed: ${err instanceof Error ? err.message : String(err)}`);
181
- console.log('You can try again: pb capture "your insight here"');
182
- }
183
- // Step 5: Summary
184
- console.log('');
185
- console.log('Setup complete! Next steps:');
186
- console.log(' pb orient -b See your workspace at a glance');
187
- console.log(' pb capture "..." Capture more knowledge');
188
- console.log(' pb session close End your session when done');
189
- console.log('');
190
- trackEvent('setup_completed');
139
+ await runOnboardingConversation(workspaceName);
191
140
  }
192
141
  //# sourceMappingURL=setup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Conversation engine — reusable phase evaluation and advancement.
3
+ * WP-304: Deterministic Shell, Stochastic Core.
4
+ *
5
+ * The engine evaluates exit criteria and decides when to advance.
6
+ * It does NOT generate content — that's the LLM's job.
7
+ */
8
+ import type { ConversationPhase, ConversationState } from './conversation-phases.js';
9
+ /** Result of evaluating the current phase. */
10
+ export interface PhaseAdvanceResult {
11
+ /** Whether the current phase is complete. */
12
+ complete: boolean;
13
+ /** Whether advancement was forced by maxExchanges. */
14
+ forced: boolean;
15
+ /** Criteria that were met. */
16
+ metCriteria: string[];
17
+ /** Criteria that were NOT met. */
18
+ unmetCriteria: string[];
19
+ /** The next phase, or null if all phases are done. */
20
+ nextPhase: ConversationPhase | null;
21
+ /** True when all phases are complete — trigger extraction. */
22
+ readyToExtract: boolean;
23
+ }
24
+ /**
25
+ * Evaluate whether the current phase is complete.
26
+ * Returns advancement decision based on exit criteria and exchange count.
27
+ */
28
+ export declare function evaluatePhase(phases: ConversationPhase[], state: ConversationState): PhaseAdvanceResult;
29
+ /**
30
+ * Build the phase directive to inject into the system prompt.
31
+ * For bridge phase, injects accumulated quotable fragments so the LLM has
32
+ * concrete user phrases to work with instead of re-extracting from history.
33
+ */
34
+ export declare function buildPhaseDirective(phase: ConversationPhase, phaseIndex: number, totalPhases: number, quotableFragments?: string[]): string;
35
+ /**
36
+ * Strip banned patterns the LLM produces despite prompt instructions.
37
+ * Safety net — Gemini Flash at temp 0.3 still produces these ~15% of the time.
38
+ */
39
+ export declare function sanitizeResponse(text: string): string;
40
+ /**
41
+ * If we're in bridge phase and the response ends with a question, strip it.
42
+ * Bridge is a statement — the LLM should not ask another question.
43
+ */
44
+ export declare function enforceBridgeIsStatement(text: string, phaseId: string): string;
45
+ /**
46
+ * Detect if a response is too similar to recent ones (refusal loop).
47
+ * Uses Jaccard similarity on word sets.
48
+ */
49
+ export declare function isTooSimilar(newMsg: string, recentMsgs: string[], threshold?: number): boolean;
50
+ //# sourceMappingURL=conversation-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-engine.d.ts","sourceRoot":"","sources":["../../src/lib/conversation-engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAErF,8CAA8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,6CAA6C;IAC7C,QAAQ,EAAE,OAAO,CAAC;IAElB,sDAAsD;IACtD,MAAM,EAAE,OAAO,CAAC;IAEhB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB,kCAAkC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IAExB,sDAAsD;IACtD,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAEpC,8DAA8D;IAC9D,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,iBAAiB,EAAE,EAC3B,KAAK,EAAE,iBAAiB,GACvB,kBAAkB,CAwDpB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAC3B,MAAM,CAgBR;AAcD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAO9E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,SAAS,SAAM,GAAG,OAAO,CAW3F"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Conversation engine — reusable phase evaluation and advancement.
3
+ * WP-304: Deterministic Shell, Stochastic Core.
4
+ *
5
+ * The engine evaluates exit criteria and decides when to advance.
6
+ * It does NOT generate content — that's the LLM's job.
7
+ */
8
+ /**
9
+ * Evaluate whether the current phase is complete.
10
+ * Returns advancement decision based on exit criteria and exchange count.
11
+ */
12
+ export function evaluatePhase(phases, state) {
13
+ const currentIndex = phases.findIndex((p) => p.id === state.currentPhaseId);
14
+ const current = phases[currentIndex];
15
+ if (!current) {
16
+ return { complete: true, forced: false, metCriteria: [], unmetCriteria: [], nextPhase: null, readyToExtract: true };
17
+ }
18
+ const exchangesInPhase = state.phaseExchanges[current.id] ?? 0;
19
+ const met = [];
20
+ const unmet = [];
21
+ for (const criterion of current.exitCriteria) {
22
+ if (criterion.check(state)) {
23
+ met.push(criterion.name);
24
+ }
25
+ else {
26
+ unmet.push(criterion.name);
27
+ }
28
+ }
29
+ // Check if all required criteria are met
30
+ const allRequiredMet = current.exitCriteria.filter((c) => c.required).every((c) => c.check(state));
31
+ // All criteria met (or all required met with at least some met) — advance naturally
32
+ if (unmet.length === 0 || (allRequiredMet && met.length > 0)) {
33
+ const nextPhase = phases[currentIndex + 1] ?? null;
34
+ return {
35
+ complete: true,
36
+ forced: false,
37
+ metCriteria: met,
38
+ unmetCriteria: unmet,
39
+ nextPhase,
40
+ readyToExtract: nextPhase === null,
41
+ };
42
+ }
43
+ // Max exchanges hit — force advancement (but only if required criteria are met or we're past max+1)
44
+ if (exchangesInPhase >= current.maxExchanges) {
45
+ if (!allRequiredMet && exchangesInPhase === current.maxExchanges) {
46
+ // Give one extra turn for required criteria
47
+ return { complete: false, forced: false, metCriteria: met, unmetCriteria: unmet, nextPhase: null, readyToExtract: false };
48
+ }
49
+ const nextPhase = phases[currentIndex + 1] ?? null;
50
+ return {
51
+ complete: true,
52
+ forced: true,
53
+ metCriteria: met,
54
+ unmetCriteria: unmet,
55
+ nextPhase,
56
+ readyToExtract: nextPhase === null,
57
+ };
58
+ }
59
+ // Phase continues
60
+ return { complete: false, forced: false, metCriteria: met, unmetCriteria: unmet, nextPhase: null, readyToExtract: false };
61
+ }
62
+ /**
63
+ * Build the phase directive to inject into the system prompt.
64
+ * For bridge phase, injects accumulated quotable fragments so the LLM has
65
+ * concrete user phrases to work with instead of re-extracting from history.
66
+ */
67
+ export function buildPhaseDirective(phase, phaseIndex, totalPhases, quotableFragments) {
68
+ let directive = phase.directive;
69
+ // Inject accumulated quotable fragments into bridge phase
70
+ if (phase.id === 'bridge' && quotableFragments && quotableFragments.length > 0) {
71
+ const fragmentList = quotableFragments
72
+ .slice(0, 8)
73
+ .map((f) => ` - "${f}"`)
74
+ .join('\n');
75
+ const fragmentBlock = `\nThe user said these things during the conversation — use them as bullet items:\n${fragmentList}\nPrefer these exact phrases over anything you'd summarize yourself.`;
76
+ directive = directive.replace('{{QUOTABLE_FRAGMENTS}}', fragmentBlock);
77
+ }
78
+ else {
79
+ directive = directive.replace('{{QUOTABLE_FRAGMENTS}}', '');
80
+ }
81
+ return `\nCURRENT PHASE (${phaseIndex + 1}/${totalPhases}): ${phase.id.toUpperCase()}\n${directive}\nDo NOT summarize, synthesize, or list items unless the directive above explicitly tells you to.`;
82
+ }
83
+ // ── Post-processing ─────────────────────────────────────────────────────────
84
+ const BANNED_PATTERNS = [
85
+ /\bsounds like\b/gi,
86
+ /\bit sounds like\b/gi,
87
+ /\bsounds\s+(?:ambitious|exciting|fascinating|impressive|intriguing|interesting|amazing|complex|like)\b/gi,
88
+ /\bintriguing\b/gi,
89
+ // Anti-paraphrase: strip "When you say X," and "You mentioned X," prefixes
90
+ /^when you (?:say|mention|talk about)\b[^.?!]{0,60}[,]\s*/i,
91
+ /^you mentioned\b[^.?!]{0,60}[,]\s*/i,
92
+ ];
93
+ /**
94
+ * Strip banned patterns the LLM produces despite prompt instructions.
95
+ * Safety net — Gemini Flash at temp 0.3 still produces these ~15% of the time.
96
+ */
97
+ export function sanitizeResponse(text) {
98
+ let cleaned = text;
99
+ for (const pattern of BANNED_PATTERNS) {
100
+ cleaned = cleaned.replace(pattern, '');
101
+ }
102
+ // Clean up double spaces and leading punctuation from removals
103
+ return cleaned.replace(/\s{2,}/g, ' ').replace(/^\s*[.,]\s*/, '').trim();
104
+ }
105
+ /**
106
+ * If we're in bridge phase and the response ends with a question, strip it.
107
+ * Bridge is a statement — the LLM should not ask another question.
108
+ */
109
+ export function enforceBridgeIsStatement(text, phaseId) {
110
+ if (phaseId !== 'bridge')
111
+ return text;
112
+ const sentences = text.split(/(?<=[.!?])\s+/);
113
+ if (sentences.length > 1 && sentences[sentences.length - 1].trim().endsWith('?')) {
114
+ return sentences.slice(0, -1).join(' ').trim();
115
+ }
116
+ return text;
117
+ }
118
+ /**
119
+ * Detect if a response is too similar to recent ones (refusal loop).
120
+ * Uses Jaccard similarity on word sets.
121
+ */
122
+ export function isTooSimilar(newMsg, recentMsgs, threshold = 0.6) {
123
+ const newWords = new Set(newMsg.toLowerCase().split(/\s+/));
124
+ for (const prev of recentMsgs.slice(-2)) {
125
+ const prevWords = new Set(prev.toLowerCase().split(/\s+/));
126
+ const intersection = new Set([...newWords].filter((w) => prevWords.has(w)));
127
+ const union = new Set([...newWords, ...prevWords]);
128
+ if (union.size > 0 && intersection.size / union.size > threshold) {
129
+ return true;
130
+ }
131
+ }
132
+ return false;
133
+ }
134
+ //# sourceMappingURL=conversation-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-engine.js","sourceRoot":"","sources":["../../src/lib/conversation-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Conversation phase types — reusable phase-gated conversation control.
3
+ * WP-304: LLM owns content, CLI owns flow.
4
+ *
5
+ * Pattern: Deterministic Shell, Stochastic Core.
6
+ * - CLI defines phases with exit criteria (deterministic)
7
+ * - LLM generates conversation content within each phase (stochastic)
8
+ * - CLI evaluates criteria and decides when to advance
9
+ */
10
+ /** A conversation phase with a directive and exit criteria. */
11
+ export interface ConversationPhase {
12
+ /** Unique phase identifier. */
13
+ id: string;
14
+ /**
15
+ * Injected into the system prompt. Tells the LLM what this phase
16
+ * is about and what kind of questions to ask.
17
+ */
18
+ directive: string;
19
+ /**
20
+ * CLI-enforced exit criteria. Required ones must pass to advance.
21
+ * Non-required ones are advisory — help quality but don't block.
22
+ */
23
+ exitCriteria: ExitCriterion[];
24
+ /**
25
+ * Max exchanges in this phase before forced advancement.
26
+ * Prevents getting stuck with uncooperative users.
27
+ */
28
+ maxExchanges: number;
29
+ }
30
+ /** An exit criterion — pure function over conversation state. */
31
+ export interface ExitCriterion {
32
+ /** Human-readable name for debugging. */
33
+ name: string;
34
+ /** Returns true when the criterion is met. */
35
+ check: (state: ConversationState) => boolean;
36
+ /** If true, forced advancement cannot skip this. */
37
+ required: boolean;
38
+ }
39
+ /** Conversation state — the CLI's accumulated view of the conversation. */
40
+ export interface ConversationState {
41
+ /** All messages so far. */
42
+ messages: Array<{
43
+ role: 'user' | 'assistant';
44
+ content: string;
45
+ }>;
46
+ /** Number of user messages total. */
47
+ userMessageCount: number;
48
+ /** Total user words total. */
49
+ totalUserWords: number;
50
+ /** Exchange count per phase. */
51
+ phaseExchanges: Record<string, number>;
52
+ /** Which phases have been completed. */
53
+ completedPhases: Set<string>;
54
+ /** Current phase ID. */
55
+ currentPhaseId: string;
56
+ /** Heuristic signals detected from user messages. */
57
+ signals: ConversationSignals;
58
+ /** Count of consecutive refusals/non-answers. */
59
+ refusalStreak: number;
60
+ }
61
+ /** Signals detected from user messages — heuristic + LLM-reported. */
62
+ export interface ConversationSignals {
63
+ /** User described what they're building. */
64
+ mentionedProduct: boolean;
65
+ /** User described a problem, pain point, or challenge. */
66
+ mentionedChallenge: boolean;
67
+ /** User mentioned why they're trying Product Brain. */
68
+ mentionedWhyPB: boolean;
69
+ /** User gave at least one substantive answer (>= 8 words). */
70
+ hasSubstantiveAnswer: boolean;
71
+ /** Number of user messages with >= 5 words. */
72
+ substantiveMessageCount: number;
73
+ }
74
+ //# sourceMappingURL=conversation-phases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-phases.d.ts","sourceRoot":"","sources":["../../src/lib/conversation-phases.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,+DAA+D;AAC/D,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,EAAE,EAAE,MAAM,CAAC;IAEX;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,YAAY,EAAE,aAAa,EAAE,CAAC;IAE9B;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IAEb,8CAA8C;IAC9C,KAAK,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,OAAO,CAAC;IAE7C,oDAAoD;IACpD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEjE,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IAEzB,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IAEvB,gCAAgC;IAChC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEvC,wCAAwC;IACxC,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAE7B,wBAAwB;IACxB,cAAc,EAAE,MAAM,CAAC;IAEvB,qDAAqD;IACrD,OAAO,EAAE,mBAAmB,CAAC;IAE7B,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,gBAAgB,EAAE,OAAO,CAAC;IAE1B,0DAA0D;IAC1D,kBAAkB,EAAE,OAAO,CAAC;IAE5B,uDAAuD;IACvD,cAAc,EAAE,OAAO,CAAC;IAExB,8DAA8D;IAC9D,oBAAoB,EAAE,OAAO,CAAC;IAE9B,+CAA+C;IAC/C,uBAAuB,EAAE,MAAM,CAAC;CACjC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Conversation phase types — reusable phase-gated conversation control.
3
+ * WP-304: LLM owns content, CLI owns flow.
4
+ *
5
+ * Pattern: Deterministic Shell, Stochastic Core.
6
+ * - CLI defines phases with exit criteria (deterministic)
7
+ * - LLM generates conversation content within each phase (stochastic)
8
+ * - CLI evaluates criteria and decides when to advance
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=conversation-phases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-phases.js","sourceRoot":"","sources":["../../src/lib/conversation-phases.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Conversation signal detection — heuristic pattern matching on user text.
3
+ * WP-304: CLI-side signal detection as backup/validation for LLM-reported signals.
4
+ *
5
+ * These are intentionally loose. False positives advance the conversation
6
+ * (fine — user gave a relevant answer). False negatives mean one more turn
7
+ * in the phase (also fine — maxExchanges caps prevent loops).
8
+ */
9
+ import type { ConversationSignals } from './conversation-phases.js';
10
+ /**
11
+ * Detect signals from user messages using regex heuristics.
12
+ * Returns accumulated signals across all user messages.
13
+ */
14
+ export declare function detectSignals(messages: Array<{
15
+ role: string;
16
+ content: string;
17
+ }>): ConversationSignals;
18
+ /**
19
+ * Merge LLM-reported signals into heuristic signals.
20
+ * LLM signals are additive — once true, always true.
21
+ */
22
+ export declare function mergeSignals(heuristic: ConversationSignals, llmSignals: {
23
+ describedProduct?: boolean;
24
+ describedProblem?: boolean;
25
+ answeredWhyPB?: boolean;
26
+ }): ConversationSignals;
27
+ //# sourceMappingURL=conversation-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-signals.d.ts","sourceRoot":"","sources":["../../src/lib/conversation-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAwBpE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,mBAAmB,CAUrG;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,mBAAmB,EAC9B,UAAU,EAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9F,mBAAmB,CAOrB"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Conversation signal detection — heuristic pattern matching on user text.
3
+ * WP-304: CLI-side signal detection as backup/validation for LLM-reported signals.
4
+ *
5
+ * These are intentionally loose. False positives advance the conversation
6
+ * (fine — user gave a relevant answer). False negatives mean one more turn
7
+ * in the phase (also fine — maxExchanges caps prevent loops).
8
+ */
9
+ const PRODUCT_PATTERNS = [
10
+ /\b(?:building|making|creating|working on|developing)\b/i,
11
+ /\b(?:app|platform|tool|product|service|saas|api|system|software)\b/i,
12
+ ];
13
+ const CHALLENGE_PATTERNS = [
14
+ /\b(?:hard|hardest|difficult|challeng|struggling?|pain|problem|frustrat|broken|mess)\b/i,
15
+ /\b(?:don'?t know|can'?t figure|losing track|forget|lost context|outdated|rot)\b/i,
16
+ /\b(?:hate|annoying|waste|slow|manual|tedious)\b/i,
17
+ ];
18
+ const WHY_PB_PATTERNS = [
19
+ /\b(?:product brain|brain)\b/i,
20
+ /\b(?:context|memory|decisions?|knowledge|source of truth|ssot)\b/i,
21
+ /\b(?:ai|llm|claude|cursor|copilot|agent)\b.*\b(?:context|remember|forget|know)\b/i,
22
+ /\b(?:remember|forget|consistent|prompt)\b.*\b(?:ai|llm|agent|tool)\b/i,
23
+ ];
24
+ function countWords(text) {
25
+ return text.trim().split(/\s+/).filter(Boolean).length;
26
+ }
27
+ /**
28
+ * Detect signals from user messages using regex heuristics.
29
+ * Returns accumulated signals across all user messages.
30
+ */
31
+ export function detectSignals(messages) {
32
+ const userMessages = messages.filter((m) => m.role === 'user');
33
+ return {
34
+ mentionedProduct: userMessages.some((m) => PRODUCT_PATTERNS.some((p) => p.test(m.content))),
35
+ mentionedChallenge: userMessages.some((m) => CHALLENGE_PATTERNS.some((p) => p.test(m.content))),
36
+ mentionedWhyPB: userMessages.some((m) => WHY_PB_PATTERNS.some((p) => p.test(m.content))),
37
+ hasSubstantiveAnswer: userMessages.some((m) => countWords(m.content) >= 8),
38
+ substantiveMessageCount: userMessages.filter((m) => countWords(m.content) >= 5).length,
39
+ };
40
+ }
41
+ /**
42
+ * Merge LLM-reported signals into heuristic signals.
43
+ * LLM signals are additive — once true, always true.
44
+ */
45
+ export function mergeSignals(heuristic, llmSignals) {
46
+ return {
47
+ ...heuristic,
48
+ mentionedProduct: heuristic.mentionedProduct || (llmSignals.describedProduct ?? false),
49
+ mentionedChallenge: heuristic.mentionedChallenge || (llmSignals.describedProblem ?? false),
50
+ mentionedWhyPB: heuristic.mentionedWhyPB || (llmSignals.answeredWhyPB ?? false),
51
+ };
52
+ }
53
+ //# sourceMappingURL=conversation-signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-signals.js","sourceRoot":"","sources":["../../src/lib/conversation-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,gBAAgB,GAAG;IACvB,yDAAyD;IACzD,qEAAqE;CACtE,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,wFAAwF;IACxF,kFAAkF;IAClF,kDAAkD;CACnD,CAAC;AAEF,MAAM,eAAe,GAAG;IACtB,8BAA8B;IAC9B,mEAAmE;IACnE,mFAAmF;IACnF,uEAAuE;CACxE,CAAC;AAEF,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,QAAkD;IAC9E,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAE/D,OAAO;QACL,gBAAgB,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3F,kBAAkB,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/F,cAAc,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACxF,oBAAoB,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1E,uBAAuB,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;KACvF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAC1B,SAA8B,EAC9B,UAA+F;IAE/F,OAAO;QACL,GAAG,SAAS;QACZ,gBAAgB,EAAE,SAAS,CAAC,gBAAgB,IAAI,CAAC,UAAU,CAAC,gBAAgB,IAAI,KAAK,CAAC;QACtF,kBAAkB,EAAE,SAAS,CAAC,kBAAkB,IAAI,CAAC,UAAU,CAAC,gBAAgB,IAAI,KAAK,CAAC;QAC1F,cAAc,EAAE,SAAS,CAAC,cAAc,IAAI,CAAC,UAAU,CAAC,aAAa,IAAI,KAAK,CAAC;KAChF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Onboarding conversation phases — the 5-phase arc for first-time setup.
3
+ * WP-304: Each phase has a directive (what the LLM should do) and exit criteria (what the CLI checks).
4
+ */
5
+ import type { ConversationPhase } from './conversation-phases.js';
6
+ export declare const ONBOARDING_PHASES: ConversationPhase[];
7
+ /** Check if user wants to skip the conversation. */
8
+ export declare function userWantsToSkip(text: string): boolean;
9
+ //# sourceMappingURL=onboarding-phases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding-phases.d.ts","sourceRoot":"","sources":["../../src/lib/onboarding-phases.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,eAAO,MAAM,iBAAiB,EAAE,iBAAiB,EA4GhD,CAAC;AAKF,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD"}