@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.
- package/CHANGELOG.md +43 -0
- package/README.md +22 -10
- package/dist/capabilities.js +6 -3
- package/dist/explore.js +51 -0
- package/dist/humanFormat.js +226 -0
- package/dist/outcomes.js +15 -14
- package/dist/server.js +275 -37
- package/dist/solutionPath.js +274 -0
- package/dist/tools/compliance.js +289 -35
- package/dist/tools/debug.js +83 -26
- package/dist/tools/design.js +24 -4
- package/dist/tools/harness.js +30 -2
- package/dist/tools/integration.js +161 -10
- package/dist/tools/project.js +39 -13
- package/dist/tools/sdkFacts.js +8 -1
- package/dist/tools/sensors.js +1 -1
- package/dist/uikitCustomization.js +384 -0
- package/package.json +1 -1
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));
|
|
@@ -285,12 +288,11 @@ async function handleCli(args) {
|
|
|
285
288
|
return "exit";
|
|
286
289
|
}
|
|
287
290
|
if (command === "plan" || command === "plan-integration") {
|
|
288
|
-
await
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
});
|
|
291
|
+
const input = await planCliInput(args);
|
|
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);
|
|
294
296
|
return "exit";
|
|
295
297
|
}
|
|
296
298
|
if (command === "plan-harness") {
|
|
@@ -414,21 +416,30 @@ async function handleCli(args) {
|
|
|
414
416
|
return "exit";
|
|
415
417
|
}
|
|
416
418
|
if (command === "init") {
|
|
417
|
-
assertOnlyKnownFlags(args, ["request", "surface", "surface-path", "answer", "allow-unresolved-intake"], "init");
|
|
418
|
-
const
|
|
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") });
|
|
419
422
|
if (result.status === "needs-clarification" && typeof result.exitCode === "number") {
|
|
420
423
|
process.exitCode = result.exitCode;
|
|
421
424
|
}
|
|
425
|
+
if (hasFlag(args, "baseline") && result.status === "initialized") {
|
|
426
|
+
result.baseline_recorded = await recordBaseline(initRepo);
|
|
427
|
+
}
|
|
422
428
|
console.log(JSON.stringify(result, null, 2));
|
|
423
429
|
return "exit";
|
|
424
430
|
}
|
|
425
431
|
if (command === "check") {
|
|
426
|
-
assertOnlyKnownFlags(args, ["ci"], "check");
|
|
427
|
-
const result = await checkCompliance(positionalRepoPath(args.slice(1)));
|
|
428
|
-
|
|
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);
|
|
429
435
|
process.exitCode = result.exitCode;
|
|
430
436
|
return "exit";
|
|
431
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
|
+
}
|
|
432
443
|
if (command === "sync") {
|
|
433
444
|
assertOnlyKnownFlags(args, [], "sync");
|
|
434
445
|
console.log(JSON.stringify(await syncCompliance(positionalRepoPath(args.slice(1))), null, 2));
|
|
@@ -448,13 +459,26 @@ async function handleCli(args) {
|
|
|
448
459
|
return "exit";
|
|
449
460
|
}
|
|
450
461
|
if (command === "explain") {
|
|
451
|
-
assertOnlyKnownFlags(args, [], "explain");
|
|
452
|
-
|
|
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);
|
|
453
473
|
return "exit";
|
|
454
474
|
}
|
|
455
475
|
if (command === "status") {
|
|
456
|
-
assertOnlyKnownFlags(args, [], "status");
|
|
457
|
-
|
|
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
|
+
}
|
|
458
482
|
return "exit";
|
|
459
483
|
}
|
|
460
484
|
if (command === "engagement") {
|
|
@@ -583,13 +607,17 @@ Create the evidence-backed implementation packet before editing code.
|
|
|
583
607
|
|
|
584
608
|
Usage:
|
|
585
609
|
vise plan [repoPath] --request "Add a social feed"
|
|
610
|
+
vise plan --summary "Use UIKit for a standard feed"
|
|
586
611
|
vise plan apps/web --request "Create posts" --surface apps/web
|
|
612
|
+
vise plan . --request "Use UIKit for a standard feed" --summary
|
|
587
613
|
|
|
588
614
|
Re-plan with collected answers (repeat --answer for each intake question):
|
|
589
615
|
vise plan . --request "Add a social feed" \\
|
|
590
616
|
--answer feed_scope=community \\
|
|
591
617
|
--answer feed_target=existing\\ communityId \\
|
|
592
|
-
--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.`;
|
|
593
621
|
}
|
|
594
622
|
if (command === "creative") {
|
|
595
623
|
return `${packageName} creative
|
|
@@ -838,7 +866,9 @@ Usage:
|
|
|
838
866
|
Check the current source and recorded attestations against the compliance contract. Read-only.
|
|
839
867
|
|
|
840
868
|
Usage:
|
|
841
|
-
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.`;
|
|
842
872
|
}
|
|
843
873
|
if (command === "sync") {
|
|
844
874
|
return `${packageName} sync
|
|
@@ -854,23 +884,45 @@ Usage:
|
|
|
854
884
|
Record a host-agent or local-human attestation for one compliance rule.
|
|
855
885
|
|
|
856
886
|
Usage:
|
|
857
|
-
vise attest [repoPath] --rule
|
|
887
|
+
vise attest [repoPath] --rule typescript.client.region --confidence high --signer host-agent --evidence-file evidence.json --rationale "Why this rule is satisfied."`;
|
|
858
888
|
}
|
|
859
889
|
if (command === "explain") {
|
|
860
890
|
return `${packageName} explain
|
|
861
891
|
|
|
862
|
-
Explain one compliance rule.
|
|
892
|
+
Explain one compliance rule, or list every valid rule id when run with no id.
|
|
863
893
|
|
|
864
894
|
Usage:
|
|
865
|
-
vise explain
|
|
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.
|
|
904
|
+
|
|
905
|
+
Usage:
|
|
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}`;
|
|
866
918
|
}
|
|
867
919
|
if (command === "status") {
|
|
868
920
|
return `${packageName} status
|
|
869
921
|
|
|
870
|
-
Print a compact compliance summary.
|
|
922
|
+
Print a compact compliance summary. JSON by default; --format human for a readable verdict.
|
|
871
923
|
|
|
872
924
|
Usage:
|
|
873
|
-
vise status [repoPath]`;
|
|
925
|
+
vise status [repoPath] [--format human]`;
|
|
874
926
|
}
|
|
875
927
|
if (command === "design") {
|
|
876
928
|
return `${packageName} design
|
|
@@ -919,14 +971,17 @@ contractual record of which broad-social outcomes are in scope for this project.
|
|
|
919
971
|
|
|
920
972
|
Usage:
|
|
921
973
|
vise engagement init [repoPath] --tier <free|pro|partner> --customer-id <id> --scope <outcome,...>
|
|
922
|
-
vise engagement init [repoPath] --scope feed,
|
|
974
|
+
vise engagement init [repoPath] --scope add-feed,add-community --target-completion 2026-12-31 \\
|
|
923
975
|
--reviewer-name "Jane Doe" --reviewer-email jane@example.com --evidence-upload-consent
|
|
924
976
|
vise engagement show [repoPath]
|
|
925
977
|
|
|
926
978
|
Flags (init):
|
|
927
979
|
--tier <free|pro|partner> Engagement tier (validated).
|
|
928
980
|
--customer-id <id> Customer identifier recorded in the artifact.
|
|
929
|
-
--scope <outcome,...> In-scope
|
|
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.
|
|
930
985
|
--target-completion <date> Target completion date, YYYY-MM-DD.
|
|
931
986
|
--reviewer-name <name> Human reviewer recorded on the engagement.
|
|
932
987
|
--reviewer-email <email> Human reviewer contact.
|
|
@@ -952,10 +1007,12 @@ Usage:
|
|
|
952
1007
|
vise learning record [repoPath] Record a local-only learning event
|
|
953
1008
|
vise learning show [repoPath] Show local learning summary
|
|
954
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)
|
|
955
1011
|
vise plan [repoPath] --request "..." Create an implementation plan
|
|
956
1012
|
vise workplan next [repoPath] --request "..." Get the next broad-social surface to implement
|
|
957
|
-
vise init [repoPath] --request "..." Initialize compliance sidecar
|
|
958
|
-
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
|
|
959
1016
|
vise sync [repoPath] Persist deterministic-pass evidence
|
|
960
1017
|
vise attest [repoPath] --rule ... Record a compliance attestation
|
|
961
1018
|
vise explain <ruleId> Explain one compliance rule
|
|
@@ -993,6 +1050,43 @@ async function printToolResult(tool, input) {
|
|
|
993
1050
|
console.log(text);
|
|
994
1051
|
return { result, text };
|
|
995
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
|
+
}
|
|
1063
|
+
async function planCliInput(args) {
|
|
1064
|
+
const subArgs = args.slice(1);
|
|
1065
|
+
const requestFromFlag = flagValue(args, "request");
|
|
1066
|
+
if (requestFromFlag) {
|
|
1067
|
+
return {
|
|
1068
|
+
repoPath: positionalRepoPath(subArgs),
|
|
1069
|
+
request: requestFromFlag,
|
|
1070
|
+
surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
|
|
1071
|
+
answers: keyValueFlag(args, "answer"),
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
const positional = positionalValues(subArgs);
|
|
1075
|
+
const [first, ...rest] = positional;
|
|
1076
|
+
if (!first) {
|
|
1077
|
+
throw new Error("plan requires --request or a quoted request argument.");
|
|
1078
|
+
}
|
|
1079
|
+
const firstLooksLikePath = await pathExists(first);
|
|
1080
|
+
if (firstLooksLikePath && rest.length === 0) {
|
|
1081
|
+
throw new Error("plan requires --request when the only positional argument is a repository path.");
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
repoPath: firstLooksLikePath ? first : ".",
|
|
1085
|
+
request: firstLooksLikePath ? rest.join(" ").trim() : positional.join(" ").trim(),
|
|
1086
|
+
surfacePath: flagValue(args, "surface") ?? flagValue(args, "surface-path"),
|
|
1087
|
+
answers: keyValueFlag(args, "answer"),
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
996
1090
|
function toolResultStatus(printed) {
|
|
997
1091
|
try {
|
|
998
1092
|
const payload = JSON.parse(printed.text);
|
|
@@ -1002,6 +1096,125 @@ function toolResultStatus(printed) {
|
|
|
1002
1096
|
return undefined;
|
|
1003
1097
|
}
|
|
1004
1098
|
}
|
|
1099
|
+
function planSummary(payload) {
|
|
1100
|
+
const solutionPath = objectProp(payload, "solutionPath");
|
|
1101
|
+
const uikitCustomization = objectProp(payload, "uikitCustomization");
|
|
1102
|
+
const intake = objectProp(payload, "intake");
|
|
1103
|
+
const answers = objectProp(intake, "answers") ?? {};
|
|
1104
|
+
const workplan = objectProp(payload, "socialWorkplan");
|
|
1105
|
+
const questions = arrayProp(intake, "questions").map(questionSummary);
|
|
1106
|
+
const decisionsRequired = stringArrayProp(payload, "decisionsRequired");
|
|
1107
|
+
const solutionPathAnswerId = stringProp(solutionPath, "answerId");
|
|
1108
|
+
const uikitCustomizationAnswerId = stringProp(uikitCustomization, "answerId");
|
|
1109
|
+
return stripUndefined({
|
|
1110
|
+
kind: "plan-summary",
|
|
1111
|
+
outcome: stringProp(payload, "outcome"),
|
|
1112
|
+
platform: stringProp(payload, "platform"),
|
|
1113
|
+
supportLevel: stringProp(payload, "supportLevel"),
|
|
1114
|
+
solutionPath: solutionPath
|
|
1115
|
+
? {
|
|
1116
|
+
recommendation: stringProp(solutionPath, "recommendation"),
|
|
1117
|
+
confidence: stringProp(solutionPath, "confidence"),
|
|
1118
|
+
summary: stringProp(solutionPath, "summary"),
|
|
1119
|
+
answerId: solutionPathAnswerId,
|
|
1120
|
+
decisionRequired: booleanProp(objectProp(solutionPath, "decision"), "requiredBeforeHandRolledUi") === true
|
|
1121
|
+
&& !hasAnswerValue(answers, solutionPathAnswerId),
|
|
1122
|
+
}
|
|
1123
|
+
: undefined,
|
|
1124
|
+
uikitCustomization: uikitCustomization
|
|
1125
|
+
? {
|
|
1126
|
+
status: stringProp(uikitCustomization, "status"),
|
|
1127
|
+
recommendedLevel: stringProp(uikitCustomization, "recommendedLevel"),
|
|
1128
|
+
confidence: stringProp(uikitCustomization, "confidence"),
|
|
1129
|
+
summary: stringProp(uikitCustomization, "summary"),
|
|
1130
|
+
answerId: uikitCustomizationAnswerId,
|
|
1131
|
+
decisionRequired: booleanProp(objectProp(uikitCustomization, "decision"), "requiredBeforeCustomization") === true
|
|
1132
|
+
&& !hasAnswerValue(answers, uikitCustomizationAnswerId),
|
|
1133
|
+
}
|
|
1134
|
+
: undefined,
|
|
1135
|
+
intake: intake
|
|
1136
|
+
? {
|
|
1137
|
+
status: stringProp(intake, "status"),
|
|
1138
|
+
remainingBlocking: numberProp(intake, "remainingBlocking"),
|
|
1139
|
+
blockingQuestions: questions.filter((question) => question.blocksImplementationWhenMissing),
|
|
1140
|
+
openQuestions: questions,
|
|
1141
|
+
answers,
|
|
1142
|
+
}
|
|
1143
|
+
: undefined,
|
|
1144
|
+
decisionsRequired,
|
|
1145
|
+
workplan: workplan ? workplanSummary(workplan) : undefined,
|
|
1146
|
+
nextStep: stringProp(payload, "nextStep"),
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
function hasAnswerValue(answers, answerId) {
|
|
1150
|
+
if (!answerId) {
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
const answer = answers[answerId];
|
|
1154
|
+
return typeof answer === "string" && answer.trim() !== "";
|
|
1155
|
+
}
|
|
1156
|
+
function workplanSummary(workplan) {
|
|
1157
|
+
return stripUndefined({
|
|
1158
|
+
kind: stringProp(workplan, "kind"),
|
|
1159
|
+
status: stringProp(workplan, "status"),
|
|
1160
|
+
nextStep: stringProp(workplan, "nextStep"),
|
|
1161
|
+
surfaces: arrayProp(workplan, "sequence").map((surface) => {
|
|
1162
|
+
const surfaceObject = asObject(surface);
|
|
1163
|
+
const intake = objectProp(surfaceObject, "intake");
|
|
1164
|
+
return stripUndefined({
|
|
1165
|
+
id: stringProp(surfaceObject, "id"),
|
|
1166
|
+
order: numberProp(surfaceObject, "order"),
|
|
1167
|
+
outcome: stringProp(surfaceObject, "outcome"),
|
|
1168
|
+
label: stringProp(surfaceObject, "label"),
|
|
1169
|
+
remainingBlocking: numberProp(intake, "remainingBlocking"),
|
|
1170
|
+
intakeStatus: stringProp(intake, "status"),
|
|
1171
|
+
});
|
|
1172
|
+
}),
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
function questionSummary(value) {
|
|
1176
|
+
const question = asObject(value);
|
|
1177
|
+
return {
|
|
1178
|
+
id: stringProp(question, "id"),
|
|
1179
|
+
question: stringProp(question, "question"),
|
|
1180
|
+
required: booleanProp(question, "required"),
|
|
1181
|
+
blocksImplementationWhenMissing: booleanProp(question, "blocksImplementationWhenMissing"),
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
function objectProp(value, key) {
|
|
1185
|
+
if (!value) {
|
|
1186
|
+
return undefined;
|
|
1187
|
+
}
|
|
1188
|
+
return asObject(value[key]);
|
|
1189
|
+
}
|
|
1190
|
+
function asObject(value) {
|
|
1191
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
1192
|
+
}
|
|
1193
|
+
function arrayProp(value, key) {
|
|
1194
|
+
if (!value) {
|
|
1195
|
+
return [];
|
|
1196
|
+
}
|
|
1197
|
+
const item = value[key];
|
|
1198
|
+
return Array.isArray(item) ? item : [];
|
|
1199
|
+
}
|
|
1200
|
+
function stringArrayProp(value, key) {
|
|
1201
|
+
return arrayProp(value, key).filter((item) => typeof item === "string");
|
|
1202
|
+
}
|
|
1203
|
+
function stringProp(value, key) {
|
|
1204
|
+
const item = value?.[key];
|
|
1205
|
+
return typeof item === "string" ? item : undefined;
|
|
1206
|
+
}
|
|
1207
|
+
function numberProp(value, key) {
|
|
1208
|
+
const item = value?.[key];
|
|
1209
|
+
return typeof item === "number" ? item : undefined;
|
|
1210
|
+
}
|
|
1211
|
+
function booleanProp(value, key) {
|
|
1212
|
+
const item = value?.[key];
|
|
1213
|
+
return typeof item === "boolean" ? item : undefined;
|
|
1214
|
+
}
|
|
1215
|
+
function stripUndefined(value) {
|
|
1216
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
|
|
1217
|
+
}
|
|
1005
1218
|
async function installSkill(args) {
|
|
1006
1219
|
const source = skillSourceDir();
|
|
1007
1220
|
const instructionInstall = instructionInstallDestination(args);
|
|
@@ -1281,7 +1494,7 @@ async function completeWorkplanSurface(args) {
|
|
|
1281
1494
|
throw new Error(`Surface "${surfaceId}" is not in this social workplan. Available surfaces: ${sequence.map((item) => item.id).join(", ") || "(none)"}.`);
|
|
1282
1495
|
}
|
|
1283
1496
|
const now = new Date().toISOString();
|
|
1284
|
-
const check = await greenWorkplanCheck(repoRoot, surfaceId);
|
|
1497
|
+
const check = await greenWorkplanCheck(repoRoot, surfaceId, surface.outcome);
|
|
1285
1498
|
const snapshot = await writeWorkplanSurfaceSnapshot(repoRoot, surfaceId, check, now);
|
|
1286
1499
|
const existing = await readWorkplanProgress(repoRoot);
|
|
1287
1500
|
const base = existing?.request === request
|
|
@@ -1331,7 +1544,7 @@ async function completeWorkplanSurface(args) {
|
|
|
1331
1544
|
nextStep: status.nextStep,
|
|
1332
1545
|
};
|
|
1333
1546
|
}
|
|
1334
|
-
async function greenWorkplanCheck(repoRoot, surfaceId) {
|
|
1547
|
+
async function greenWorkplanCheck(repoRoot, surfaceId, expectedOutcome) {
|
|
1335
1548
|
let check;
|
|
1336
1549
|
try {
|
|
1337
1550
|
check = await checkCompliance(repoRoot);
|
|
@@ -1340,6 +1553,9 @@ async function greenWorkplanCheck(repoRoot, surfaceId) {
|
|
|
1340
1553
|
const message = error instanceof Error ? error.message : String(error);
|
|
1341
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}`);
|
|
1342
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
|
+
}
|
|
1343
1559
|
if (check.status !== "green") {
|
|
1344
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.`);
|
|
1345
1561
|
}
|
|
@@ -1432,13 +1648,25 @@ function ciCheckResult(result) {
|
|
|
1432
1648
|
enabled: true,
|
|
1433
1649
|
passed: result.exitCode === 0,
|
|
1434
1650
|
exitCode: result.exitCode,
|
|
1435
|
-
blockingResultStatuses: [
|
|
1651
|
+
blockingResultStatuses: [
|
|
1652
|
+
"contract-drift",
|
|
1653
|
+
"blocked",
|
|
1654
|
+
"deterministic-failures",
|
|
1655
|
+
"needs-attestation",
|
|
1656
|
+
"completeness-gap",
|
|
1657
|
+
"selected-capability-failures",
|
|
1658
|
+
],
|
|
1436
1659
|
message: result.exitCode === 0 ? "Compliance green for CI." : `Compliance failed for CI with status: ${result.status}.`,
|
|
1437
1660
|
},
|
|
1438
1661
|
};
|
|
1439
1662
|
}
|
|
1440
1663
|
function positionalRepoPath(args) {
|
|
1441
|
-
const
|
|
1664
|
+
const values = positionalValues(args);
|
|
1665
|
+
return values[0] ?? ".";
|
|
1666
|
+
}
|
|
1667
|
+
function positionalValues(args) {
|
|
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"]);
|
|
1669
|
+
const values = [];
|
|
1442
1670
|
for (let index = 0; index < args.length; index += 1) {
|
|
1443
1671
|
const arg = args[index];
|
|
1444
1672
|
if (!arg) {
|
|
@@ -1454,9 +1682,18 @@ function positionalRepoPath(args) {
|
|
|
1454
1682
|
if (arg.startsWith("-")) {
|
|
1455
1683
|
continue;
|
|
1456
1684
|
}
|
|
1457
|
-
|
|
1685
|
+
values.push(arg);
|
|
1686
|
+
}
|
|
1687
|
+
return values;
|
|
1688
|
+
}
|
|
1689
|
+
async function pathExists(input) {
|
|
1690
|
+
try {
|
|
1691
|
+
await stat(path.resolve(expandHome(input)));
|
|
1692
|
+
return true;
|
|
1693
|
+
}
|
|
1694
|
+
catch {
|
|
1695
|
+
return false;
|
|
1458
1696
|
}
|
|
1459
|
-
return ".";
|
|
1460
1697
|
}
|
|
1461
1698
|
function requiredPositionalText(args, message) {
|
|
1462
1699
|
const values = [];
|
|
@@ -1609,5 +1846,6 @@ function doctorResult() {
|
|
|
1609
1846
|
docsBaseUrl: process.env.SOCIAL_PLUS_DOCS_BASE_URL ?? "https://learn.social.plus",
|
|
1610
1847
|
transport: "stdio",
|
|
1611
1848
|
tools: Array.from(tools.keys()),
|
|
1849
|
+
support: SUPPORT_URL,
|
|
1612
1850
|
};
|
|
1613
1851
|
}
|