@yemi33/minions 0.1.813 → 0.1.815

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.
@@ -0,0 +1,137 @@
1
+ /**
2
+ * engine/teams-cards.js — Adaptive Card templates for Teams notifications.
3
+ * All cards use schema version 1.4 and include fallback text.
4
+ */
5
+
6
+ const SCHEMA = 'http://adaptivecards.io/schemas/adaptive-card.json';
7
+ const VERSION = '1.4';
8
+ const DASHBOARD_URL = 'http://localhost:7331';
9
+
10
+ function wrapCard(body, actions, fallbackText) {
11
+ return {
12
+ type: 'AdaptiveCard',
13
+ $schema: SCHEMA,
14
+ version: VERSION,
15
+ fallbackText: fallbackText || 'Minions notification',
16
+ body,
17
+ actions: actions || [],
18
+ };
19
+ }
20
+
21
+ /**
22
+ * Agent completion card — shows agent name, task title, result, PR link.
23
+ * @param {string} agent — agent name/id
24
+ * @param {object} item — { title, id }
25
+ * @param {string} result — 'success' or 'error'
26
+ * @param {string} [prUrl] — PR URL if available
27
+ */
28
+ function buildCompletionCard(agent, item, result, prUrl) {
29
+ const isSuccess = result === 'success';
30
+ const badge = isSuccess ? 'Done' : 'Failed';
31
+ const color = isSuccess ? 'good' : 'attention';
32
+ const title = item?.title || item?.id || 'Unknown task';
33
+
34
+ const body = [
35
+ { type: 'TextBlock', text: `${badge} — ${agent}`, weight: 'bolder', size: 'medium', color },
36
+ { type: 'TextBlock', text: title, wrap: true },
37
+ ];
38
+
39
+ const actions = [
40
+ { type: 'Action.OpenUrl', title: 'Open Dashboard', url: DASHBOARD_URL },
41
+ ];
42
+ if (prUrl) {
43
+ actions.unshift({ type: 'Action.OpenUrl', title: 'View PR', url: prUrl });
44
+ }
45
+
46
+ return wrapCard(body, actions, `${badge}: ${agent} — ${title}`);
47
+ }
48
+
49
+ /**
50
+ * PR lifecycle card — shows PR title, event, author, project.
51
+ * @param {object} pr — { id, title, url, agent }
52
+ * @param {string} event — 'pr-merged', 'pr-abandoned', 'build-failed', etc.
53
+ * @param {object} [project] — { name }
54
+ */
55
+ function buildPrCard(pr, event, project) {
56
+ const title = pr?.title || pr?.id || 'Unknown PR';
57
+ const agent = pr?.agent || 'unknown';
58
+ const projectName = project?.name || '';
59
+
60
+ const body = [
61
+ { type: 'TextBlock', text: `${event}`, weight: 'bolder', size: 'medium' },
62
+ { type: 'ColumnSet', columns: [
63
+ { type: 'Column', width: 'stretch', items: [
64
+ { type: 'TextBlock', text: title, wrap: true, weight: 'bolder' },
65
+ { type: 'TextBlock', text: `${agent}${projectName ? ' | ' + projectName : ''}`, isSubtle: true, spacing: 'none' },
66
+ ]},
67
+ ]},
68
+ ];
69
+
70
+ const actions = [
71
+ { type: 'Action.OpenUrl', title: 'Open Dashboard', url: DASHBOARD_URL },
72
+ ];
73
+ if (pr?.url) {
74
+ actions.unshift({ type: 'Action.OpenUrl', title: 'View PR', url: pr.url });
75
+ }
76
+
77
+ return wrapCard(body, actions, `${event}: ${title} (${agent})`);
78
+ }
79
+
80
+ /**
81
+ * Plan lifecycle card — shows plan name, event, item counts.
82
+ * @param {object} plan — { name, file, doneCount, totalCount }
83
+ * @param {string} event — 'plan-completed', 'plan-approved', 'plan-rejected', 'verify-created'
84
+ */
85
+ function buildPlanCard(plan, event) {
86
+ const name = plan?.name || plan?.file || 'Unknown plan';
87
+ const hasCounts = plan?.doneCount != null && plan?.totalCount != null;
88
+
89
+ const body = [
90
+ { type: 'TextBlock', text: `${event}`, weight: 'bolder', size: 'medium' },
91
+ { type: 'TextBlock', text: name, wrap: true },
92
+ ];
93
+
94
+ if (hasCounts) {
95
+ body.push({ type: 'TextBlock', text: `${plan.doneCount}/${plan.totalCount} items completed`, isSubtle: true });
96
+ }
97
+
98
+ const actions = [
99
+ { type: 'Action.OpenUrl', title: 'Open Dashboard', url: `${DASHBOARD_URL}/prd` },
100
+ ];
101
+
102
+ return wrapCard(body, actions, `${event}: ${name}${hasCounts ? ` (${plan.doneCount}/${plan.totalCount})` : ''}`);
103
+ }
104
+
105
+ /**
106
+ * CC response mirror card — shows user question and CC answer.
107
+ * @param {string} question — user's CC input
108
+ * @param {string} answer — CC response (truncated if needed)
109
+ */
110
+ function buildCCResponseCard(question, answer) {
111
+ const maxLen = 3000;
112
+ const truncated = answer.length > maxLen
113
+ ? answer.slice(0, maxLen) + '...'
114
+ : answer;
115
+
116
+ const body = [
117
+ { type: 'TextBlock', text: 'Command Center', weight: 'bolder', size: 'medium' },
118
+ { type: 'TextBlock', text: `> ${question.slice(0, 200)}`, wrap: true, isSubtle: true },
119
+ { type: 'TextBlock', text: truncated, wrap: true },
120
+ ];
121
+
122
+ const actions = [
123
+ { type: 'Action.OpenUrl', title: 'Open Dashboard', url: DASHBOARD_URL },
124
+ ];
125
+
126
+ return wrapCard(body, actions, `CC: ${question.slice(0, 100)} — ${answer.slice(0, 200)}`);
127
+ }
128
+
129
+ module.exports = {
130
+ buildCompletionCard,
131
+ buildPrCard,
132
+ buildPlanCard,
133
+ buildCCResponseCard,
134
+ SCHEMA,
135
+ VERSION,
136
+ DASHBOARD_URL,
137
+ };