designwithai-mcp 0.5.0 → 0.6.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/export-findings.js +142 -0
- package/dist/server.js +70 -2
- package/dist/severity.js +71 -0
- package/package.json +2 -2
- package/skills/better-interface/SKILL.md +3 -0
- package/skills/review-ai-product/SKILL.md +3 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L'EXPORT DES CONSTATS — vers les outils que l'équipe emploie déjà.
|
|
3
|
+
*
|
|
4
|
+
* Trois verbes manquaient au produit : assigner, informer, tracer. Ils
|
|
5
|
+
* demandaient jusqu'ici trois intégrations, trois jeux de secrets à stocker et
|
|
6
|
+
* trois API à maintenir — pour un produit dont la promesse est de ne rien
|
|
7
|
+
* garder de ce qui ne lui appartient pas.
|
|
8
|
+
*
|
|
9
|
+
* LA SORTIE PAR LE HAUT : on n'écrit dans aucun outil. On produit un texte
|
|
10
|
+
* parfaitement formaté, et c'est l'assistant de l'utilisateur — chez lui, avec
|
|
11
|
+
* SES serveurs déjà branchés — qui le pose dans Jira, Linear ou Slack. Zéro
|
|
12
|
+
* secret stocké, zéro jeton à révoquer, zéro API à suivre quand elle change.
|
|
13
|
+
*
|
|
14
|
+
* DÉTERMINISTE, ET C'EST LA CONDITION. Aucun appel de modèle ici : le même
|
|
15
|
+
* audit rend exactement le même texte, à la virgule près. Un export qui varie
|
|
16
|
+
* d'une fois sur l'autre produirait deux tickets pour un seul défaut — et
|
|
17
|
+
* personne ne saurait lequel fermer.
|
|
18
|
+
*/
|
|
19
|
+
import { severityRank, worstSeverity, deriveVerdict, verdictLisible } from "./severity.js";
|
|
20
|
+
/** Le seuil : ce qui mérite un ticket. Un mineur encombre une file, il ne la sert pas. */
|
|
21
|
+
export const SEUIL_TICKET = 2; // majeur et au-dessus
|
|
22
|
+
/**
|
|
23
|
+
* Ce qu'on lit en tête d'un constat.
|
|
24
|
+
*
|
|
25
|
+
* Les reviews d'avant n'ont pas de titre. Le repli le DIT au lieu d'afficher un
|
|
26
|
+
* vide — un ticket sans intitulé est un ticket que personne n'ouvre.
|
|
27
|
+
*/
|
|
28
|
+
export function titre(f) {
|
|
29
|
+
return (f.title || f.summary || "Constat sans intitulé — voir la preuve").trim();
|
|
30
|
+
}
|
|
31
|
+
const ordonner = (findings) => [...findings].sort((a, b) => severityRank(a.severity) - severityRank(b.severity));
|
|
32
|
+
/** Ce qui reste ouvert. Un constat corrigé n'a rien à faire dans une file. */
|
|
33
|
+
const ouverts = (findings) => findings.filter((f) => (f.status ?? "open") === "open");
|
|
34
|
+
/**
|
|
35
|
+
* Le bloc des limites, en pied de chaque sortie.
|
|
36
|
+
*
|
|
37
|
+
* IL NE S'OMET JAMAIS QUAND IL Y A QUELQUE CHOSE À DIRE. Un ticket se lit par
|
|
38
|
+
* quelqu'un qui n'a pas vu l'analyse : sans le périmètre, il prend un constat
|
|
39
|
+
* partiel pour un constat complet, et l'absence d'un défaut pour sa correction.
|
|
40
|
+
*/
|
|
41
|
+
function blocLimites(review) {
|
|
42
|
+
const l = review.limites ?? [];
|
|
43
|
+
if (l.length === 0)
|
|
44
|
+
return "";
|
|
45
|
+
return (`\n\n---\nCe que cette lecture n'a pas couvert :\n` +
|
|
46
|
+
l.map((x) => `- ${x}`).join("\n") +
|
|
47
|
+
`\nUn constat absent d'ici ne prouve donc pas qu'il n'existe pas.`);
|
|
48
|
+
}
|
|
49
|
+
/** Le renvoi vers l'analyse. Un ticket sans retour est un ticket orphelin. */
|
|
50
|
+
function retour(review) {
|
|
51
|
+
return `designwithai · ${review.skill} · ${review.artifact_ref} · analyse ${review.id}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Le format « tickets » — un bloc par constat, prêt à devenir une issue.
|
|
55
|
+
*
|
|
56
|
+
* Chaque bloc porte les quatre choses qu'une issue exige et qu'on oublie
|
|
57
|
+
* toujours : ce qui cloche en langage courant, la preuve qui permet de le
|
|
58
|
+
* retrouver, la correction proposée, et le critère qui dira que c'est fini.
|
|
59
|
+
* Sans le dernier, un ticket se ferme sur une impression.
|
|
60
|
+
*/
|
|
61
|
+
export function enTickets(review) {
|
|
62
|
+
const retenus = ordonner(ouverts(review.findings)).filter((f) => severityRank(f.severity) <= SEUIL_TICKET);
|
|
63
|
+
if (retenus.length === 0) {
|
|
64
|
+
return (`Aucun constat de gravité majeure ou plus sur ${review.artifact_ref}.\n\n` +
|
|
65
|
+
`Rien à mettre en file. Les constats mineurs et les notes restent dans l'analyse — ` +
|
|
66
|
+
`les sortir ici encombrerait la file de ton équipe sans la servir.` +
|
|
67
|
+
blocLimites(review));
|
|
68
|
+
}
|
|
69
|
+
const blocs = retenus.map((f, i) => {
|
|
70
|
+
const lignes = [
|
|
71
|
+
`## ${i + 1}. ${titre(f)}`,
|
|
72
|
+
"",
|
|
73
|
+
`**Gravité** : ${f.severity ?? "non précisée"}${f.location ? ` · **Où** : ${f.location}` : ""}`,
|
|
74
|
+
];
|
|
75
|
+
if (f.evidence)
|
|
76
|
+
lignes.push("", `**Ce qui le prouve**`, f.evidence);
|
|
77
|
+
if (f.impact)
|
|
78
|
+
lignes.push("", `**Pourquoi ça compte**`, f.impact);
|
|
79
|
+
if (f.correction)
|
|
80
|
+
lignes.push("", `**Correction proposée**`, f.correction);
|
|
81
|
+
lignes.push("", `**C'est fait quand** : ${f.acceptance ?? "à définir avec l'équipe — l'analyse n'a pas fourni de critère de fin"}`);
|
|
82
|
+
if (f.confidence && f.confidence !== "élevée") {
|
|
83
|
+
lignes.push("", `_Confiance ${f.confidence} : à vérifier avant d'engager du travail._`);
|
|
84
|
+
}
|
|
85
|
+
lignes.push("", `_${retour(review)}_`);
|
|
86
|
+
return lignes.join("\n");
|
|
87
|
+
});
|
|
88
|
+
const entete = `${retenus.length} ticket${retenus.length > 1 ? "s" : ""} à créer — ${review.artifact_ref}\n` +
|
|
89
|
+
`Un bloc par constat. Les mineurs et les notes ne sont pas ici : ils encombreraient la file.`;
|
|
90
|
+
return `${entete}\n\n${blocs.join("\n\n---\n\n")}${blocLimites(review)}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Le format « message » — ce qu'on poste dans un canal, tel quel.
|
|
94
|
+
*
|
|
95
|
+
* Il tient en quelques lignes parce qu'un message d'équipe se lit en passant.
|
|
96
|
+
* Le verdict traduit d'abord — personne ne doit traduire « PASS WITH FIXES » —
|
|
97
|
+
* puis ce qui a bougé, puis les trois points les plus graves. Pas dix : trois.
|
|
98
|
+
*/
|
|
99
|
+
export function enMessage(review, mouvement) {
|
|
100
|
+
const ouvertsTries = ordonner(ouverts(review.findings));
|
|
101
|
+
const lisible = verdictLisible(review.verdict) ?? verdictLisible(deriveVerdict(ouvertsTries)) ?? "Analysé";
|
|
102
|
+
const lignes = [`**${review.artifact_ref} — ${lisible.toLowerCase()}**`];
|
|
103
|
+
if (mouvement) {
|
|
104
|
+
/* Le mouvement se dit en clair, et « rien n'a bougé » est un résultat.
|
|
105
|
+
Annoncer un progrès qui n'a pas eu lieu est la faute qu'on a mis un
|
|
106
|
+
chantier entier à retirer du rapport ; elle ne rentrera pas par ici. */
|
|
107
|
+
const parts = [];
|
|
108
|
+
if (mouvement.regles > 0)
|
|
109
|
+
parts.push(`${mouvement.regles} réglé${mouvement.regles > 1 ? "s" : ""}`);
|
|
110
|
+
if (mouvement.nouveaux > 0)
|
|
111
|
+
parts.push(`${mouvement.nouveaux} nouveau${mouvement.nouveaux > 1 ? "x" : ""}`);
|
|
112
|
+
if (mouvement.restants > 0)
|
|
113
|
+
parts.push(`${mouvement.restants} encore ouvert${mouvement.restants > 1 ? "s" : ""}`);
|
|
114
|
+
lignes.push(parts.length > 0 ? `Depuis la dernière fois : ${parts.join(" · ")}.` : "Rien n'a bougé depuis la dernière fois.");
|
|
115
|
+
}
|
|
116
|
+
if (ouvertsTries.length === 0) {
|
|
117
|
+
lignes.push("", "Rien d'ouvert à signaler.");
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
lignes.push("", `Les ${Math.min(3, ouvertsTries.length)} plus graves :`);
|
|
121
|
+
for (const f of ouvertsTries.slice(0, 3)) {
|
|
122
|
+
lignes.push(`• ${titre(f)}${f.location ? ` — ${f.location}` : ""} _(${f.severity ?? "?"})_`);
|
|
123
|
+
}
|
|
124
|
+
if (ouvertsTries.length > 3) {
|
|
125
|
+
lignes.push(`_et ${ouvertsTries.length - 3} autre${ouvertsTries.length - 3 > 1 ? "s" : ""}, dans l'analyse complète._`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
lignes.push("", `_${retour(review)}_`);
|
|
129
|
+
return lignes.join("\n") + blocLimites(review);
|
|
130
|
+
}
|
|
131
|
+
/** Le compte de ce qui partirait, pour le dire avant d'exporter. */
|
|
132
|
+
export function compter(review) {
|
|
133
|
+
const o = ouverts(review.findings);
|
|
134
|
+
return {
|
|
135
|
+
total: review.findings.length,
|
|
136
|
+
exportables: o.filter((f) => severityRank(f.severity) <= SEUIL_TICKET).length,
|
|
137
|
+
pire: worstSeverity(o),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
export function exporter(review, format, mouvement) {
|
|
141
|
+
return format === "tickets" ? enTickets(review) : enMessage(review, mouvement);
|
|
142
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -18,6 +18,8 @@ import { compareReviews, getReview, listReviews, saveReview, updateFindingStatus
|
|
|
18
18
|
import { saveDecision } from "./decisions.js";
|
|
19
19
|
import { clearGate, loadState, reopenGate, saveState } from "./state.js";
|
|
20
20
|
import { readRepoSnapshot } from "./connectors.js";
|
|
21
|
+
import { SEVERITES } from "./severity.js";
|
|
22
|
+
import { compter, exporter } from "./export-findings.js";
|
|
21
23
|
import { inventorierCode } from "./ds-inventory.js";
|
|
22
24
|
import { buildPayload, rendreEnvoi, rendreInventaire } from "./inventory-tool.js";
|
|
23
25
|
import { trySemanticScores } from "./embeddings.js";
|
|
@@ -196,7 +198,7 @@ const TOOLS = [
|
|
|
196
198
|
items: {
|
|
197
199
|
type: "object",
|
|
198
200
|
properties: {
|
|
199
|
-
severity: { type: "string", enum: [
|
|
201
|
+
severity: { type: "string", enum: [...SEVERITES] },
|
|
200
202
|
location: { type: "string" },
|
|
201
203
|
category: { type: "string" },
|
|
202
204
|
summary: { type: "string" },
|
|
@@ -443,6 +445,28 @@ const TOOLS = [
|
|
|
443
445
|
},
|
|
444
446
|
},
|
|
445
447
|
},
|
|
448
|
+
{
|
|
449
|
+
name: "export_findings",
|
|
450
|
+
description: "Transforme les constats d'une review en TICKETS prêts à créer, ou en MESSAGE prêt à " +
|
|
451
|
+
"poster. Rien n'est envoyé nulle part : tu reçois un texte, et c'est TOI qui le poses " +
|
|
452
|
+
"dans Jira, Linear, GitHub ou Slack avec les serveurs que l'utilisateur a déjà branchés. " +
|
|
453
|
+
"Déterministe — le même audit rend le même texte.",
|
|
454
|
+
inputSchema: {
|
|
455
|
+
type: "object",
|
|
456
|
+
properties: {
|
|
457
|
+
format: {
|
|
458
|
+
type: "string",
|
|
459
|
+
enum: ["tickets", "message"],
|
|
460
|
+
description: "tickets = un bloc par constat majeur ou plus, avec preuve, correction et critère " +
|
|
461
|
+
"de fin. message = le verdict, ce qui a bougé, et les trois plus graves.",
|
|
462
|
+
},
|
|
463
|
+
review_id: { type: "string", description: "La review à exporter. Par défaut, la dernière." },
|
|
464
|
+
regles: { type: "number", description: "Constats réglés depuis la fois d'avant, si tu le sais." },
|
|
465
|
+
nouveaux: { type: "number", description: "Constats apparus depuis." },
|
|
466
|
+
},
|
|
467
|
+
required: ["format"],
|
|
468
|
+
},
|
|
469
|
+
},
|
|
446
470
|
{
|
|
447
471
|
name: "framing_criteria",
|
|
448
472
|
description: "Charge les CRITÈRES D'ACCEPTATION d'un cadrage écrit avant de construire, pour vérifier " +
|
|
@@ -644,7 +668,7 @@ function toolPlan(args) {
|
|
|
644
668
|
}
|
|
645
669
|
// ─── Reviews suivies (étape C) ───────────────────────────────────────────────
|
|
646
670
|
function sevCounts(findings) {
|
|
647
|
-
const order =
|
|
671
|
+
const order = SEVERITES;
|
|
648
672
|
const c = {};
|
|
649
673
|
for (const f of findings) {
|
|
650
674
|
const s = f.severity ?? "note";
|
|
@@ -1050,6 +1074,48 @@ function toolNextGate(args) {
|
|
|
1050
1074
|
* dit explicitement quoi en faire — c'est `verify_fix` qui statue, un critère à
|
|
1051
1075
|
* la fois, avec sa preuve.
|
|
1052
1076
|
*/
|
|
1077
|
+
/**
|
|
1078
|
+
* `export_findings` — les trois verbes, sans une seule intégration.
|
|
1079
|
+
*
|
|
1080
|
+
* Assigner, informer, tracer manquaient au produit. Les câbler aurait demandé
|
|
1081
|
+
* trois API, trois jeux de secrets et leur maintenance — pour un produit dont
|
|
1082
|
+
* la promesse est de ne rien garder qui ne lui appartienne pas.
|
|
1083
|
+
*
|
|
1084
|
+
* On sort par le haut : le texte est produit ici, parfaitement formaté, et
|
|
1085
|
+
* c'est l'assistant de l'utilisateur qui le pose dans SES outils, avec SES
|
|
1086
|
+
* serveurs déjà branchés. Aucun secret ne transite, aucune API à suivre quand
|
|
1087
|
+
* elle change.
|
|
1088
|
+
*/
|
|
1089
|
+
function toolExportFindings(args) {
|
|
1090
|
+
const format = String(args.format ?? "");
|
|
1091
|
+
if (format !== "tickets" && format !== "message") {
|
|
1092
|
+
return refusal("export_findings attend un format : « tickets » ou « message ».");
|
|
1093
|
+
}
|
|
1094
|
+
const review = args.review_id ? getReview(String(args.review_id)) : (listReviews()[0] ?? null);
|
|
1095
|
+
if (!review) {
|
|
1096
|
+
return refusal("Aucune review à exporter. Lance d'abord un skill et enregistre le résultat avec save_review.");
|
|
1097
|
+
}
|
|
1098
|
+
const compte = compter(review);
|
|
1099
|
+
/* Le mouvement n'est employé QUE si l'appelant le connaît. On ne le déduit
|
|
1100
|
+
pas d'une comparaison de photos : c'est exactement ce qui avait produit un
|
|
1101
|
+
« ton produit monte » sur un site que personne n'avait touché. */
|
|
1102
|
+
const mouvement = typeof args.regles === "number" || typeof args.nouveaux === "number"
|
|
1103
|
+
? {
|
|
1104
|
+
regles: Number(args.regles ?? 0),
|
|
1105
|
+
nouveaux: Number(args.nouveaux ?? 0),
|
|
1106
|
+
restants: review.findings.filter((f) => (f.status ?? "open") === "open").length,
|
|
1107
|
+
}
|
|
1108
|
+
: undefined;
|
|
1109
|
+
const corps = exporter(review, format, mouvement);
|
|
1110
|
+
const entete = format === "tickets"
|
|
1111
|
+
? `${compte.exportables} constat(s) exportable(s) sur ${compte.total} — les mineurs et les notes restent dans l'analyse.\n` +
|
|
1112
|
+
`CE QUE TU EN FAIS : crée une issue par bloc, avec les serveurs que l'utilisateur a déjà branchés ` +
|
|
1113
|
+
`(Jira, Linear, GitHub). Ne réécris pas les titres : ils sont déjà écrits pour être lus par quelqu'un ` +
|
|
1114
|
+
`qui n'a pas vu l'analyse. Si aucun serveur n'est branché, rends-lui le texte tel quel.`
|
|
1115
|
+
: `CE QUE TU EN FAIS : poste ce message tel quel dans le canal qu'il te dira (Slack, Teams). ` +
|
|
1116
|
+
`Ne l'allonge pas : il est court parce qu'un message d'équipe se lit en passant.`;
|
|
1117
|
+
return text(`${entete}\n\n${corps}`);
|
|
1118
|
+
}
|
|
1053
1119
|
async function toolFramingCriteria(args) {
|
|
1054
1120
|
const token = process.env.DWAI_TOKEN?.trim();
|
|
1055
1121
|
if (!token) {
|
|
@@ -1255,6 +1321,8 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1255
1321
|
return toolGenerateDecision(args);
|
|
1256
1322
|
case "next_gate":
|
|
1257
1323
|
return toolNextGate(args);
|
|
1324
|
+
case "export_findings":
|
|
1325
|
+
return toolExportFindings(args);
|
|
1258
1326
|
case "framing_criteria":
|
|
1259
1327
|
return toolFramingCriteria(args);
|
|
1260
1328
|
case "inventory_ds":
|
package/dist/severity.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LA SÉVÉRITÉ ET LE VERDICT, CÔTÉ MCP.
|
|
3
|
+
*
|
|
4
|
+
* La définition de référence vit dans `supabase/functions/_shared/severity.ts` :
|
|
5
|
+
* c'est elle que le serveur applique et que le site affiche. Ce paquet-ci ne
|
|
6
|
+
* peut pas l'importer — il est publié seul sur npm, et un import qui remonte
|
|
7
|
+
* hors de son dossier ne survivrait pas à l'emballage. C'est la même frontière
|
|
8
|
+
* que celle de l'inventaire Figma, et elle se paie une fois.
|
|
9
|
+
*
|
|
10
|
+
* Ce fichier est donc une RÉÉCRITURE, pas une copie de confort. Et
|
|
11
|
+
* `scripts/test-severite-frontiere.ts` empêche les deux de diverger : il
|
|
12
|
+
* compare les deux tables terme à terme, depuis le site qui peut lire les deux.
|
|
13
|
+
*
|
|
14
|
+
* CE QU'IL REMPLACE. La même échelle était écrite à trois endroits du paquet —
|
|
15
|
+
* le type dans `reviews.ts`, l'énumération du schéma d'outil, et un tableau
|
|
16
|
+
* d'ordre au milieu du serveur. Trois copies qui n'avaient aucun moyen de se
|
|
17
|
+
* contredire bruyamment.
|
|
18
|
+
*/
|
|
19
|
+
/** Du plus grave au moins grave. L'ordre EST la définition. */
|
|
20
|
+
export const SEVERITES = ["bloquant", "critique", "majeur", "mineur", "note"];
|
|
21
|
+
const RANK = Object.fromEntries(SEVERITES.map((s, i) => [s, i]));
|
|
22
|
+
/** Une sévérité inconnue passe en dernier — jamais devant un défaut réel. */
|
|
23
|
+
export function severityRank(s) {
|
|
24
|
+
return RANK[(s ?? "").toLowerCase().trim()] ?? 9;
|
|
25
|
+
}
|
|
26
|
+
/** La plus grave d'un lot, ou `null` s'il n'y a rien. */
|
|
27
|
+
export function worstSeverity(findings) {
|
|
28
|
+
let pire = null;
|
|
29
|
+
for (const f of findings) {
|
|
30
|
+
const s = (f.severity ?? "").toLowerCase().trim();
|
|
31
|
+
if (!RANK[s])
|
|
32
|
+
if (RANK[s] !== 0)
|
|
33
|
+
continue;
|
|
34
|
+
if (pire === null || severityRank(s) < severityRank(pire))
|
|
35
|
+
pire = s;
|
|
36
|
+
}
|
|
37
|
+
return pire;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Le verdict se DÉDUIT des sévérités, il ne se décide pas.
|
|
41
|
+
*
|
|
42
|
+
* Un bloquant, ou deux critiques, et c'est un échec. Un critique isolé ou un
|
|
43
|
+
* majeur laissent passer avec des corrections. La règle est celle du serveur,
|
|
44
|
+
* terme à terme.
|
|
45
|
+
*/
|
|
46
|
+
export function deriveVerdict(findings) {
|
|
47
|
+
const compte = (s) => findings.filter((f) => (f.severity ?? "").toLowerCase().trim() === s).length;
|
|
48
|
+
if (compte("bloquant") > 0)
|
|
49
|
+
return "FAIL";
|
|
50
|
+
if (compte("critique") >= 2)
|
|
51
|
+
return "FAIL";
|
|
52
|
+
if (compte("critique") === 1 || compte("majeur") > 0)
|
|
53
|
+
return "PASS WITH FIXES";
|
|
54
|
+
return "PASS";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* LES VERDICTS, DITS EN FRANÇAIS.
|
|
58
|
+
*
|
|
59
|
+
* `PASS`, `FAIL`, `BLOCKED` sont du vocabulaire de fabrication. Ils n'ont rien à
|
|
60
|
+
* faire dans un ticket que lira quelqu'un qui n'a jamais ouvert ce produit.
|
|
61
|
+
*/
|
|
62
|
+
export const VERDICT_LISIBLE = {
|
|
63
|
+
PASS: "Ça tient",
|
|
64
|
+
"PASS WITH FIXES": "Presque prêt — des choses à corriger",
|
|
65
|
+
FAIL: "À corriger avant d'ouvrir",
|
|
66
|
+
BLOCKED: "Pas assez d'infos pour juger",
|
|
67
|
+
};
|
|
68
|
+
export function verdictLisible(v) {
|
|
69
|
+
const cle = String(v ?? "").trim().toUpperCase();
|
|
70
|
+
return VERDICT_LISIBLE[cle] ?? null;
|
|
71
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "designwithai-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"description": "Le serveur designwithai pour ton éditeur : audite ton produit — interface, navigation, performance, sécurité des données, hygiène Figma — trace les corrections, et fait remonter les résultats dans ton tableau de bord.",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsc",
|
|
29
|
-
"test": "tsx scripts/test-finding-identity.ts && tsx scripts/test-scoring.ts && tsx scripts/test-ds-diff.ts && tsx scripts/test-ds-match.ts && tsx scripts/test-ds-corpus.ts && tsx scripts/test-ds-inventory.ts && tsx scripts/test-inventory-tool.ts && tsx scripts/check-goldens.ts",
|
|
29
|
+
"test": "tsx scripts/test-finding-identity.ts && tsx scripts/test-scoring.ts && tsx scripts/test-ds-diff.ts && tsx scripts/test-ds-match.ts && tsx scripts/test-ds-corpus.ts && tsx scripts/test-export-findings.ts && tsx scripts/test-ds-inventory.ts && tsx scripts/test-inventory-tool.ts && tsx scripts/check-goldens.ts",
|
|
30
30
|
"start": "node dist/server.js",
|
|
31
31
|
"dev": "tsx src/server.ts",
|
|
32
32
|
"embed": "node scripts/embed.mjs",
|
|
@@ -133,6 +133,9 @@ Lot 4 - Polish et prévention
|
|
|
133
133
|
- Axes non vérifiés, nommés
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
|
|
137
|
+
Ces constats s'exportent en tickets ou en message d'équipe — `export_findings`. Rien n'est envoyé nulle part : l'outil rend un texte, et c'est l'assistant de la personne qui le pose dans SES outils, avec SES accès.
|
|
138
|
+
|
|
136
139
|
## Critères de qualité du skill
|
|
137
140
|
|
|
138
141
|
- Les skills activés sont justifiés par le contenu de l'artefact.
|
|
@@ -219,6 +219,9 @@ Lot 4 Accélérer
|
|
|
219
219
|
- Axes non vérifiés, nommés
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
+
|
|
223
|
+
Ces constats s'exportent en tickets ou en message d'équipe — `export_findings`. Rien n'est envoyé nulle part : l'outil rend un texte, et c'est l'assistant de la personne qui le pose dans SES outils, avec SES accès.
|
|
224
|
+
|
|
222
225
|
## Critères de qualité du skill
|
|
223
226
|
|
|
224
227
|
- Le verdict sépare clairement prototype, V0, production et industrialisation.
|