@amityco/social-plus-vise 1.6.0 → 1.8.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 +44 -0
- package/README.md +10 -4
- package/dist/flow.js +6 -0
- package/dist/server.js +71 -18
- package/dist/tools/blocks.js +82 -9
- package/dist/tools/compliance.js +315 -27
- package/dist/tools/internalWorkspace.js +44 -0
- package/dist/tools/project.js +116 -29
- package/dist/tools/sdkFacts.js +233 -7
- package/dist/tools/sensors.js +102 -36
- package/package.json +2 -2
- package/packages/intelligence/catalog/catalog.schema.json +6 -0
- package/packages/intelligence/catalog/experience-objects.json +11 -11
- package/sdk-surface/manifest.json +4 -4
- package/sdk-surface/models.typescript.json +2903 -463
- package/skills/social-plus-vise/SKILL.md +20 -0
package/dist/tools/sensors.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { objectInput, optionalNumberField, optionalStringField, stringField, textResult } from "../types.js";
|
|
4
4
|
import { detectCommandSensors } from "./harness.js";
|
|
5
5
|
import { inspectProject } from "./project.js";
|
|
6
|
+
const maxCapturedOutputChars = 8000;
|
|
6
7
|
export const runSensorsTool = {
|
|
7
8
|
name: "run_sensors",
|
|
8
9
|
description: "Run detected project command sensors, such as build/typecheck/test, with a timeout and structured results.",
|
|
@@ -48,8 +49,29 @@ export const runSensorsTool = {
|
|
|
48
49
|
const include = stringArrayField(args, "include");
|
|
49
50
|
const root = path.resolve(repoPath);
|
|
50
51
|
const inspection = await inspectProject(root, optionalStringField(args, "surfacePath"));
|
|
52
|
+
if (!inspection.scanCoverage.complete) {
|
|
53
|
+
return textResult({
|
|
54
|
+
status: "incomplete-analysis",
|
|
55
|
+
surfacePath: inspection.selectedSurface?.path,
|
|
56
|
+
targetPlatforms: inspection.platforms,
|
|
57
|
+
scanCoverage: inspection.scanCoverage,
|
|
58
|
+
reason: "Vise could not inspect the complete project inventory, so detected sensors may be incomplete. Narrow the run with --surface/--surface-path.",
|
|
59
|
+
});
|
|
60
|
+
}
|
|
51
61
|
const detectedSensors = await detectCommandSensors(inspection.effectiveRoot, inspection.platforms);
|
|
52
62
|
const selectedSensors = include.length > 0 ? detectedSensors.filter((sensor) => include.includes(sensor.name)) : detectedSensors;
|
|
63
|
+
const unmatched = include.filter((requestedName) => !detectedSensors.some((sensor) => sensor.name === requestedName));
|
|
64
|
+
if (unmatched.length > 0) {
|
|
65
|
+
return textResult({
|
|
66
|
+
status: "invalid-selection",
|
|
67
|
+
surfacePath: inspection.selectedSurface?.path,
|
|
68
|
+
targetPlatforms: inspection.platforms,
|
|
69
|
+
requested: include,
|
|
70
|
+
unmatched,
|
|
71
|
+
available: detectedSensors.map((sensor) => sensor.name),
|
|
72
|
+
reason: `No detected sensor matched: ${unmatched.join(", ")}. Explicit --include selections are strict.`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
53
75
|
if (dryRun) {
|
|
54
76
|
return textResult({
|
|
55
77
|
status: "dry-run",
|
|
@@ -91,35 +113,56 @@ async function runSensor(cwd, sensor, timeoutMs) {
|
|
|
91
113
|
};
|
|
92
114
|
}
|
|
93
115
|
return new Promise((resolve) => {
|
|
94
|
-
|
|
95
|
-
|
|
116
|
+
const stdout = { value: "", omitted: 0 };
|
|
117
|
+
const stderr = { value: "", omitted: 0 };
|
|
118
|
+
let environmentProbeTail = "";
|
|
119
|
+
let matchedEnvironmentSkip;
|
|
96
120
|
let settled = false;
|
|
97
121
|
const child = spawn(command, args, {
|
|
98
122
|
cwd,
|
|
99
123
|
shell: false,
|
|
100
124
|
env: process.env,
|
|
125
|
+
detached: process.platform !== "win32",
|
|
101
126
|
});
|
|
127
|
+
const observeEnvironmentOutput = (chunk) => {
|
|
128
|
+
if (!matchedEnvironmentSkip && sensor.environmentSkips?.length) {
|
|
129
|
+
const probe = `${environmentProbeTail}${chunk}`;
|
|
130
|
+
matchedEnvironmentSkip = sensor.environmentSkips.find(({ pattern }) => {
|
|
131
|
+
try {
|
|
132
|
+
return new RegExp(pattern, "i").test(probe);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
environmentProbeTail = probe.slice(-4096);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
102
141
|
const timeout = setTimeout(() => {
|
|
103
142
|
if (settled) {
|
|
104
143
|
return;
|
|
105
144
|
}
|
|
106
145
|
settled = true;
|
|
107
|
-
child
|
|
146
|
+
terminateProcessTree(child);
|
|
108
147
|
resolve({
|
|
109
148
|
name: sensor.name,
|
|
110
149
|
command: sensor.command,
|
|
111
150
|
status: "timed-out",
|
|
112
151
|
durationMs: Date.now() - startedAt,
|
|
113
|
-
stdout:
|
|
114
|
-
stderr:
|
|
152
|
+
stdout: capturedOutput(stdout),
|
|
153
|
+
stderr: capturedOutput(stderr),
|
|
115
154
|
reason: sensor.timeoutReason ?? `Timed out after ${timeoutMs}ms.`,
|
|
116
155
|
});
|
|
117
156
|
}, timeoutMs);
|
|
118
157
|
child.stdout?.on("data", (chunk) => {
|
|
119
|
-
|
|
158
|
+
const text = chunk.toString("utf8");
|
|
159
|
+
captureOutput(stdout, text);
|
|
160
|
+
observeEnvironmentOutput(text);
|
|
120
161
|
});
|
|
121
162
|
child.stderr?.on("data", (chunk) => {
|
|
122
|
-
|
|
163
|
+
const text = chunk.toString("utf8");
|
|
164
|
+
captureOutput(stderr, text);
|
|
165
|
+
observeEnvironmentOutput(text);
|
|
123
166
|
});
|
|
124
167
|
child.on("error", (error) => {
|
|
125
168
|
if (settled) {
|
|
@@ -127,12 +170,13 @@ async function runSensor(cwd, sensor, timeoutMs) {
|
|
|
127
170
|
}
|
|
128
171
|
settled = true;
|
|
129
172
|
clearTimeout(timeout);
|
|
173
|
+
captureOutput(stderr, error.message);
|
|
130
174
|
resolve({
|
|
131
175
|
name: sensor.name,
|
|
132
176
|
command: sensor.command,
|
|
133
177
|
status: "failed",
|
|
134
178
|
durationMs: Date.now() - startedAt,
|
|
135
|
-
stderr:
|
|
179
|
+
stderr: capturedOutput(stderr),
|
|
136
180
|
reason: "Failed to start sensor command.",
|
|
137
181
|
});
|
|
138
182
|
});
|
|
@@ -142,29 +186,18 @@ async function runSensor(cwd, sensor, timeoutMs) {
|
|
|
142
186
|
}
|
|
143
187
|
settled = true;
|
|
144
188
|
clearTimeout(timeout);
|
|
145
|
-
if (exitCode !== 0 &&
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
189
|
+
if (exitCode !== 0 && matchedEnvironmentSkip) {
|
|
190
|
+
resolve({
|
|
191
|
+
name: sensor.name,
|
|
192
|
+
command: sensor.command,
|
|
193
|
+
status: "skipped",
|
|
194
|
+
exitCode,
|
|
195
|
+
durationMs: Date.now() - startedAt,
|
|
196
|
+
stdout: capturedOutput(stdout),
|
|
197
|
+
stderr: capturedOutput(stderr),
|
|
198
|
+
reason: matchedEnvironmentSkip.reason,
|
|
154
199
|
});
|
|
155
|
-
|
|
156
|
-
resolve({
|
|
157
|
-
name: sensor.name,
|
|
158
|
-
command: sensor.command,
|
|
159
|
-
status: "skipped",
|
|
160
|
-
exitCode,
|
|
161
|
-
durationMs: Date.now() - startedAt,
|
|
162
|
-
stdout: truncate(stdout),
|
|
163
|
-
stderr: truncate(stderr),
|
|
164
|
-
reason: environmentSkip.reason,
|
|
165
|
-
});
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
200
|
+
return;
|
|
168
201
|
}
|
|
169
202
|
resolve({
|
|
170
203
|
name: sensor.name,
|
|
@@ -172,8 +205,8 @@ async function runSensor(cwd, sensor, timeoutMs) {
|
|
|
172
205
|
status: exitCode === 0 ? "passed" : "failed",
|
|
173
206
|
exitCode,
|
|
174
207
|
durationMs: Date.now() - startedAt,
|
|
175
|
-
stdout:
|
|
176
|
-
stderr:
|
|
208
|
+
stdout: capturedOutput(stdout),
|
|
209
|
+
stderr: capturedOutput(stderr),
|
|
177
210
|
});
|
|
178
211
|
});
|
|
179
212
|
});
|
|
@@ -209,9 +242,42 @@ function stringArrayField(input, field) {
|
|
|
209
242
|
}
|
|
210
243
|
return value.filter((item) => typeof item === "string" && item.trim() !== "");
|
|
211
244
|
}
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
245
|
+
function captureOutput(output, chunk) {
|
|
246
|
+
const remaining = Math.max(0, maxCapturedOutputChars - output.value.length);
|
|
247
|
+
output.value += chunk.slice(0, remaining);
|
|
248
|
+
output.omitted += Math.max(0, chunk.length - remaining);
|
|
249
|
+
}
|
|
250
|
+
function capturedOutput(output) {
|
|
251
|
+
if (output.omitted === 0) {
|
|
252
|
+
return output.value;
|
|
215
253
|
}
|
|
216
|
-
return `${value
|
|
254
|
+
return `${output.value}\n[truncated ${output.omitted} chars]`;
|
|
255
|
+
}
|
|
256
|
+
function terminateProcessTree(child) {
|
|
257
|
+
if (!child.pid) {
|
|
258
|
+
child.kill("SIGTERM");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (process.platform === "win32") {
|
|
262
|
+
const killer = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
263
|
+
stdio: "ignore",
|
|
264
|
+
windowsHide: true,
|
|
265
|
+
});
|
|
266
|
+
killer.unref();
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
process.kill(-child.pid, "SIGTERM");
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
child.kill("SIGTERM");
|
|
274
|
+
}
|
|
275
|
+
const forceKill = setTimeout(() => {
|
|
276
|
+
try {
|
|
277
|
+
process.kill(-child.pid, "SIGKILL");
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
}
|
|
281
|
+
}, 1000);
|
|
282
|
+
forceKill.unref();
|
|
217
283
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amityco/social-plus-vise",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Skill-guided deterministic CLI for social.plus SDK integration assistance.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"ai-coding"
|
|
23
23
|
],
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
25
|
+
"node": ">=20"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
@@ -103,6 +103,12 @@
|
|
|
103
103
|
"minLength": 1,
|
|
104
104
|
"description": "Installable social.plus block id for this object. Source of truth for blockIdForObject. Omitted when the object has a surface but no dedicated block (e.g. forum, story). Absent on app-layer/in-development objects."
|
|
105
105
|
},
|
|
106
|
+
"sdkCapabilities": {
|
|
107
|
+
"type": "array",
|
|
108
|
+
"minItems": 1,
|
|
109
|
+
"items": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" },
|
|
110
|
+
"description": "SDK capability ids from Vise's sdk-facts table (vise/src/tools/sdkFacts.ts CAPABILITIES) that ground this object. Declared only when the id exists and `vise sdk-facts` proves it on the TypeScript surface — never guessed; omit when no proven mapping exists (e.g. story, app-layer objects). Consumed by the social.plus blocks catalog gap-report so draft block requests carry proven capability ids instead of a TODO(human) marker."
|
|
111
|
+
},
|
|
106
112
|
"availability": { "$ref": "#/$defs/availability" }
|
|
107
113
|
}
|
|
108
114
|
},
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
"schemaVersion": "0.1.0",
|
|
3
3
|
"status": "seeded",
|
|
4
4
|
"items": [
|
|
5
|
-
{ "id": "feed", "name": "Feed", "description": "A stream of social posts or updates.", "category": "content", "surface": "feed", "block": "feed" },
|
|
6
|
-
{ "id": "comment", "name": "Comment", "description": "A response thread attached to content or an object.", "category": "participation", "surface": "comments", "block": "comments" },
|
|
7
|
-
{ "id": "forum", "name": "Forum", "description": "A structured discussion space optimized for topics and knowledge.", "category": "knowledge", "surface": "feed" },
|
|
8
|
-
{ "id": "community", "name": "Community", "description": "A persistent group identity around people, content, or purpose.", "category": "identity", "surface": "community", "block": "community" },
|
|
9
|
-
{ "id": "creator", "name": "Creator", "description": "A person or entity that owns an audience relationship.", "category": "identity", "surface": "profile", "block": "profile" },
|
|
10
|
-
{ "id": "profile", "name": "Profile", "description": "A user or creator identity surface with social relationship signals.", "category": "identity", "surface": "profile", "block": "profile" },
|
|
11
|
-
{ "id": "chat", "name": "Chat", "description": "Direct or group conversation.", "category": "conversation", "surface": "chat", "block": "chat" },
|
|
5
|
+
{ "id": "feed", "name": "Feed", "description": "A stream of social posts or updates.", "category": "content", "surface": "feed", "block": "feed", "sdkCapabilities": ["posts"] },
|
|
6
|
+
{ "id": "comment", "name": "Comment", "description": "A response thread attached to content or an object.", "category": "participation", "surface": "comments", "block": "comments", "sdkCapabilities": ["comments"] },
|
|
7
|
+
{ "id": "forum", "name": "Forum", "description": "A structured discussion space optimized for topics and knowledge.", "category": "knowledge", "surface": "feed", "sdkCapabilities": ["posts"] },
|
|
8
|
+
{ "id": "community", "name": "Community", "description": "A persistent group identity around people, content, or purpose.", "category": "identity", "surface": "community", "block": "community", "sdkCapabilities": ["community"] },
|
|
9
|
+
{ "id": "creator", "name": "Creator", "description": "A person or entity that owns an audience relationship.", "category": "identity", "surface": "profile", "block": "profile", "sdkCapabilities": ["users"] },
|
|
10
|
+
{ "id": "profile", "name": "Profile", "description": "A user or creator identity surface with social relationship signals.", "category": "identity", "surface": "profile", "block": "profile", "sdkCapabilities": ["users"] },
|
|
11
|
+
{ "id": "chat", "name": "Chat", "description": "Direct or group conversation.", "category": "conversation", "surface": "chat", "block": "chat", "sdkCapabilities": ["chat"] },
|
|
12
12
|
{ "id": "story", "name": "Story", "description": "Ephemeral or sequenced media updates.", "category": "content", "surface": "feed" },
|
|
13
|
-
{ "id": "livestream", "name": "Livestream", "description": "Real-time video or audio participation.", "category": "live", "surface": "livestream", "block": "livestream" },
|
|
14
|
-
{ "id": "event", "name": "Event", "description": "A scheduled social moment.", "category": "live", "surface": "notifications", "block": "events" },
|
|
15
|
-
{ "id": "rsvp", "name": "RSVP", "description": "A user's intent to attend or participate in an event.", "category": "commitment", "surface": "notifications", "block": "events" },
|
|
13
|
+
{ "id": "livestream", "name": "Livestream", "description": "Real-time video or audio participation.", "category": "live", "surface": "livestream", "block": "livestream", "sdkCapabilities": ["livestream"] },
|
|
14
|
+
{ "id": "event", "name": "Event", "description": "A scheduled social moment.", "category": "live", "surface": "notifications", "block": "events", "sdkCapabilities": ["events"] },
|
|
15
|
+
{ "id": "rsvp", "name": "RSVP", "description": "A user's intent to attend or participate in an event.", "category": "commitment", "surface": "notifications", "block": "events", "sdkCapabilities": ["events"] },
|
|
16
16
|
{ "id": "challenge", "name": "Challenge", "description": "A time-bounded task or contest.", "category": "participation", "availability": "in-development" },
|
|
17
17
|
{ "id": "mission", "name": "Mission", "description": "A guided objective that encourages progression.", "category": "progression", "availability": "in-development" },
|
|
18
18
|
{ "id": "reward", "name": "Reward", "description": "A benefit or acknowledgement earned through behavior.", "category": "incentive", "availability": "in-development" },
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
{ "id": "leaderboard", "name": "Leaderboard", "description": "Ranked comparison between participants.", "category": "competition", "availability": "in-development" },
|
|
22
22
|
{ "id": "knowledge", "name": "Knowledge", "description": "Reusable question, answer, guide, or expertise content.", "category": "knowledge" },
|
|
23
23
|
{ "id": "prediction", "name": "Prediction", "description": "A user's forecast or choice about a future outcome.", "category": "participation" },
|
|
24
|
-
{ "id": "notification", "name": "Notification", "description": "A timely signal that brings users back to an engagement moment.", "category": "activation", "surface": "notifications", "block": "notifications" }
|
|
24
|
+
{ "id": "notification", "name": "Notification", "description": "A timely signal that brings users back to an engagement moment.", "category": "activation", "surface": "notifications", "block": "notifications", "sdkCapabilities": ["notification-tray"] }
|
|
25
25
|
]
|
|
26
26
|
}
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
"typescript": {
|
|
64
64
|
"status": "extracted",
|
|
65
65
|
"file": "models.typescript.json",
|
|
66
|
-
"bytes":
|
|
67
|
-
"sha256": "
|
|
66
|
+
"bytes": 102455,
|
|
67
|
+
"sha256": "8ba407b1d3f0228e6af4a0c5e99e8031950e106295bc64bf5dd67c600a9681e5",
|
|
68
68
|
"fieldsGrounding": "names-and-types",
|
|
69
|
-
"modelCount":
|
|
69
|
+
"modelCount": 14,
|
|
70
70
|
"extractor": "vise-ts-model-extractor",
|
|
71
71
|
"extractorVersion": "0.1.0",
|
|
72
|
-
"extractedAt": "2026-07-
|
|
72
|
+
"extractedAt": "2026-07-08T06:54:23.130Z"
|
|
73
73
|
},
|
|
74
74
|
"android": {
|
|
75
75
|
"status": "extracted",
|