@amityco/social-plus-vise 1.1.1 → 1.3.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.
@@ -0,0 +1,274 @@
1
+ const UIKIT_SIGNALS = [
2
+ {
3
+ id: "explicit-uikit",
4
+ label: "Explicit social.plus UIKit / UI Kit mention",
5
+ strength: "strong",
6
+ pattern: /\b(?:social\.plus\s+)?(?:ui\s?kit|uikit)\b(?!\s+(?:experience|expertise|knowledge|background|familiarity))/i,
7
+ },
8
+ {
9
+ id: "prebuilt-components",
10
+ label: "Prebuilt or ready-made social UI components",
11
+ strength: "strong",
12
+ pattern: /\b(pre[-\s]?built|ready[-\s]?made)\b|\b(?:ready[-\s]?to[-\s]?use|out[-\s]?of[-\s]?the[-\s]?box|plug[-\s]?and[-\s]?play|drop[-\s]?in|off[-\s]?the[-\s]?shelf)\b(?:\s+(?:social\.plus|social|messaging|message|notifications?|community|communities|chat|feeds?|profiles?|stor(?:y|ies)|comments?|users?|groups?|channels?|posts?|prebuilt|standard|ui\s?kit|uikit))?\s+(?:ui|ui\s?kit|uikit|components?|widgets?|screens?|views?|feed|chat|profile|comments?|communit(?:y|ies)|stor(?:y|ies)|surfaces?)\b/i,
13
+ },
14
+ {
15
+ id: "speed-to-launch",
16
+ label: "Launch speed / MVP pressure",
17
+ strength: "medium",
18
+ pattern: /\b(quick launch|launch quickly|ship quickly|ship fast|mvp|build (?:a |an |the )?prototype|prototype (?:quickly|fast|first)|rapid prototyp\w*|weeks not months|days not months|save (?:development|dev|build|engineering) time|reduce development time)\b/i,
19
+ },
20
+ {
21
+ id: "standard-layout",
22
+ label: "Standard social app layout or white-label surface",
23
+ strength: "medium",
24
+ pattern: /\b(standard social|standard layout|standard feed|standard chat|standard profile|white[-\s]?label|basic styling|minimal customization|moderate customization|brand colors only)\b/i,
25
+ },
26
+ {
27
+ id: "uikit-customization-ladder",
28
+ label: "UIKit customization mode",
29
+ strength: "medium",
30
+ pattern: /\b(dynamic ui|component styling|fork and extend|forked ui\s?kit|fork the ui\s?kit|customi[sz]e the ui\s?kit)\b/i,
31
+ },
32
+ ];
33
+ const REJECT_NEGATOR = "do not|don't|dont|won't|wont|can't|cant|cannot|avoid|skip|no|without|not|never";
34
+ const REJECT_CONNECTOR = "to|use|uses|using|used|want|wants|wanting|adopt|adopts|adopting|leverage|leveraging|rely|relying|touch|build|building|go|going|be|been|with|on";
35
+ const REJECT_UIKIT_NOUN = "(?:the\\s+)?(?:social\\.plus\\s+)?(?:ui\\s?kit|uikit)\\b";
36
+ const REJECT_SKILL_LOOKAHEAD = "(?!\\s+(?:experience|expertise|knowledge|background|familiarity))";
37
+ const REJECT_UIKIT_SOURCE = `\\b(?:${REJECT_NEGATOR})(?:\\s+(?:${REJECT_CONNECTOR}))*\\s+${REJECT_UIKIT_NOUN}${REJECT_SKILL_LOOKAHEAD}` +
38
+ `|\\b(?:instead of|rather than)(?:\\s+(?:${REJECT_CONNECTOR}))*\\s+${REJECT_UIKIT_NOUN}` +
39
+ `|\\b(?:anything|everything)\\s+but\\s+${REJECT_UIKIT_NOUN}`;
40
+ const NEGATED_FROM_SCRATCH = /\b(?:not|never|avoid|don't|dont|do not|rather than|instead of)\b[\s\w,]{0,16}?\bfrom scratch\b/i;
41
+ const NEGATED_STANDARD = /\b(?:not|never|avoid|don't|dont|do not|rather than|instead of)\b[\s\w,]{0,12}?\b(?:standard social|standard layout|standard feed|standard chat|standard profile|white[-\s]?label|basic styling|minimal customization|moderate customization)\b/i;
42
+ const SDK_SIGNALS = [
43
+ {
44
+ id: "direct-sdk",
45
+ label: "Direct SDK implementation",
46
+ strength: "strong",
47
+ pattern: /\b(?:directly|direct|using|use|with|via|embed|embedding|integrate|integrating)\s+(?:the\s+)?(?:social\.plus\s+)?SDK\b|\b(?:the\s+)?(?:social\.plus\s+)?SDK\s+for\b|\bSDK[-\s]?owned\b|\bSDK[-\s]?custom\b/i,
48
+ },
49
+ {
50
+ id: "reject-uikit",
51
+ label: "Explicitly rejects UIKit or chooses SDK instead of UIKit",
52
+ strength: "strong",
53
+ pattern: new RegExp(REJECT_UIKIT_SOURCE, "i"),
54
+ },
55
+ {
56
+ id: "custom-ui",
57
+ label: "Custom UI or non-standard experience",
58
+ strength: "strong",
59
+ pattern: /\b(?:custom|bespoke|unique|fully[-\s]?custom|completely[-\s]?custom|totally[-\s]?(?:different|custom)|brand[-\s]?new|differentiated)\s+(?:(?:social|messaging|notification|community|chat|feed|profile|story|stories|comment|comments|user|group|channel|post|navigation|onboarding|discovery)\s+)?(?:ui|interface|experience)\b|\b(?:our|my|their|its|your)\s+own\s+(?:\w+\s+){0,2}?(?:component(?:\s+tree)?|components?|screens?|widgets?|ui)\b|\bnon[-\s]?standard (?:ui|layout|flow|experience)\b|\bpixel[-\s]?perfect\b/i,
60
+ },
61
+ {
62
+ id: "custom-flows",
63
+ label: "Custom user flows or interactions",
64
+ strength: "strong",
65
+ pattern: /\b(custom flows?|custom user flows?|custom interactions?|custom workflows?|unique flows?|bespoke flows?|special interaction)\b/i,
66
+ },
67
+ {
68
+ id: "full-control",
69
+ label: "Full control / design freedom",
70
+ strength: "strong",
71
+ pattern: /\b(full control|complete control|maximum flexibility|complete design freedom|full design freedom|build from scratch|hand[-\s]?rolled|from scratch)\b/i,
72
+ },
73
+ {
74
+ id: "deep-app-integration",
75
+ label: "Deep existing-app integration",
76
+ strength: "medium",
77
+ pattern: /\b(deeply integrate|existing app flow|existing navigation|custom backend|server[-\s]?to[-\s]?server|non[-\s]?standard platform|advanced technical requirements)\b/i,
78
+ },
79
+ ];
80
+ const SOURCE_EVIDENCE = [
81
+ "social.plus UIKit docs: prebuilt UI components, quick launch, and minimal/moderate customization paths.",
82
+ "social.plus SDK docs: custom experiences, unique user flows, complete design freedom, and existing-app integration.",
83
+ ];
84
+ export function recommendSolutionPath(request, answers = {}) {
85
+ const text = typeof request === "string" ? request : "";
86
+ const explicitAnswer = normalizeSolutionPathAnswer(answers?.solution_path);
87
+ const sdkSignals = collectSignals(text, SDK_SIGNALS).filter((signal) => {
88
+ if (signal.id !== "full-control" || !/scratch/i.test(signal.matched)) {
89
+ return true;
90
+ }
91
+ return !NEGATED_FROM_SCRATCH.test(text);
92
+ });
93
+ const negativeUIKitSignals = sdkSignals.filter((signal) => signal.id === "reject-uikit");
94
+ let uikitScanText = text;
95
+ if (negativeUIKitSignals.length > 0) {
96
+ uikitScanText = text.replace(new RegExp(REJECT_UIKIT_SOURCE, "gi"), " ");
97
+ }
98
+ let uikitSignals = collectSignals(uikitScanText, UIKIT_SIGNALS).filter((signal) => {
99
+ if (signal.id !== "standard-layout") {
100
+ return true;
101
+ }
102
+ return !NEGATED_STANDARD.test(text);
103
+ });
104
+ if (negativeUIKitSignals.length > 0 && !uikitSignals.some((signal) => signal.strength === "strong")) {
105
+ uikitSignals = [];
106
+ }
107
+ if (explicitAnswer) {
108
+ return guidanceFor({
109
+ recommendation: explicitAnswer,
110
+ confidence: "high",
111
+ uikitSignals,
112
+ sdkSignals,
113
+ answered: explicitAnswer,
114
+ });
115
+ }
116
+ const hasStrongUIKit = uikitSignals.some((signal) => signal.strength === "strong");
117
+ const hasStrongSdk = sdkSignals.some((signal) => signal.strength === "strong");
118
+ if (uikitSignals.length > 0 && sdkSignals.length > 0) {
119
+ return guidanceFor({
120
+ recommendation: "needs-decision",
121
+ confidence: hasStrongUIKit || hasStrongSdk ? "medium" : "low",
122
+ uikitSignals,
123
+ sdkSignals,
124
+ });
125
+ }
126
+ if (uikitSignals.length > 0) {
127
+ return guidanceFor({
128
+ recommendation: "uikit",
129
+ confidence: hasStrongUIKit ? "high" : "medium",
130
+ uikitSignals,
131
+ sdkSignals,
132
+ });
133
+ }
134
+ if (sdkSignals.length > 0) {
135
+ return guidanceFor({
136
+ recommendation: "sdk",
137
+ confidence: hasStrongSdk ? "high" : "medium",
138
+ uikitSignals,
139
+ sdkSignals,
140
+ });
141
+ }
142
+ return guidanceFor({
143
+ recommendation: "sdk",
144
+ confidence: "medium",
145
+ uikitSignals,
146
+ sdkSignals,
147
+ });
148
+ }
149
+ function guidanceFor(args) {
150
+ const decision = {
151
+ requiredBeforeHandRolledUi: !args.answered && (args.recommendation === "uikit" || args.recommendation === "hybrid" || args.recommendation === "needs-decision"),
152
+ question: "Should this build use social.plus UIKit components, a direct SDK implementation, or a hybrid path?",
153
+ options: [
154
+ "uikit: use social.plus UIKit for standard social surfaces and theme/customize it",
155
+ "sdk: build the experience directly with the social.plus SDK",
156
+ "hybrid: use UIKit for standard surfaces and SDK/custom code for differentiated app-layer behavior",
157
+ ],
158
+ };
159
+ if (args.recommendation === "uikit") {
160
+ return {
161
+ recommendation: "uikit",
162
+ confidence: args.confidence,
163
+ summary: args.answered === "uikit"
164
+ ? "The customer explicitly selected the UIKit path. Confirm scope, then use social.plus UIKit as the primary implementation artifact instead of hand-rolling standard social UI."
165
+ : "The request sounds UIKit-shaped: favor social.plus UIKit for the standard social surface, then customize within the UIKit theming/component model before considering direct SDK UI.",
166
+ answerId: "solution_path",
167
+ signals: { uikit: args.uikitSignals, sdk: args.sdkSignals },
168
+ decision,
169
+ implementationGuidance: [
170
+ "Confirm `solution_path=uikit` before the host agent hand-rolls standard feed, chat, profile, comments, community, or story UI.",
171
+ "Use UIKit installation, authentication, customization, and platform build guidance as the implementation source of truth.",
172
+ "Keep Vise's existing SDK setup, secrets, region, auth, build, and project-sensor guidance around the UIKit integration.",
173
+ "Do not claim deterministic compliance for UIKit internals hidden outside the customer repo; validate customer wiring, configuration, custom code, and local build evidence.",
174
+ "Keep design conformance advisory: use Dynamic UI, component styling, or fork-and-extend based on the customer's customization need.",
175
+ ],
176
+ evidence: SOURCE_EVIDENCE,
177
+ advisoryOnly: "This is an advisory solution-path recommendation. It does not change outcome classification, compliance rules, sidecar schema, or `vise check` exit codes.",
178
+ };
179
+ }
180
+ if (args.recommendation === "needs-decision") {
181
+ return {
182
+ recommendation: "needs-decision",
183
+ confidence: args.confidence,
184
+ summary: args.answered === "hybrid"
185
+ ? "The customer selected a hybrid path. Treat UIKit as the default for standard surfaces and use direct SDK/custom code only for the differentiated behavior."
186
+ : "The request contains both UIKit-shaped speed/prebuilt signals and SDK-shaped custom-control signals. Ask for the solution path before the agent starts UI implementation.",
187
+ answerId: "solution_path",
188
+ signals: { uikit: args.uikitSignals, sdk: args.sdkSignals },
189
+ decision,
190
+ implementationGuidance: [
191
+ "Resolve `solution_path` before hand-rolling standard social UI.",
192
+ "Choose UIKit when the standard social surface can be themed or lightly customized.",
193
+ "Choose SDK when the requested workflow, layout, or interaction model is materially different from UIKit's component model.",
194
+ "Choose hybrid when UIKit can cover baseline social surfaces and SDK/custom code is needed for app-specific extensions.",
195
+ "Do not claim deterministic compliance for UIKit internals hidden outside the customer repo; validate customer wiring, configuration, custom code, and local build evidence.",
196
+ ],
197
+ evidence: SOURCE_EVIDENCE,
198
+ advisoryOnly: "This is an advisory solution-path recommendation. It does not change outcome classification, compliance rules, sidecar schema, or `vise check` exit codes.",
199
+ };
200
+ }
201
+ if (args.recommendation === "hybrid") {
202
+ return {
203
+ recommendation: "hybrid",
204
+ confidence: args.confidence,
205
+ summary: "The customer selected a hybrid path. Treat UIKit as the default for standard surfaces and use direct SDK/custom code only for the differentiated behavior.",
206
+ answerId: "solution_path",
207
+ signals: { uikit: args.uikitSignals, sdk: args.sdkSignals },
208
+ decision,
209
+ implementationGuidance: [
210
+ "Use UIKit for standard social surfaces that fit its component and customization model.",
211
+ "Use direct SDK/custom code only for differentiated app-layer behavior, custom profile/discovery surfaces, or non-standard flows.",
212
+ "Keep the handoff explicit in the implementation summary: name which surfaces are UIKit-owned and which are SDK/customer-owned.",
213
+ "Do not claim deterministic compliance for UIKit internals hidden outside the customer repo; validate customer wiring, configuration, custom code, and local build evidence.",
214
+ ],
215
+ evidence: SOURCE_EVIDENCE,
216
+ advisoryOnly: "This is an advisory solution-path recommendation. It does not change outcome classification, compliance rules, sidecar schema, or `vise check` exit codes.",
217
+ };
218
+ }
219
+ return {
220
+ recommendation: "sdk",
221
+ confidence: args.confidence,
222
+ summary: args.answered === "sdk"
223
+ ? "The customer explicitly selected the direct SDK path. Continue with the normal Vise SDK integration harness."
224
+ : args.sdkSignals.some((signal) => signal.id === "reject-uikit")
225
+ ? "The request explicitly rejects UIKit or chooses the SDK instead of UIKit, so the normal SDK-first Vise harness is the safer fit."
226
+ : args.sdkSignals.length > 0
227
+ ? "The request asks for custom control, unique flows, or deep app integration, so the normal SDK-first Vise harness is the safer fit."
228
+ : "No strong UIKit fast-launch/prebuilt-surface signal was detected, so Vise keeps the existing SDK-first integration flow.",
229
+ answerId: "solution_path",
230
+ signals: { uikit: args.uikitSignals, sdk: args.sdkSignals },
231
+ decision,
232
+ implementationGuidance: [
233
+ "Use the normal Vise SDK integration flow: plan, answer intake, initialize the compliance contract, implement, check, and run sensors.",
234
+ "If the customer later asks for a standard prebuilt social surface or quick-launch MVP, re-plan with `--answer solution_path=uikit` or a UIKit-specific request.",
235
+ ],
236
+ evidence: SOURCE_EVIDENCE,
237
+ advisoryOnly: "This is an advisory solution-path recommendation. It does not change outcome classification, compliance rules, sidecar schema, or `vise check` exit codes.",
238
+ };
239
+ }
240
+ function collectSignals(request, signals) {
241
+ return signals.flatMap((signal) => {
242
+ const match = request.match(signal.pattern);
243
+ if (!match?.[0]) {
244
+ return [];
245
+ }
246
+ return [{
247
+ id: signal.id,
248
+ label: signal.label,
249
+ strength: signal.strength,
250
+ matched: match[0],
251
+ }];
252
+ });
253
+ }
254
+ const REJECT_UIKIT_IN_ANSWER = new RegExp(REJECT_UIKIT_SOURCE, "i");
255
+ const REJECT_HYBRID_IN_ANSWER = /\b(?:not|no|without|avoid|don't|dont|do not|never|rather than|instead of)\b[\s\w,]{0,12}?\bhybrid\b/;
256
+ function normalizeSolutionPathAnswer(answer) {
257
+ const normalized = (typeof answer === "string" ? answer : "").trim().toLowerCase();
258
+ if (!normalized) {
259
+ return undefined;
260
+ }
261
+ if (/\bhybrid\b/.test(normalized) && !REJECT_HYBRID_IN_ANSWER.test(normalized)) {
262
+ return "hybrid";
263
+ }
264
+ if (REJECT_UIKIT_IN_ANSWER.test(normalized)) {
265
+ return "sdk";
266
+ }
267
+ if (/\b(ui\s?kit|uikit)\b/.test(normalized)) {
268
+ return "uikit";
269
+ }
270
+ if (/\bsdk\b/.test(normalized)) {
271
+ return "sdk";
272
+ }
273
+ return undefined;
274
+ }