@amityco/social-plus-vise 1.2.0 → 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.
package/dist/server.js CHANGED
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
8
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
- import { attestRule, attestRuleTool, checkCompliance, checkComplianceTool, explainRule, explainRuleTool, experienceReportTool, initCompliance, initComplianceTool, initEngagement, initEngagementTool, showEngagement, showEngagementTool, statusCompliance, syncCompliance, syncComplianceTool, } from "./tools/compliance.js";
9
+ import { attestRule, attestRuleTool, checkCompliance, checkComplianceTool, explainRule, explainRuleTool, experienceReportTool, initCompliance, initComplianceTool, recordBaseline, initEngagement, initEngagementTool, listRules, showEngagement, showEngagementTool, statusCompliance, syncCompliance, syncComplianceTool, } from "./tools/compliance.js";
10
10
  import { designCheckTool, designExtractTool, designInitTokensTool, designPreviewTool, designReferenceTool } from "./tools/design.js";
11
11
  import { getDocPageTool, searchDocsTool } from "./tools/docs.js";
12
12
  import { compileExperienceTool } from "./tools/experienceCompiler.js";
@@ -22,7 +22,10 @@ import { addBlockInstall, listRegistryBlocks, planBlockInstall, validateBlockIns
22
22
  import { debugIssueTool, debugIssue } from "./tools/debug.js";
23
23
  import { creativeAcceptTool, creativeBriefTool } from "./tools/creative.js";
24
24
  import { uxHarnessTool } from "./tools/uxHarness.js";
25
+ import { exploreRequest } from "./explore.js";
26
+ import { renderHuman, wantsHumanFormat } from "./humanFormat.js";
25
27
  import { packageName, packageVersion } from "./version.js";
28
+ const SUPPORT_URL = "mailto:support@social.plus";
26
29
  const tools = new Map([
27
30
  searchDocsTool,
28
31
  getDocPageTool,
@@ -107,14 +110,14 @@ async function handleCli(args) {
107
110
  console.log(helpText(args[1]));
108
111
  return "exit";
109
112
  }
110
- if (command === "doctor" || command === "--doctor") {
111
- console.log(JSON.stringify(doctorResult(), null, 2));
112
- return "exit";
113
- }
114
113
  if (isHelpRequest(args)) {
115
114
  console.log(helpText(command));
116
115
  return "exit";
117
116
  }
117
+ if (command === "doctor" || command === "--doctor") {
118
+ emitResult("doctor", doctorResult(), args);
119
+ return "exit";
120
+ }
118
121
  try {
119
122
  if (command === "install-skill" || command === "install_skill") {
120
123
  console.log(JSON.stringify(await installSkill(args.slice(1)), null, 2));
@@ -286,15 +289,10 @@ async function handleCli(args) {
286
289
  }
287
290
  if (command === "plan" || command === "plan-integration") {
288
291
  const input = await planCliInput(args);
289
- if (hasFlag(args, "summary")) {
290
- const result = await planIntegrationTool.call(input);
291
- const text = result.content.map((item) => item.text).join("\n");
292
- const payload = JSON.parse(text);
293
- console.log(JSON.stringify(planSummary(payload), null, 2));
294
- }
295
- else {
296
- await printToolResult(planIntegrationTool, input);
297
- }
292
+ const result = await planIntegrationTool.call(input);
293
+ const text = result.content.map((item) => item.text).join("\n");
294
+ const payload = JSON.parse(text);
295
+ emitResult("plan", hasFlag(args, "summary") ? planSummary(payload) : payload, args);
298
296
  return "exit";
299
297
  }
300
298
  if (command === "plan-harness") {
@@ -418,21 +416,30 @@ async function handleCli(args) {
418
416
  return "exit";
419
417
  }
420
418
  if (command === "init") {
421
- assertOnlyKnownFlags(args, ["request", "surface", "surface-path", "answer", "allow-unresolved-intake"], "init");
422
- const result = await initCompliance(positionalRepoPath(args.slice(1)), requiredFlagValue(args, "request", "init requires --request."), flagValue(args, "surface") ?? flagValue(args, "surface-path"), keyValueFlag(args, "answer"), { allowUnresolvedIntake: hasFlag(args, "allow-unresolved-intake") });
419
+ assertOnlyKnownFlags(args, ["request", "surface", "surface-path", "answer", "allow-unresolved-intake", "baseline"], "init");
420
+ const initRepo = positionalRepoPath(args.slice(1));
421
+ const result = await initCompliance(initRepo, requiredFlagValue(args, "request", "init requires --request."), flagValue(args, "surface") ?? flagValue(args, "surface-path"), keyValueFlag(args, "answer"), { allowUnresolvedIntake: hasFlag(args, "allow-unresolved-intake") });
423
422
  if (result.status === "needs-clarification" && typeof result.exitCode === "number") {
424
423
  process.exitCode = result.exitCode;
425
424
  }
425
+ if (hasFlag(args, "baseline") && result.status === "initialized") {
426
+ result.baseline_recorded = await recordBaseline(initRepo);
427
+ }
426
428
  console.log(JSON.stringify(result, null, 2));
427
429
  return "exit";
428
430
  }
429
431
  if (command === "check") {
430
- assertOnlyKnownFlags(args, ["ci"], "check");
431
- const result = await checkCompliance(positionalRepoPath(args.slice(1)));
432
- console.log(JSON.stringify(hasFlag(args, "ci") ? ciCheckResult(result) : result, null, 2));
432
+ assertOnlyKnownFlags(args, ["ci", "format", "new-only"], "check");
433
+ const result = await checkCompliance(positionalRepoPath(args.slice(1)), { newOnly: hasFlag(args, "new-only") });
434
+ emitResult("check", hasFlag(args, "ci") ? ciCheckResult(result) : result, args);
433
435
  process.exitCode = result.exitCode;
434
436
  return "exit";
435
437
  }
438
+ if (command === "baseline") {
439
+ assertOnlyKnownFlags(args, [], "baseline");
440
+ console.log(JSON.stringify(await recordBaseline(positionalRepoPath(args.slice(1))), null, 2));
441
+ return "exit";
442
+ }
436
443
  if (command === "sync") {
437
444
  assertOnlyKnownFlags(args, [], "sync");
438
445
  console.log(JSON.stringify(await syncCompliance(positionalRepoPath(args.slice(1))), null, 2));
@@ -452,13 +459,26 @@ async function handleCli(args) {
452
459
  return "exit";
453
460
  }
454
461
  if (command === "explain") {
455
- assertOnlyKnownFlags(args, [], "explain");
456
- console.log(JSON.stringify(await explainRule(requiredPositionalText(args.slice(1), "explain requires a rule id.")), null, 2));
462
+ assertOnlyKnownFlags(args, ["format"], "explain");
463
+ const explainRuleId = positionalValues(args.slice(1))[0];
464
+ const payload = explainRuleId ? await explainRule(explainRuleId) : await listRules();
465
+ emitResult("explain", payload, args);
466
+ return "exit";
467
+ }
468
+ if (command === "explore") {
469
+ assertOnlyKnownFlags(args, ["request", "platform", "format"], "explore");
470
+ const request = flagValue(args, "request") ??
471
+ requiredPositionalText(args.slice(1), 'explore requires a request, e.g. `vise explore "add a social feed"`.');
472
+ emitResult("explore", exploreRequest(request, flagValue(args, "platform")), args);
457
473
  return "exit";
458
474
  }
459
475
  if (command === "status") {
460
- assertOnlyKnownFlags(args, [], "status");
461
- console.log(JSON.stringify(await statusCompliance(positionalRepoPath(args.slice(1))), null, 2));
476
+ assertOnlyKnownFlags(args, ["format", "new-only"], "status");
477
+ const result = await statusCompliance(positionalRepoPath(args.slice(1)), { newOnly: hasFlag(args, "new-only") });
478
+ emitResult("status", result, args);
479
+ if (typeof result.exitCode === "number") {
480
+ process.exitCode = result.exitCode;
481
+ }
462
482
  return "exit";
463
483
  }
464
484
  if (command === "engagement") {
@@ -595,7 +615,9 @@ Re-plan with collected answers (repeat --answer for each intake question):
595
615
  vise plan . --request "Add a social feed" \\
596
616
  --answer feed_scope=community \\
597
617
  --answer feed_target=existing\\ communityId \\
598
- --answer target_screen_or_route=app/feed/page.tsx`;
618
+ --answer target_screen_or_route=app/feed/page.tsx
619
+
620
+ Output is JSON by default (for agents/CI). Add --format human for a readable plan summary.`;
599
621
  }
600
622
  if (command === "creative") {
601
623
  return `${packageName} creative
@@ -844,7 +866,9 @@ Usage:
844
866
  Check the current source and recorded attestations against the compliance contract. Read-only.
845
867
 
846
868
  Usage:
847
- vise check [repoPath] [--ci]`;
869
+ vise check [repoPath] [--ci] [--format human]
870
+
871
+ Output is JSON by default (for agents/CI). Add --format human for a readable verdict summary.`;
848
872
  }
849
873
  if (command === "sync") {
850
874
  return `${packageName} sync
@@ -860,23 +884,45 @@ Usage:
860
884
  Record a host-agent or local-human attestation for one compliance rule.
861
885
 
862
886
  Usage:
863
- vise attest [repoPath] --rule sdk.init.at-startup --confidence high --signer host-agent --evidence-file evidence.json --rationale "Why this rule is satisfied."`;
887
+ vise attest [repoPath] --rule typescript.client.region --confidence high --signer host-agent --evidence-file evidence.json --rationale "Why this rule is satisfied."`;
864
888
  }
865
889
  if (command === "explain") {
866
890
  return `${packageName} explain
867
891
 
868
- Explain one compliance rule.
892
+ Explain one compliance rule, or list every valid rule id when run with no id.
893
+
894
+ Usage:
895
+ vise explain [--format human] List all rule ids (public + contract) with where each applies
896
+ vise explain typescript.client.region [--format human]`;
897
+ }
898
+ if (command === "explore") {
899
+ return `${packageName} explore
900
+
901
+ Discover what social.plus offers for a request — before any project or credentials exist. Read-only:
902
+ maps the request to a candidate outcome (or lists the full menu when it can't), and for each shows the
903
+ capabilities involved, the canonical docs, and the command to start. No project, no API key, no writes.
869
904
 
870
905
  Usage:
871
- vise explain sdk.init.at-startup`;
906
+ vise explore "add a social feed" [--platform typescript] [--format human]`;
907
+ }
908
+ if (command === "doctor") {
909
+ return `${packageName} doctor
910
+
911
+ Print install diagnostics (Node version, transport, docs source, registered tools).
912
+ Read-only. JSON by default; --format human for a readable summary.
913
+
914
+ Usage:
915
+ vise doctor [--format human]
916
+
917
+ If diagnostics report a problem, see ${SUPPORT_URL}`;
872
918
  }
873
919
  if (command === "status") {
874
920
  return `${packageName} status
875
921
 
876
- Print a compact compliance summary.
922
+ Print a compact compliance summary. JSON by default; --format human for a readable verdict.
877
923
 
878
924
  Usage:
879
- vise status [repoPath]`;
925
+ vise status [repoPath] [--format human]`;
880
926
  }
881
927
  if (command === "design") {
882
928
  return `${packageName} design
@@ -925,14 +971,17 @@ contractual record of which broad-social outcomes are in scope for this project.
925
971
 
926
972
  Usage:
927
973
  vise engagement init [repoPath] --tier <free|pro|partner> --customer-id <id> --scope <outcome,...>
928
- vise engagement init [repoPath] --scope feed,communities --target-completion 2026-12-31 \\
974
+ vise engagement init [repoPath] --scope add-feed,add-community --target-completion 2026-12-31 \\
929
975
  --reviewer-name "Jane Doe" --reviewer-email jane@example.com --evidence-upload-consent
930
976
  vise engagement show [repoPath]
931
977
 
932
978
  Flags (init):
933
979
  --tier <free|pro|partner> Engagement tier (validated).
934
980
  --customer-id <id> Customer identifier recorded in the artifact.
935
- --scope <outcome,...> In-scope broad-social outcomes (repeatable; comma-separated).
981
+ --scope <outcome,...> In-scope outcomes, comma-separated. One or more of: setup-sdk,
982
+ setup-push, setup-live-data, add-feed, add-comments, add-chat,
983
+ add-community, add-follow, add-moderation, add-notifications,
984
+ troubleshoot, validate-setup.
936
985
  --target-completion <date> Target completion date, YYYY-MM-DD.
937
986
  --reviewer-name <name> Human reviewer recorded on the engagement.
938
987
  --reviewer-email <email> Human reviewer contact.
@@ -958,10 +1007,12 @@ Usage:
958
1007
  vise learning record [repoPath] Record a local-only learning event
959
1008
  vise learning show [repoPath] Show local learning summary
960
1009
  vise debug [repoPath] --error ... Debug an SDK-specific runtime error and emit a repair brief
1010
+ vise explore "<request>" Discover what social.plus offers for a request (no project/credentials needed)
961
1011
  vise plan [repoPath] --request "..." Create an implementation plan
962
1012
  vise workplan next [repoPath] --request "..." Get the next broad-social surface to implement
963
- vise init [repoPath] --request "..." Initialize compliance sidecar
964
- vise check [repoPath] Check compliance contract
1013
+ vise init [repoPath] --request "..." Initialize compliance sidecar (add --baseline on a brownfield app)
1014
+ vise check [repoPath] Check compliance contract (add --new-only to gate on findings since the baseline)
1015
+ vise baseline [repoPath] Snapshot pre-existing findings so check --new-only gates only new ones
965
1016
  vise sync [repoPath] Persist deterministic-pass evidence
966
1017
  vise attest [repoPath] --rule ... Record a compliance attestation
967
1018
  vise explain <ruleId> Explain one compliance rule
@@ -999,6 +1050,16 @@ async function printToolResult(tool, input) {
999
1050
  console.log(text);
1000
1051
  return { result, text };
1001
1052
  }
1053
+ function emitResult(command, payload, args) {
1054
+ if (wantsHumanFormat(flagValue(args, "format"))) {
1055
+ const human = renderHuman(command, payload);
1056
+ if (human !== null) {
1057
+ console.log(human);
1058
+ return;
1059
+ }
1060
+ }
1061
+ console.log(JSON.stringify(payload, null, 2));
1062
+ }
1002
1063
  async function planCliInput(args) {
1003
1064
  const subArgs = args.slice(1);
1004
1065
  const requestFromFlag = flagValue(args, "request");
@@ -1433,7 +1494,7 @@ async function completeWorkplanSurface(args) {
1433
1494
  throw new Error(`Surface "${surfaceId}" is not in this social workplan. Available surfaces: ${sequence.map((item) => item.id).join(", ") || "(none)"}.`);
1434
1495
  }
1435
1496
  const now = new Date().toISOString();
1436
- const check = await greenWorkplanCheck(repoRoot, surfaceId);
1497
+ const check = await greenWorkplanCheck(repoRoot, surfaceId, surface.outcome);
1437
1498
  const snapshot = await writeWorkplanSurfaceSnapshot(repoRoot, surfaceId, check, now);
1438
1499
  const existing = await readWorkplanProgress(repoRoot);
1439
1500
  const base = existing?.request === request
@@ -1483,7 +1544,7 @@ async function completeWorkplanSurface(args) {
1483
1544
  nextStep: status.nextStep,
1484
1545
  };
1485
1546
  }
1486
- async function greenWorkplanCheck(repoRoot, surfaceId) {
1547
+ async function greenWorkplanCheck(repoRoot, surfaceId, expectedOutcome) {
1487
1548
  let check;
1488
1549
  try {
1489
1550
  check = await checkCompliance(repoRoot);
@@ -1492,6 +1553,9 @@ async function greenWorkplanCheck(repoRoot, surfaceId) {
1492
1553
  const message = error instanceof Error ? error.message : String(error);
1493
1554
  throw new Error(`Cannot mark "${surfaceId}" complete because the current compliance check could not run. Run the focused \`vise init\` command for this surface first, then \`vise check .\`. ${message}`);
1494
1555
  }
1556
+ if (expectedOutcome && check.outcome !== expectedOutcome) {
1557
+ throw new Error(`Cannot mark "${surfaceId}" complete: the sp-vise sidecar is currently scoped to outcome "${check.outcome}", not this surface's outcome "${expectedOutcome}". Each workplan surface has its own sidecar scope — run this surface's focused \`vise init\` command (it sets \`--answer feature_surface=${surfaceId}\`) and \`vise check .\` before recording it. (The check reported "${check.status}" only because it ran against the other surface's contract.)`);
1558
+ }
1495
1559
  if (check.status !== "green") {
1496
1560
  throw new Error(`Cannot mark "${surfaceId}" complete because \`vise check\` returned "${check.status}" (exit ${check.exitCode}). Resolve the check result before recording workplan progress.`);
1497
1561
  }
@@ -1584,7 +1648,14 @@ function ciCheckResult(result) {
1584
1648
  enabled: true,
1585
1649
  passed: result.exitCode === 0,
1586
1650
  exitCode: result.exitCode,
1587
- blockingResultStatuses: ["contract-drift", "blocked", "deterministic-failures", "needs-attestation"],
1651
+ blockingResultStatuses: [
1652
+ "contract-drift",
1653
+ "blocked",
1654
+ "deterministic-failures",
1655
+ "needs-attestation",
1656
+ "completeness-gap",
1657
+ "selected-capability-failures",
1658
+ ],
1588
1659
  message: result.exitCode === 0 ? "Compliance green for CI." : `Compliance failed for CI with status: ${result.status}.`,
1589
1660
  },
1590
1661
  };
@@ -1594,7 +1665,7 @@ function positionalRepoPath(args) {
1594
1665
  return values[0] ?? ".";
1595
1666
  }
1596
1667
  function positionalValues(args) {
1597
- const flagsWithValues = new Set(["request", "requirements", "prototype", "variant", "variant-id", "brief", "brief-path", "surface", "surface-path", "platform", "capability", "surface-dir", "format", "include", "timeout-ms", "query", "path", "limit", "answer", "target", "dest", "destination", "rule", "confidence", "signer", "identity", "evidence-file", "rationale", "repo", "reference", "registry", "block", "package-source", "note", "kind", "sentiment", "metric"]);
1668
+ const flagsWithValues = new Set(["request", "requirements", "prototype", "variant", "variant-id", "brief", "brief-path", "surface", "surface-path", "platform", "capability", "surface-dir", "format", "include", "timeout-ms", "query", "path", "limit", "answer", "target", "dest", "destination", "rule", "confidence", "signer", "identity", "evidence-file", "rationale", "repo", "reference", "registry", "block", "package-source", "note", "kind", "sentiment", "metric", "tier", "customer-id", "scope", "target-completion", "reviewer-name", "reviewer-email"]);
1598
1669
  const values = [];
1599
1670
  for (let index = 0; index < args.length; index += 1) {
1600
1671
  const arg = args[index];
@@ -1775,5 +1846,6 @@ function doctorResult() {
1775
1846
  docsBaseUrl: process.env.SOCIAL_PLUS_DOCS_BASE_URL ?? "https://learn.social.plus",
1776
1847
  transport: "stdio",
1777
1848
  tools: Array.from(tools.keys()),
1849
+ support: SUPPORT_URL,
1778
1850
  };
1779
1851
  }
@@ -56,7 +56,7 @@ const SDK_SIGNALS = [
56
56
  id: "custom-ui",
57
57
  label: "Custom UI or non-standard experience",
58
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|\bnon[-\s]?standard (?:ui|layout|flow|experience)\b|\bpixel[-\s]?perfect\b/i,
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
60
  },
61
61
  {
62
62
  id: "custom-flows",