edsger 0.56.2 → 0.57.0

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.
Files changed (77) hide show
  1. package/dist/api/chat.js +55 -2
  2. package/dist/api/cross-product.d.ts +8 -1
  3. package/dist/api/cross-product.js +44 -1
  4. package/dist/api/intelligence.js +98 -0
  5. package/dist/api/issues/get-issue.js +26 -0
  6. package/dist/api/issues/issue-utils.js +52 -0
  7. package/dist/api/issues/test-cases.js +89 -14
  8. package/dist/api/issues/update-issue.js +46 -8
  9. package/dist/api/issues/user-stories.js +89 -14
  10. package/dist/api/products/test-cases.d.ts +18 -0
  11. package/dist/api/products/test-cases.js +51 -0
  12. package/dist/api/products.js +21 -0
  13. package/dist/api/release-test-cases.js +38 -0
  14. package/dist/api/releases.js +86 -0
  15. package/dist/api/tasks.js +41 -4
  16. package/dist/api/test-reports.js +22 -4
  17. package/dist/api/user-psychology.d.ts +101 -0
  18. package/dist/api/user-psychology.js +143 -0
  19. package/dist/auth/auth-store.d.ts +33 -0
  20. package/dist/auth/auth-store.js +39 -0
  21. package/dist/commands/agent-workflow/chat-worker.js +187 -15
  22. package/dist/commands/agent-workflow/processor.d.ts +11 -0
  23. package/dist/commands/agent-workflow/processor.js +81 -2
  24. package/dist/commands/product-test-cases/index.d.ts +12 -0
  25. package/dist/commands/product-test-cases/index.js +40 -0
  26. package/dist/commands/screen-flow/index.d.ts +16 -0
  27. package/dist/commands/screen-flow/index.js +45 -0
  28. package/dist/commands/user-psychology/index.d.ts +7 -0
  29. package/dist/commands/user-psychology/index.js +51 -0
  30. package/dist/index.js +65 -0
  31. package/dist/phases/analyze-logs/index.js +27 -6
  32. package/dist/phases/bug-fixing/context-fetcher.js +26 -5
  33. package/dist/phases/find-features/index.js +53 -9
  34. package/dist/phases/find-shared/mcp.js +21 -0
  35. package/dist/phases/growth-analysis/context.d.ts +5 -3
  36. package/dist/phases/growth-analysis/context.js +52 -5
  37. package/dist/phases/output-contracts.js +129 -0
  38. package/dist/phases/pr-resolve/github-reply.d.ts +5 -2
  39. package/dist/phases/pr-resolve/github-reply.js +19 -3
  40. package/dist/phases/pr-resolve/index.js +19 -5
  41. package/dist/phases/pr-resolve/prompts.js +17 -18
  42. package/dist/phases/product-test-cases/index.d.ts +25 -0
  43. package/dist/phases/product-test-cases/index.js +174 -0
  44. package/dist/phases/product-test-cases/prompts.d.ts +24 -0
  45. package/dist/phases/product-test-cases/prompts.js +80 -0
  46. package/dist/phases/product-test-cases/types.d.ts +17 -0
  47. package/dist/phases/product-test-cases/types.js +27 -0
  48. package/dist/phases/screen-flow/index.d.ts +23 -0
  49. package/dist/phases/screen-flow/index.js +229 -0
  50. package/dist/phases/screen-flow/prompts.d.ts +19 -0
  51. package/dist/phases/screen-flow/prompts.js +39 -0
  52. package/dist/phases/screen-flow/theme.d.ts +19 -0
  53. package/dist/phases/screen-flow/theme.js +182 -0
  54. package/dist/phases/screen-flow/types.d.ts +130 -0
  55. package/dist/phases/screen-flow/types.js +66 -0
  56. package/dist/phases/user-psychology/agent.d.ts +16 -0
  57. package/dist/phases/user-psychology/agent.js +105 -0
  58. package/dist/phases/user-psychology/context.d.ts +10 -0
  59. package/dist/phases/user-psychology/context.js +65 -0
  60. package/dist/phases/user-psychology/index.d.ts +18 -0
  61. package/dist/phases/user-psychology/index.js +96 -0
  62. package/dist/phases/user-psychology/prompts.d.ts +2 -0
  63. package/dist/phases/user-psychology/prompts.js +41 -0
  64. package/dist/services/audit-logs.js +67 -9
  65. package/dist/services/branches.js +90 -14
  66. package/dist/services/phase-ratings.js +71 -9
  67. package/dist/services/product-logs.js +65 -5
  68. package/dist/services/pull-requests.js +74 -14
  69. package/dist/skills/phase/screen-flow/SKILL.md +78 -0
  70. package/dist/skills/phase/user-psychology/SKILL.md +135 -0
  71. package/dist/supabase/client.d.ts +23 -0
  72. package/dist/supabase/client.js +90 -0
  73. package/dist/system/session-manager.js +97 -24
  74. package/dist/types/index.d.ts +3 -0
  75. package/dist/utils/logger.js +24 -4
  76. package/package.json +5 -4
  77. package/vitest.config.ts +1 -0
@@ -1,3 +1,4 @@
1
+ import { getSupabase, hasSupabaseSession } from '../../supabase/client.js';
1
2
  import { logError, logInfo } from '../../utils/logger.js';
2
3
  import { callMcpEndpoint } from '../mcp-client.js';
3
4
  /**
@@ -7,6 +8,22 @@ export async function getUserStories(issueId, verbose) {
7
8
  if (verbose) {
8
9
  logInfo(`Fetching user stories for issue: ${issueId}`);
9
10
  }
11
+ if (hasSupabaseSession()) {
12
+ try {
13
+ const { data, error } = await getSupabase()
14
+ .from('user_stories')
15
+ .select('*')
16
+ .eq('issue_id', issueId)
17
+ .order('created_at', { ascending: true });
18
+ if (error) {
19
+ throw new Error(error.message);
20
+ }
21
+ return (data || []);
22
+ }
23
+ catch {
24
+ // Fall through to MCP
25
+ }
26
+ }
10
27
  const result = (await callMcpEndpoint('user_stories/list', {
11
28
  issue_id: issueId,
12
29
  }));
@@ -20,16 +37,38 @@ export async function createUserStory(issueId, userStory, verbose) {
20
37
  if (verbose) {
21
38
  logInfo(`Creating user story for issue: ${issueId}`);
22
39
  }
23
- await callMcpEndpoint('user_stories/create', {
24
- issue_id: issueId,
25
- user_stories: [
26
- {
40
+ let usedSdk = false;
41
+ if (hasSupabaseSession()) {
42
+ try {
43
+ const { error } = await getSupabase()
44
+ .from('user_stories')
45
+ .insert({
46
+ issue_id: issueId,
27
47
  title: userStory.title,
28
48
  description: userStory.description,
29
49
  status: userStory.status || 'draft',
30
- },
31
- ],
32
- });
50
+ });
51
+ if (error) {
52
+ throw new Error(error.message);
53
+ }
54
+ usedSdk = true;
55
+ }
56
+ catch {
57
+ // Fall through to MCP
58
+ }
59
+ }
60
+ if (!usedSdk) {
61
+ await callMcpEndpoint('user_stories/create', {
62
+ issue_id: issueId,
63
+ user_stories: [
64
+ {
65
+ title: userStory.title,
66
+ description: userStory.description,
67
+ status: userStory.status || 'draft',
68
+ },
69
+ ],
70
+ });
71
+ }
33
72
  if (verbose) {
34
73
  logInfo('✅ User story created successfully');
35
74
  }
@@ -49,9 +88,27 @@ export async function deleteUserStory(userStoryId, verbose) {
49
88
  if (verbose) {
50
89
  logInfo(`Deleting user story: ${userStoryId}`);
51
90
  }
52
- await callMcpEndpoint('user_stories/delete', {
53
- user_story_id: userStoryId,
54
- });
91
+ let usedSdk = false;
92
+ if (hasSupabaseSession()) {
93
+ try {
94
+ const { error } = await getSupabase()
95
+ .from('user_stories')
96
+ .delete()
97
+ .eq('id', userStoryId);
98
+ if (error) {
99
+ throw new Error(error.message);
100
+ }
101
+ usedSdk = true;
102
+ }
103
+ catch {
104
+ // Fall through to MCP
105
+ }
106
+ }
107
+ if (!usedSdk) {
108
+ await callMcpEndpoint('user_stories/delete', {
109
+ user_story_id: userStoryId,
110
+ });
111
+ }
55
112
  if (verbose) {
56
113
  logInfo('✅ User story deleted successfully');
57
114
  }
@@ -71,10 +128,28 @@ export async function updateUserStoryStatus(userStoryId, status, verbose) {
71
128
  if (verbose) {
72
129
  logInfo(`Updating user story ${userStoryId} status to: ${status}`);
73
130
  }
74
- await callMcpEndpoint('user_stories/update_status', {
75
- user_story_id: userStoryId,
76
- status,
77
- });
131
+ let usedSdk = false;
132
+ if (hasSupabaseSession()) {
133
+ try {
134
+ const { error } = await getSupabase()
135
+ .from('user_stories')
136
+ .update({ status })
137
+ .eq('id', userStoryId);
138
+ if (error) {
139
+ throw new Error(error.message);
140
+ }
141
+ usedSdk = true;
142
+ }
143
+ catch {
144
+ // Fall through to MCP
145
+ }
146
+ }
147
+ if (!usedSdk) {
148
+ await callMcpEndpoint('user_stories/update_status', {
149
+ user_story_id: userStoryId,
150
+ status,
151
+ });
152
+ }
78
153
  if (verbose) {
79
154
  logInfo('✅ User story status updated successfully');
80
155
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Product-scoped test case helpers.
3
+ *
4
+ * Issue-scoped helpers live in `api/issues/test-cases.ts`. The MCP
5
+ * `test_cases/list` and `test_cases/create` endpoints accept either
6
+ * `issue_id` or `product_id`, so the only difference here is which
7
+ * parent we send.
8
+ */
9
+ import { type TestCase } from '../../types/issues.js';
10
+ export interface CreateProductTestCaseInput {
11
+ name: string;
12
+ description: string;
13
+ is_critical?: boolean;
14
+ }
15
+ export declare function getProductTestCases(productId: string, verbose?: boolean): Promise<TestCase[]>;
16
+ export declare function createProductTestCases(productId: string, testCases: CreateProductTestCaseInput[], verbose?: boolean): Promise<{
17
+ createdIds: string[];
18
+ }>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Product-scoped test case helpers.
3
+ *
4
+ * Issue-scoped helpers live in `api/issues/test-cases.ts`. The MCP
5
+ * `test_cases/list` and `test_cases/create` endpoints accept either
6
+ * `issue_id` or `product_id`, so the only difference here is which
7
+ * parent we send.
8
+ */
9
+ import { logError, logInfo } from '../../utils/logger.js';
10
+ import { callMcpEndpoint } from '../mcp-client.js';
11
+ export async function getProductTestCases(productId, verbose) {
12
+ if (verbose) {
13
+ logInfo(`Fetching test cases for product: ${productId}`);
14
+ }
15
+ const result = (await callMcpEndpoint('test_cases/list', {
16
+ product_id: productId,
17
+ }));
18
+ return (result.test_cases || []);
19
+ }
20
+ export async function createProductTestCases(productId, testCases, verbose) {
21
+ if (testCases.length === 0) {
22
+ return { createdIds: [] };
23
+ }
24
+ try {
25
+ if (verbose) {
26
+ logInfo(`Saving ${testCases.length} product test cases as draft...`);
27
+ }
28
+ const result = (await callMcpEndpoint('test_cases/create', {
29
+ product_id: productId,
30
+ test_cases: testCases.map((tc) => ({
31
+ name: tc.name,
32
+ description: tc.description,
33
+ is_critical: tc.is_critical || false,
34
+ status: 'draft',
35
+ })),
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP result is loosely typed
37
+ }));
38
+ const createdIds = (result.created_test_cases || [])
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ .map((tc) => tc?.id)
41
+ .filter((id) => typeof id === 'string');
42
+ if (verbose) {
43
+ logInfo(`✅ Saved ${createdIds.length} product test cases as draft`);
44
+ }
45
+ return { createdIds };
46
+ }
47
+ catch (error) {
48
+ logError(`Failed to save product test cases: ${error instanceof Error ? error.message : String(error)}`);
49
+ return { createdIds: [] };
50
+ }
51
+ }
@@ -1,3 +1,4 @@
1
+ import { getSupabase, hasSupabaseSession } from '../supabase/client.js';
1
2
  import { logInfo } from '../utils/logger.js';
2
3
  import { callMcpEndpoint } from './mcp-client.js';
3
4
  /**
@@ -7,6 +8,26 @@ export async function getProduct(productId, verbose) {
7
8
  if (verbose) {
8
9
  logInfo(`Fetching product details for: ${productId}`);
9
10
  }
11
+ if (hasSupabaseSession()) {
12
+ try {
13
+ const { data, error } = await getSupabase()
14
+ .from('products')
15
+ .select('*')
16
+ .eq('id', productId)
17
+ .maybeSingle();
18
+ if (error) {
19
+ throw new Error(error.message);
20
+ }
21
+ if (data) {
22
+ return data;
23
+ }
24
+ // Fall through to MCP if no row — resources/read may still resolve
25
+ // the product via slug or a different schema view.
26
+ }
27
+ catch {
28
+ // Fall through to MCP
29
+ }
30
+ }
10
31
  const result = (await callMcpEndpoint('resources/read', {
11
32
  uri: `product://${productId}`,
12
33
  }));
@@ -1,7 +1,24 @@
1
+ import { getSupabase, hasSupabaseSession } from '../supabase/client.js';
1
2
  import { logDebug, logInfo } from '../utils/logger.js';
2
3
  import { callMcpEndpoint } from './mcp-client.js';
3
4
  export async function clearReleaseTestCases(releaseId, verbose) {
4
5
  logDebug(`Clearing draft cases for release ${releaseId}`, verbose);
6
+ if (hasSupabaseSession()) {
7
+ try {
8
+ const { error } = await getSupabase()
9
+ .from('release_test_cases')
10
+ .delete()
11
+ .eq('release_id', releaseId)
12
+ .eq('status', 'draft');
13
+ if (error) {
14
+ throw new Error(error.message);
15
+ }
16
+ return;
17
+ }
18
+ catch {
19
+ // Fall through to MCP
20
+ }
21
+ }
5
22
  await callMcpEndpoint('releases/test_cases/clear', {
6
23
  release_id: releaseId,
7
24
  });
@@ -13,6 +30,27 @@ export async function createReleaseTestCases(releaseId, cases, verbose) {
13
30
  if (verbose) {
14
31
  logInfo(`Inserting ${cases.length} release test cases`);
15
32
  }
33
+ if (hasSupabaseSession()) {
34
+ try {
35
+ const rows = cases.map((c) => ({
36
+ release_id: releaseId,
37
+ name: c.name,
38
+ description: c.description,
39
+ is_critical: c.is_critical || false,
40
+ }));
41
+ const { data, error } = await getSupabase()
42
+ .from('release_test_cases')
43
+ .insert(rows)
44
+ .select('id');
45
+ if (error) {
46
+ throw new Error(error.message);
47
+ }
48
+ return data?.length ?? rows.length;
49
+ }
50
+ catch {
51
+ // Fall through to MCP
52
+ }
53
+ }
16
54
  const result = (await callMcpEndpoint('releases/test_cases/create', {
17
55
  release_id: releaseId,
18
56
  test_cases: cases,
@@ -1,21 +1,90 @@
1
+ import { getSupabase, hasSupabaseSession } from '../supabase/client.js';
1
2
  import { logDebug } from '../utils/logger.js';
2
3
  import { callMcpEndpoint } from './mcp-client.js';
3
4
  export async function upsertRelease(params, verbose) {
4
5
  logDebug(`Upserting release ${params.product_id}@${params.tag}`, verbose);
6
+ if (hasSupabaseSession()) {
7
+ try {
8
+ const { data, error } = await getSupabase()
9
+ .from('releases')
10
+ .upsert(params, { onConflict: 'product_id,tag' })
11
+ .select()
12
+ .single();
13
+ if (error) {
14
+ throw new Error(error.message);
15
+ }
16
+ return data;
17
+ }
18
+ catch {
19
+ // Fall through to MCP
20
+ }
21
+ }
5
22
  return (await callMcpEndpoint('releases/upsert', params));
6
23
  }
7
24
  export async function updateRelease(params, verbose) {
8
25
  logDebug(`Updating release ${params.release_id}`, verbose);
26
+ if (hasSupabaseSession()) {
27
+ try {
28
+ const { release_id, ...patch } = params;
29
+ const { data, error } = await getSupabase()
30
+ .from('releases')
31
+ .update(patch)
32
+ .eq('id', release_id)
33
+ .select()
34
+ .single();
35
+ if (error) {
36
+ throw new Error(error.message);
37
+ }
38
+ return data;
39
+ }
40
+ catch {
41
+ // Fall through to MCP
42
+ }
43
+ }
9
44
  return (await callMcpEndpoint('releases/update', params));
10
45
  }
11
46
  export async function getRelease(releaseId, verbose) {
12
47
  logDebug(`Fetching release ${releaseId}`, verbose);
48
+ if (hasSupabaseSession()) {
49
+ try {
50
+ const { data, error } = await getSupabase()
51
+ .from('releases')
52
+ .select('*')
53
+ .eq('id', releaseId)
54
+ .single();
55
+ if (error) {
56
+ throw new Error(error.message);
57
+ }
58
+ return data;
59
+ }
60
+ catch {
61
+ // Fall through to MCP
62
+ }
63
+ }
13
64
  return (await callMcpEndpoint('releases/get', {
14
65
  release_id: releaseId,
15
66
  }));
16
67
  }
17
68
  export async function getLatestReleaseForProduct(productId, verbose) {
18
69
  logDebug(`Fetching latest release for product ${productId}`, verbose);
70
+ if (hasSupabaseSession()) {
71
+ try {
72
+ const { data, error } = await getSupabase()
73
+ .from('releases')
74
+ .select('*')
75
+ .eq('product_id', productId)
76
+ .order('published_at', { ascending: false, nullsFirst: false })
77
+ .limit(1)
78
+ .maybeSingle();
79
+ if (error) {
80
+ throw new Error(error.message);
81
+ }
82
+ return data ?? null;
83
+ }
84
+ catch {
85
+ // Fall through to MCP
86
+ }
87
+ }
19
88
  const result = (await callMcpEndpoint('releases/latest_for_product', {
20
89
  product_id: productId,
21
90
  }));
@@ -23,6 +92,23 @@ export async function getLatestReleaseForProduct(productId, verbose) {
23
92
  }
24
93
  export async function getReleaseByTag(productId, tag, verbose) {
25
94
  logDebug(`Looking up release ${productId}@${tag}`, verbose);
95
+ if (hasSupabaseSession()) {
96
+ try {
97
+ const { data, error } = await getSupabase()
98
+ .from('releases')
99
+ .select('*')
100
+ .eq('product_id', productId)
101
+ .eq('tag', tag)
102
+ .maybeSingle();
103
+ if (error) {
104
+ throw new Error(error.message);
105
+ }
106
+ return data ?? null;
107
+ }
108
+ catch {
109
+ // Fall through to MCP
110
+ }
111
+ }
26
112
  const result = (await callMcpEndpoint('releases/get_by_tag', {
27
113
  product_id: productId,
28
114
  tag,
package/dist/api/tasks.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { getSupabase, hasSupabaseSession } from '../supabase/client.js';
1
2
  import { logError, logInfo } from '../utils/logger.js';
2
3
  import { callMcpEndpoint } from './mcp-client.js';
3
4
  /**
@@ -8,6 +9,24 @@ export async function getProductPendingTasks(productId, verbose) {
8
9
  logInfo(`Fetching pending product tasks for: ${productId}`);
9
10
  }
10
11
  try {
12
+ if (hasSupabaseSession()) {
13
+ try {
14
+ const { data, error } = await getSupabase()
15
+ .from('tasks')
16
+ .select('*')
17
+ .eq('product_id', productId)
18
+ .eq('executor', 'ai')
19
+ .eq('status', 'pending')
20
+ .order('sequence', { ascending: true });
21
+ if (error) {
22
+ throw new Error(error.message);
23
+ }
24
+ return (data || []);
25
+ }
26
+ catch {
27
+ // Fall through to MCP
28
+ }
29
+ }
11
30
  const result = (await callMcpEndpoint('tasks/list_for_product', {
12
31
  product_id: productId,
13
32
  executor: 'ai',
@@ -31,10 +50,28 @@ export async function updateTaskStatus(taskId, status, verbose) {
31
50
  if (verbose) {
32
51
  logInfo(`Updating task ${taskId} status to: ${status}`);
33
52
  }
34
- await callMcpEndpoint('tasks/update', {
35
- task_id: taskId,
36
- status,
37
- });
53
+ let usedSdk = false;
54
+ if (hasSupabaseSession()) {
55
+ try {
56
+ const { error } = await getSupabase()
57
+ .from('tasks')
58
+ .update({ status })
59
+ .eq('id', taskId);
60
+ if (error) {
61
+ throw new Error(error.message);
62
+ }
63
+ usedSdk = true;
64
+ }
65
+ catch {
66
+ // Fall through to MCP
67
+ }
68
+ }
69
+ if (!usedSdk) {
70
+ await callMcpEndpoint('tasks/update', {
71
+ task_id: taskId,
72
+ status,
73
+ });
74
+ }
38
75
  return true;
39
76
  }
40
77
  catch (error) {
@@ -1,3 +1,4 @@
1
+ import { getSupabase, hasSupabaseSession } from '../supabase/client.js';
1
2
  import { logError, logInfo } from '../utils/logger.js';
2
3
  import { callMcpEndpoint } from './mcp-client.js';
3
4
  /**
@@ -8,10 +9,27 @@ export async function createTestReport(mcpServerUrl, mcpToken, issueId, reportDa
8
9
  if (verbose) {
9
10
  logInfo(`Creating test report for issue: ${issueId}`);
10
11
  }
11
- await callMcpEndpoint('test_reports/create', {
12
- issue_id: issueId,
13
- ...reportData,
14
- });
12
+ let usedSdk = false;
13
+ if (hasSupabaseSession()) {
14
+ try {
15
+ const { error } = await getSupabase()
16
+ .from('test_reports')
17
+ .insert({ issue_id: issueId, ...reportData });
18
+ if (error) {
19
+ throw new Error(error.message);
20
+ }
21
+ usedSdk = true;
22
+ }
23
+ catch {
24
+ // Fall through to MCP
25
+ }
26
+ }
27
+ if (!usedSdk) {
28
+ await callMcpEndpoint('test_reports/create', {
29
+ issue_id: issueId,
30
+ ...reportData,
31
+ });
32
+ }
15
33
  if (verbose) {
16
34
  logInfo('✅ Test report created successfully');
17
35
  }
@@ -0,0 +1,101 @@
1
+ export type UserPsychologyAnalysisStatus = 'pending' | 'completed' | 'failed';
2
+ export interface PsychologyPersona {
3
+ name: string;
4
+ archetype: string;
5
+ demographics: Record<string, unknown>;
6
+ goals: string[];
7
+ frustrations: string[];
8
+ values: string;
9
+ decision_drivers: string[];
10
+ anti_persona_note?: string;
11
+ evidence?: string;
12
+ }
13
+ export interface PsychologyJob {
14
+ statement: string;
15
+ type: 'functional' | 'emotional' | 'social';
16
+ current_alternatives: string[];
17
+ switching_cost: string;
18
+ persona: string;
19
+ }
20
+ export interface PsychologyPainPoint {
21
+ pain: string;
22
+ trigger: string;
23
+ severity: 'critical' | 'chronic' | 'occasional';
24
+ evidence?: string;
25
+ }
26
+ export interface PsychologyMotivations {
27
+ autonomy: string;
28
+ competence: string;
29
+ relatedness: string;
30
+ }
31
+ export interface PsychologyBehaviorTrigger {
32
+ behavior: string;
33
+ motivation_level: 'high' | 'medium' | 'low';
34
+ motivation_reason: string;
35
+ ability_barrier: string;
36
+ prompt: string;
37
+ recommendation: string;
38
+ }
39
+ export interface PsychologyMessagingAngle {
40
+ angle_name: string;
41
+ hook: string;
42
+ persona: string;
43
+ job: string;
44
+ psychological_lever: string;
45
+ why_it_works: string;
46
+ }
47
+ export interface UserPsychologyAnalysis {
48
+ id: string;
49
+ product_id: string;
50
+ status: UserPsychologyAnalysisStatus;
51
+ guidance: string | null;
52
+ analysis_content: string | null;
53
+ target_personas: PsychologyPersona[];
54
+ jobs_to_be_done: PsychologyJob[];
55
+ pain_points: PsychologyPainPoint[];
56
+ motivations: PsychologyMotivations | null;
57
+ behavior_triggers: PsychologyBehaviorTrigger[];
58
+ messaging_angles: PsychologyMessagingAngle[];
59
+ created_by: string;
60
+ created_at: string;
61
+ updated_at: string;
62
+ }
63
+ interface PsychologyUpdate {
64
+ analysis_content?: string | null;
65
+ target_personas?: unknown;
66
+ jobs_to_be_done?: unknown;
67
+ pain_points?: unknown;
68
+ motivations?: unknown;
69
+ behavior_triggers?: unknown;
70
+ messaging_angles?: unknown;
71
+ status?: UserPsychologyAnalysisStatus;
72
+ }
73
+ /**
74
+ * Update an existing psychology analysis row with the AI-produced result.
75
+ * The CLI always reserves the row via the desktop UI first (status='pending')
76
+ * and then fills it in here — so this path expects a real analysisId.
77
+ */
78
+ export declare function updateUserPsychologyAnalysis(analysisId: string, updates: PsychologyUpdate, verbose?: boolean): Promise<UserPsychologyAnalysis | null>;
79
+ /**
80
+ * Fetch the most recent completed psychology profile for a product.
81
+ * Used by the growth-analysis phase to ground content in real personas.
82
+ * Tries direct Supabase first; falls back to the MCP endpoint when the
83
+ * CLI is running without a synced session.
84
+ */
85
+ export declare function getLatestUserPsychologyAnalysis(productId: string, verbose?: boolean): Promise<UserPsychologyAnalysis | null>;
86
+ /**
87
+ * Best-effort classification of a failure reason into a short, stable code.
88
+ * The UI doesn't switch on these yet, but storing them keeps the error
89
+ * actionable (e.g. "ai_quota" → suggest top-up; "parse" → suggest re-run)
90
+ * and gives future analytics a handle.
91
+ */
92
+ export type UserPsychologyFailureCode = 'parse' | 'no_result' | 'ai_quota' | 'ai_auth' | 'network' | 'repo_clone' | 'context_fetch' | 'unknown';
93
+ export declare function classifyPsychologyFailure(reason: string): UserPsychologyFailureCode;
94
+ /**
95
+ * Mark an analysis as failed. Lets the desktop UI surface the error state
96
+ * instead of leaving a stuck "pending" row. The failure code is embedded
97
+ * as a leading tag in analysis_content so the UI can render it without a
98
+ * schema migration.
99
+ */
100
+ export declare function markUserPsychologyAnalysisFailed(analysisId: string, reason: string, verbose?: boolean): Promise<void>;
101
+ export {};