@codyswann/lisa 2.144.0 → 2.145.1
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/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +13 -8
- package/dist/cli/apply.js.map +1 -1
- package/dist/core/project-config.d.ts +32 -0
- package/dist/core/project-config.d.ts.map +1 -1
- package/dist/core/project-config.js +35 -0
- package/dist/core/project-config.js.map +1 -1
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/scripts/wiki-safety.mjs +76 -0
- package/plugins/lisa-wiki/skills/lisa-wiki-ingest/SKILL.md +12 -5
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/scripts/wiki-safety.mjs +76 -0
- package/plugins/lisa-wiki-agy/skills/lisa-wiki-ingest/SKILL.md +12 -5
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/scripts/wiki-safety.mjs +76 -0
- package/plugins/lisa-wiki-copilot/skills/lisa-wiki-ingest/SKILL.md +12 -5
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/scripts/wiki-safety.mjs +76 -0
- package/plugins/lisa-wiki-cursor/skills/lisa-wiki-ingest/SKILL.md +12 -5
- package/plugins/src/wiki/scripts/wiki-safety.mjs +76 -0
- package/plugins/src/wiki/skills/lisa-wiki-ingest/SKILL.md +12 -5
|
@@ -236,6 +236,82 @@ export function serializeWikiSafetyFindings(result) {
|
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
function normalizeSafetyResults(results) {
|
|
240
|
+
if (Array.isArray(results)) return results;
|
|
241
|
+
if (!results) return [];
|
|
242
|
+
return [results];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function summarizeEntityTypes(results) {
|
|
246
|
+
const byType = new Map();
|
|
247
|
+
for (const result of results) {
|
|
248
|
+
for (const finding of result?.findings ?? []) {
|
|
249
|
+
const entityType = finding?.entityType ?? "unknown";
|
|
250
|
+
const current = byType.get(entityType) ?? {
|
|
251
|
+
entityType,
|
|
252
|
+
confidence: finding?.confidence ?? "unknown",
|
|
253
|
+
count: 0,
|
|
254
|
+
};
|
|
255
|
+
current.count += Number.isFinite(finding?.count) ? finding.count : 1;
|
|
256
|
+
if (current.confidence !== "high" && finding?.confidence === "high") {
|
|
257
|
+
current.confidence = "high";
|
|
258
|
+
}
|
|
259
|
+
byType.set(entityType, current);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return [...byType.values()].sort((a, b) =>
|
|
263
|
+
a.entityType.localeCompare(b.entityType)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function createWikiIngestPublicationPolicy(options = {}) {
|
|
268
|
+
const safetyResults = normalizeSafetyResults(options.safetyResults);
|
|
269
|
+
const externalWrite = Boolean(options.externalWrite);
|
|
270
|
+
const entityTypes = summarizeEntityTypes(safetyResults);
|
|
271
|
+
const findingCount = entityTypes.reduce((sum, item) => sum + item.count, 0);
|
|
272
|
+
const reviewRequired =
|
|
273
|
+
externalWrite ||
|
|
274
|
+
findingCount > 0 ||
|
|
275
|
+
safetyResults.some(result => Boolean(result?.reviewRequired));
|
|
276
|
+
const reasons = [
|
|
277
|
+
...(externalWrite ? ["external-write source"] : []),
|
|
278
|
+
...(findingCount > 0 ? ["redacted or sensitive findings"] : []),
|
|
279
|
+
...(!externalWrite &&
|
|
280
|
+
findingCount === 0 &&
|
|
281
|
+
safetyResults.some(result => Boolean(result?.reviewRequired))
|
|
282
|
+
? ["source safety review requested"]
|
|
283
|
+
: []),
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
const findingLines =
|
|
287
|
+
entityTypes.length > 0
|
|
288
|
+
? entityTypes.map(
|
|
289
|
+
item =>
|
|
290
|
+
`- ${item.entityType}: ${item.count} ${item.count === 1 ? "finding" : "findings"} (${item.confidence})`
|
|
291
|
+
)
|
|
292
|
+
: ["- none"];
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
autoMergeAllowed: !reviewRequired,
|
|
296
|
+
reviewRequired,
|
|
297
|
+
reasons,
|
|
298
|
+
findingCount,
|
|
299
|
+
entityTypes,
|
|
300
|
+
prSummaryMarkdown: [
|
|
301
|
+
"## Wiki Safety Review",
|
|
302
|
+
"",
|
|
303
|
+
`Auto-merge: ${reviewRequired ? "disabled" : "allowed"}`,
|
|
304
|
+
`Human review required: ${reviewRequired ? "yes" : "no"}`,
|
|
305
|
+
`Reason: ${reasons.length > 0 ? reasons.join("; ") : "no redactions or sensitive findings"}`,
|
|
306
|
+
"",
|
|
307
|
+
"Finding summary:",
|
|
308
|
+
...findingLines,
|
|
309
|
+
"",
|
|
310
|
+
"Raw sensitive values are intentionally omitted from this summary.",
|
|
311
|
+
].join("\n"),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
239
315
|
export function isWikiSafetyScanTarget(filePath, options = {}) {
|
|
240
316
|
const pathModule = options.pathModule;
|
|
241
317
|
if (!pathModule) {
|
|
@@ -50,11 +50,14 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
50
50
|
7. **Commit/PR**: commit only the ingestion changes per `config.git` policy. If the ingest started on
|
|
51
51
|
the default branch, create a dedicated ingestion branch first — never commit ingestion straight to
|
|
52
52
|
the default. Push the branch and **open a PR targeting the default remote branch** (via the host's
|
|
53
|
-
PR mechanism — e.g. `gh pr create --base <default>`)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
PR mechanism — e.g. `gh pr create --base <default>`). Before enabling auto-merge, feed the safety
|
|
54
|
+
scan output into `createWikiIngestPublicationPolicy` from `scripts/wiki-safety.mjs`. Enable
|
|
55
|
+
auto-merge only when `autoMergeAllowed` is true. `external-write` runs, redacted runs, and any run
|
|
56
|
+
with sensitive findings are the exception — open the PR **without** auto-merge so a human reviews
|
|
57
|
+
them before it lands. The PR summary must include the policy's safe review summary: counts and
|
|
58
|
+
entity types only, never raw values, ranges, tokens, source snippets, or scanner output. If
|
|
59
|
+
auto-merge cannot be enabled (the host doesn't support it, branch protection forbids it, or the
|
|
60
|
+
policy requires review), leave the PR open and note that a human must merge.
|
|
58
61
|
|
|
59
62
|
## Rules
|
|
60
63
|
- **Bookend every ingest with git hygiene:** sync the branch with the default remote branch *before*
|
|
@@ -71,6 +74,10 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
71
74
|
select `--scanner gitleaks` for local parity with Gitleaks, and may use
|
|
72
75
|
`--scanner trufflehog` as a stricter optional verification pass when installed;
|
|
73
76
|
if a selected scanner is unavailable, keep the ingest blocked for review.
|
|
77
|
+
- Redaction and review policy is centralized in `scripts/wiki-safety.mjs`. Treat
|
|
78
|
+
`reviewRequired` or any summarized finding as a hard auto-merge stop. Use the
|
|
79
|
+
generated PR summary text as-is or preserve its shape so reviewers see entity
|
|
80
|
+
types and counts without raw sensitive values.
|
|
74
81
|
- Connector execution and the connector contract are detailed in the connector skills (M2+); this
|
|
75
82
|
router defines and enforces the ordering and side-effect rules above.
|
|
76
83
|
|
|
@@ -236,6 +236,82 @@ export function serializeWikiSafetyFindings(result) {
|
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
function normalizeSafetyResults(results) {
|
|
240
|
+
if (Array.isArray(results)) return results;
|
|
241
|
+
if (!results) return [];
|
|
242
|
+
return [results];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function summarizeEntityTypes(results) {
|
|
246
|
+
const byType = new Map();
|
|
247
|
+
for (const result of results) {
|
|
248
|
+
for (const finding of result?.findings ?? []) {
|
|
249
|
+
const entityType = finding?.entityType ?? "unknown";
|
|
250
|
+
const current = byType.get(entityType) ?? {
|
|
251
|
+
entityType,
|
|
252
|
+
confidence: finding?.confidence ?? "unknown",
|
|
253
|
+
count: 0,
|
|
254
|
+
};
|
|
255
|
+
current.count += Number.isFinite(finding?.count) ? finding.count : 1;
|
|
256
|
+
if (current.confidence !== "high" && finding?.confidence === "high") {
|
|
257
|
+
current.confidence = "high";
|
|
258
|
+
}
|
|
259
|
+
byType.set(entityType, current);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return [...byType.values()].sort((a, b) =>
|
|
263
|
+
a.entityType.localeCompare(b.entityType)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function createWikiIngestPublicationPolicy(options = {}) {
|
|
268
|
+
const safetyResults = normalizeSafetyResults(options.safetyResults);
|
|
269
|
+
const externalWrite = Boolean(options.externalWrite);
|
|
270
|
+
const entityTypes = summarizeEntityTypes(safetyResults);
|
|
271
|
+
const findingCount = entityTypes.reduce((sum, item) => sum + item.count, 0);
|
|
272
|
+
const reviewRequired =
|
|
273
|
+
externalWrite ||
|
|
274
|
+
findingCount > 0 ||
|
|
275
|
+
safetyResults.some(result => Boolean(result?.reviewRequired));
|
|
276
|
+
const reasons = [
|
|
277
|
+
...(externalWrite ? ["external-write source"] : []),
|
|
278
|
+
...(findingCount > 0 ? ["redacted or sensitive findings"] : []),
|
|
279
|
+
...(!externalWrite &&
|
|
280
|
+
findingCount === 0 &&
|
|
281
|
+
safetyResults.some(result => Boolean(result?.reviewRequired))
|
|
282
|
+
? ["source safety review requested"]
|
|
283
|
+
: []),
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
const findingLines =
|
|
287
|
+
entityTypes.length > 0
|
|
288
|
+
? entityTypes.map(
|
|
289
|
+
item =>
|
|
290
|
+
`- ${item.entityType}: ${item.count} ${item.count === 1 ? "finding" : "findings"} (${item.confidence})`
|
|
291
|
+
)
|
|
292
|
+
: ["- none"];
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
autoMergeAllowed: !reviewRequired,
|
|
296
|
+
reviewRequired,
|
|
297
|
+
reasons,
|
|
298
|
+
findingCount,
|
|
299
|
+
entityTypes,
|
|
300
|
+
prSummaryMarkdown: [
|
|
301
|
+
"## Wiki Safety Review",
|
|
302
|
+
"",
|
|
303
|
+
`Auto-merge: ${reviewRequired ? "disabled" : "allowed"}`,
|
|
304
|
+
`Human review required: ${reviewRequired ? "yes" : "no"}`,
|
|
305
|
+
`Reason: ${reasons.length > 0 ? reasons.join("; ") : "no redactions or sensitive findings"}`,
|
|
306
|
+
"",
|
|
307
|
+
"Finding summary:",
|
|
308
|
+
...findingLines,
|
|
309
|
+
"",
|
|
310
|
+
"Raw sensitive values are intentionally omitted from this summary.",
|
|
311
|
+
].join("\n"),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
239
315
|
export function isWikiSafetyScanTarget(filePath, options = {}) {
|
|
240
316
|
const pathModule = options.pathModule;
|
|
241
317
|
if (!pathModule) {
|
|
@@ -50,11 +50,14 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
50
50
|
7. **Commit/PR**: commit only the ingestion changes per `config.git` policy. If the ingest started on
|
|
51
51
|
the default branch, create a dedicated ingestion branch first — never commit ingestion straight to
|
|
52
52
|
the default. Push the branch and **open a PR targeting the default remote branch** (via the host's
|
|
53
|
-
PR mechanism — e.g. `gh pr create --base <default>`)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
PR mechanism — e.g. `gh pr create --base <default>`). Before enabling auto-merge, feed the safety
|
|
54
|
+
scan output into `createWikiIngestPublicationPolicy` from `scripts/wiki-safety.mjs`. Enable
|
|
55
|
+
auto-merge only when `autoMergeAllowed` is true. `external-write` runs, redacted runs, and any run
|
|
56
|
+
with sensitive findings are the exception — open the PR **without** auto-merge so a human reviews
|
|
57
|
+
them before it lands. The PR summary must include the policy's safe review summary: counts and
|
|
58
|
+
entity types only, never raw values, ranges, tokens, source snippets, or scanner output. If
|
|
59
|
+
auto-merge cannot be enabled (the host doesn't support it, branch protection forbids it, or the
|
|
60
|
+
policy requires review), leave the PR open and note that a human must merge.
|
|
58
61
|
|
|
59
62
|
## Rules
|
|
60
63
|
- **Bookend every ingest with git hygiene:** sync the branch with the default remote branch *before*
|
|
@@ -71,6 +74,10 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
71
74
|
select `--scanner gitleaks` for local parity with Gitleaks, and may use
|
|
72
75
|
`--scanner trufflehog` as a stricter optional verification pass when installed;
|
|
73
76
|
if a selected scanner is unavailable, keep the ingest blocked for review.
|
|
77
|
+
- Redaction and review policy is centralized in `scripts/wiki-safety.mjs`. Treat
|
|
78
|
+
`reviewRequired` or any summarized finding as a hard auto-merge stop. Use the
|
|
79
|
+
generated PR summary text as-is or preserve its shape so reviewers see entity
|
|
80
|
+
types and counts without raw sensitive values.
|
|
74
81
|
- Connector execution and the connector contract are detailed in the connector skills (M2+); this
|
|
75
82
|
router defines and enforces the ordering and side-effect rules above.
|
|
76
83
|
|
|
@@ -236,6 +236,82 @@ export function serializeWikiSafetyFindings(result) {
|
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
function normalizeSafetyResults(results) {
|
|
240
|
+
if (Array.isArray(results)) return results;
|
|
241
|
+
if (!results) return [];
|
|
242
|
+
return [results];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function summarizeEntityTypes(results) {
|
|
246
|
+
const byType = new Map();
|
|
247
|
+
for (const result of results) {
|
|
248
|
+
for (const finding of result?.findings ?? []) {
|
|
249
|
+
const entityType = finding?.entityType ?? "unknown";
|
|
250
|
+
const current = byType.get(entityType) ?? {
|
|
251
|
+
entityType,
|
|
252
|
+
confidence: finding?.confidence ?? "unknown",
|
|
253
|
+
count: 0,
|
|
254
|
+
};
|
|
255
|
+
current.count += Number.isFinite(finding?.count) ? finding.count : 1;
|
|
256
|
+
if (current.confidence !== "high" && finding?.confidence === "high") {
|
|
257
|
+
current.confidence = "high";
|
|
258
|
+
}
|
|
259
|
+
byType.set(entityType, current);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return [...byType.values()].sort((a, b) =>
|
|
263
|
+
a.entityType.localeCompare(b.entityType)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function createWikiIngestPublicationPolicy(options = {}) {
|
|
268
|
+
const safetyResults = normalizeSafetyResults(options.safetyResults);
|
|
269
|
+
const externalWrite = Boolean(options.externalWrite);
|
|
270
|
+
const entityTypes = summarizeEntityTypes(safetyResults);
|
|
271
|
+
const findingCount = entityTypes.reduce((sum, item) => sum + item.count, 0);
|
|
272
|
+
const reviewRequired =
|
|
273
|
+
externalWrite ||
|
|
274
|
+
findingCount > 0 ||
|
|
275
|
+
safetyResults.some(result => Boolean(result?.reviewRequired));
|
|
276
|
+
const reasons = [
|
|
277
|
+
...(externalWrite ? ["external-write source"] : []),
|
|
278
|
+
...(findingCount > 0 ? ["redacted or sensitive findings"] : []),
|
|
279
|
+
...(!externalWrite &&
|
|
280
|
+
findingCount === 0 &&
|
|
281
|
+
safetyResults.some(result => Boolean(result?.reviewRequired))
|
|
282
|
+
? ["source safety review requested"]
|
|
283
|
+
: []),
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
const findingLines =
|
|
287
|
+
entityTypes.length > 0
|
|
288
|
+
? entityTypes.map(
|
|
289
|
+
item =>
|
|
290
|
+
`- ${item.entityType}: ${item.count} ${item.count === 1 ? "finding" : "findings"} (${item.confidence})`
|
|
291
|
+
)
|
|
292
|
+
: ["- none"];
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
autoMergeAllowed: !reviewRequired,
|
|
296
|
+
reviewRequired,
|
|
297
|
+
reasons,
|
|
298
|
+
findingCount,
|
|
299
|
+
entityTypes,
|
|
300
|
+
prSummaryMarkdown: [
|
|
301
|
+
"## Wiki Safety Review",
|
|
302
|
+
"",
|
|
303
|
+
`Auto-merge: ${reviewRequired ? "disabled" : "allowed"}`,
|
|
304
|
+
`Human review required: ${reviewRequired ? "yes" : "no"}`,
|
|
305
|
+
`Reason: ${reasons.length > 0 ? reasons.join("; ") : "no redactions or sensitive findings"}`,
|
|
306
|
+
"",
|
|
307
|
+
"Finding summary:",
|
|
308
|
+
...findingLines,
|
|
309
|
+
"",
|
|
310
|
+
"Raw sensitive values are intentionally omitted from this summary.",
|
|
311
|
+
].join("\n"),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
239
315
|
export function isWikiSafetyScanTarget(filePath, options = {}) {
|
|
240
316
|
const pathModule = options.pathModule;
|
|
241
317
|
if (!pathModule) {
|
|
@@ -50,11 +50,14 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
50
50
|
7. **Commit/PR**: commit only the ingestion changes per `config.git` policy. If the ingest started on
|
|
51
51
|
the default branch, create a dedicated ingestion branch first — never commit ingestion straight to
|
|
52
52
|
the default. Push the branch and **open a PR targeting the default remote branch** (via the host's
|
|
53
|
-
PR mechanism — e.g. `gh pr create --base <default>`)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
PR mechanism — e.g. `gh pr create --base <default>`). Before enabling auto-merge, feed the safety
|
|
54
|
+
scan output into `createWikiIngestPublicationPolicy` from `scripts/wiki-safety.mjs`. Enable
|
|
55
|
+
auto-merge only when `autoMergeAllowed` is true. `external-write` runs, redacted runs, and any run
|
|
56
|
+
with sensitive findings are the exception — open the PR **without** auto-merge so a human reviews
|
|
57
|
+
them before it lands. The PR summary must include the policy's safe review summary: counts and
|
|
58
|
+
entity types only, never raw values, ranges, tokens, source snippets, or scanner output. If
|
|
59
|
+
auto-merge cannot be enabled (the host doesn't support it, branch protection forbids it, or the
|
|
60
|
+
policy requires review), leave the PR open and note that a human must merge.
|
|
58
61
|
|
|
59
62
|
## Rules
|
|
60
63
|
- **Bookend every ingest with git hygiene:** sync the branch with the default remote branch *before*
|
|
@@ -71,6 +74,10 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
71
74
|
select `--scanner gitleaks` for local parity with Gitleaks, and may use
|
|
72
75
|
`--scanner trufflehog` as a stricter optional verification pass when installed;
|
|
73
76
|
if a selected scanner is unavailable, keep the ingest blocked for review.
|
|
77
|
+
- Redaction and review policy is centralized in `scripts/wiki-safety.mjs`. Treat
|
|
78
|
+
`reviewRequired` or any summarized finding as a hard auto-merge stop. Use the
|
|
79
|
+
generated PR summary text as-is or preserve its shape so reviewers see entity
|
|
80
|
+
types and counts without raw sensitive values.
|
|
74
81
|
- Connector execution and the connector contract are detailed in the connector skills (M2+); this
|
|
75
82
|
router defines and enforces the ordering and side-effect rules above.
|
|
76
83
|
|
|
@@ -236,6 +236,82 @@ export function serializeWikiSafetyFindings(result) {
|
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
function normalizeSafetyResults(results) {
|
|
240
|
+
if (Array.isArray(results)) return results;
|
|
241
|
+
if (!results) return [];
|
|
242
|
+
return [results];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function summarizeEntityTypes(results) {
|
|
246
|
+
const byType = new Map();
|
|
247
|
+
for (const result of results) {
|
|
248
|
+
for (const finding of result?.findings ?? []) {
|
|
249
|
+
const entityType = finding?.entityType ?? "unknown";
|
|
250
|
+
const current = byType.get(entityType) ?? {
|
|
251
|
+
entityType,
|
|
252
|
+
confidence: finding?.confidence ?? "unknown",
|
|
253
|
+
count: 0,
|
|
254
|
+
};
|
|
255
|
+
current.count += Number.isFinite(finding?.count) ? finding.count : 1;
|
|
256
|
+
if (current.confidence !== "high" && finding?.confidence === "high") {
|
|
257
|
+
current.confidence = "high";
|
|
258
|
+
}
|
|
259
|
+
byType.set(entityType, current);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return [...byType.values()].sort((a, b) =>
|
|
263
|
+
a.entityType.localeCompare(b.entityType)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function createWikiIngestPublicationPolicy(options = {}) {
|
|
268
|
+
const safetyResults = normalizeSafetyResults(options.safetyResults);
|
|
269
|
+
const externalWrite = Boolean(options.externalWrite);
|
|
270
|
+
const entityTypes = summarizeEntityTypes(safetyResults);
|
|
271
|
+
const findingCount = entityTypes.reduce((sum, item) => sum + item.count, 0);
|
|
272
|
+
const reviewRequired =
|
|
273
|
+
externalWrite ||
|
|
274
|
+
findingCount > 0 ||
|
|
275
|
+
safetyResults.some(result => Boolean(result?.reviewRequired));
|
|
276
|
+
const reasons = [
|
|
277
|
+
...(externalWrite ? ["external-write source"] : []),
|
|
278
|
+
...(findingCount > 0 ? ["redacted or sensitive findings"] : []),
|
|
279
|
+
...(!externalWrite &&
|
|
280
|
+
findingCount === 0 &&
|
|
281
|
+
safetyResults.some(result => Boolean(result?.reviewRequired))
|
|
282
|
+
? ["source safety review requested"]
|
|
283
|
+
: []),
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
const findingLines =
|
|
287
|
+
entityTypes.length > 0
|
|
288
|
+
? entityTypes.map(
|
|
289
|
+
item =>
|
|
290
|
+
`- ${item.entityType}: ${item.count} ${item.count === 1 ? "finding" : "findings"} (${item.confidence})`
|
|
291
|
+
)
|
|
292
|
+
: ["- none"];
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
autoMergeAllowed: !reviewRequired,
|
|
296
|
+
reviewRequired,
|
|
297
|
+
reasons,
|
|
298
|
+
findingCount,
|
|
299
|
+
entityTypes,
|
|
300
|
+
prSummaryMarkdown: [
|
|
301
|
+
"## Wiki Safety Review",
|
|
302
|
+
"",
|
|
303
|
+
`Auto-merge: ${reviewRequired ? "disabled" : "allowed"}`,
|
|
304
|
+
`Human review required: ${reviewRequired ? "yes" : "no"}`,
|
|
305
|
+
`Reason: ${reasons.length > 0 ? reasons.join("; ") : "no redactions or sensitive findings"}`,
|
|
306
|
+
"",
|
|
307
|
+
"Finding summary:",
|
|
308
|
+
...findingLines,
|
|
309
|
+
"",
|
|
310
|
+
"Raw sensitive values are intentionally omitted from this summary.",
|
|
311
|
+
].join("\n"),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
239
315
|
export function isWikiSafetyScanTarget(filePath, options = {}) {
|
|
240
316
|
const pathModule = options.pathModule;
|
|
241
317
|
if (!pathModule) {
|
|
@@ -50,11 +50,14 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
50
50
|
7. **Commit/PR**: commit only the ingestion changes per `config.git` policy. If the ingest started on
|
|
51
51
|
the default branch, create a dedicated ingestion branch first — never commit ingestion straight to
|
|
52
52
|
the default. Push the branch and **open a PR targeting the default remote branch** (via the host's
|
|
53
|
-
PR mechanism — e.g. `gh pr create --base <default>`)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
PR mechanism — e.g. `gh pr create --base <default>`). Before enabling auto-merge, feed the safety
|
|
54
|
+
scan output into `createWikiIngestPublicationPolicy` from `scripts/wiki-safety.mjs`. Enable
|
|
55
|
+
auto-merge only when `autoMergeAllowed` is true. `external-write` runs, redacted runs, and any run
|
|
56
|
+
with sensitive findings are the exception — open the PR **without** auto-merge so a human reviews
|
|
57
|
+
them before it lands. The PR summary must include the policy's safe review summary: counts and
|
|
58
|
+
entity types only, never raw values, ranges, tokens, source snippets, or scanner output. If
|
|
59
|
+
auto-merge cannot be enabled (the host doesn't support it, branch protection forbids it, or the
|
|
60
|
+
policy requires review), leave the PR open and note that a human must merge.
|
|
58
61
|
|
|
59
62
|
## Rules
|
|
60
63
|
- **Bookend every ingest with git hygiene:** sync the branch with the default remote branch *before*
|
|
@@ -71,6 +74,10 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
71
74
|
select `--scanner gitleaks` for local parity with Gitleaks, and may use
|
|
72
75
|
`--scanner trufflehog` as a stricter optional verification pass when installed;
|
|
73
76
|
if a selected scanner is unavailable, keep the ingest blocked for review.
|
|
77
|
+
- Redaction and review policy is centralized in `scripts/wiki-safety.mjs`. Treat
|
|
78
|
+
`reviewRequired` or any summarized finding as a hard auto-merge stop. Use the
|
|
79
|
+
generated PR summary text as-is or preserve its shape so reviewers see entity
|
|
80
|
+
types and counts without raw sensitive values.
|
|
74
81
|
- Connector execution and the connector contract are detailed in the connector skills (M2+); this
|
|
75
82
|
router defines and enforces the ordering and side-effect rules above.
|
|
76
83
|
|
|
@@ -236,6 +236,82 @@ export function serializeWikiSafetyFindings(result) {
|
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
function normalizeSafetyResults(results) {
|
|
240
|
+
if (Array.isArray(results)) return results;
|
|
241
|
+
if (!results) return [];
|
|
242
|
+
return [results];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function summarizeEntityTypes(results) {
|
|
246
|
+
const byType = new Map();
|
|
247
|
+
for (const result of results) {
|
|
248
|
+
for (const finding of result?.findings ?? []) {
|
|
249
|
+
const entityType = finding?.entityType ?? "unknown";
|
|
250
|
+
const current = byType.get(entityType) ?? {
|
|
251
|
+
entityType,
|
|
252
|
+
confidence: finding?.confidence ?? "unknown",
|
|
253
|
+
count: 0,
|
|
254
|
+
};
|
|
255
|
+
current.count += Number.isFinite(finding?.count) ? finding.count : 1;
|
|
256
|
+
if (current.confidence !== "high" && finding?.confidence === "high") {
|
|
257
|
+
current.confidence = "high";
|
|
258
|
+
}
|
|
259
|
+
byType.set(entityType, current);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return [...byType.values()].sort((a, b) =>
|
|
263
|
+
a.entityType.localeCompare(b.entityType)
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function createWikiIngestPublicationPolicy(options = {}) {
|
|
268
|
+
const safetyResults = normalizeSafetyResults(options.safetyResults);
|
|
269
|
+
const externalWrite = Boolean(options.externalWrite);
|
|
270
|
+
const entityTypes = summarizeEntityTypes(safetyResults);
|
|
271
|
+
const findingCount = entityTypes.reduce((sum, item) => sum + item.count, 0);
|
|
272
|
+
const reviewRequired =
|
|
273
|
+
externalWrite ||
|
|
274
|
+
findingCount > 0 ||
|
|
275
|
+
safetyResults.some(result => Boolean(result?.reviewRequired));
|
|
276
|
+
const reasons = [
|
|
277
|
+
...(externalWrite ? ["external-write source"] : []),
|
|
278
|
+
...(findingCount > 0 ? ["redacted or sensitive findings"] : []),
|
|
279
|
+
...(!externalWrite &&
|
|
280
|
+
findingCount === 0 &&
|
|
281
|
+
safetyResults.some(result => Boolean(result?.reviewRequired))
|
|
282
|
+
? ["source safety review requested"]
|
|
283
|
+
: []),
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
const findingLines =
|
|
287
|
+
entityTypes.length > 0
|
|
288
|
+
? entityTypes.map(
|
|
289
|
+
item =>
|
|
290
|
+
`- ${item.entityType}: ${item.count} ${item.count === 1 ? "finding" : "findings"} (${item.confidence})`
|
|
291
|
+
)
|
|
292
|
+
: ["- none"];
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
autoMergeAllowed: !reviewRequired,
|
|
296
|
+
reviewRequired,
|
|
297
|
+
reasons,
|
|
298
|
+
findingCount,
|
|
299
|
+
entityTypes,
|
|
300
|
+
prSummaryMarkdown: [
|
|
301
|
+
"## Wiki Safety Review",
|
|
302
|
+
"",
|
|
303
|
+
`Auto-merge: ${reviewRequired ? "disabled" : "allowed"}`,
|
|
304
|
+
`Human review required: ${reviewRequired ? "yes" : "no"}`,
|
|
305
|
+
`Reason: ${reasons.length > 0 ? reasons.join("; ") : "no redactions or sensitive findings"}`,
|
|
306
|
+
"",
|
|
307
|
+
"Finding summary:",
|
|
308
|
+
...findingLines,
|
|
309
|
+
"",
|
|
310
|
+
"Raw sensitive values are intentionally omitted from this summary.",
|
|
311
|
+
].join("\n"),
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
239
315
|
export function isWikiSafetyScanTarget(filePath, options = {}) {
|
|
240
316
|
const pathModule = options.pathModule;
|
|
241
317
|
if (!pathModule) {
|
|
@@ -50,11 +50,14 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
50
50
|
7. **Commit/PR**: commit only the ingestion changes per `config.git` policy. If the ingest started on
|
|
51
51
|
the default branch, create a dedicated ingestion branch first — never commit ingestion straight to
|
|
52
52
|
the default. Push the branch and **open a PR targeting the default remote branch** (via the host's
|
|
53
|
-
PR mechanism — e.g. `gh pr create --base <default>`)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
PR mechanism — e.g. `gh pr create --base <default>`). Before enabling auto-merge, feed the safety
|
|
54
|
+
scan output into `createWikiIngestPublicationPolicy` from `scripts/wiki-safety.mjs`. Enable
|
|
55
|
+
auto-merge only when `autoMergeAllowed` is true. `external-write` runs, redacted runs, and any run
|
|
56
|
+
with sensitive findings are the exception — open the PR **without** auto-merge so a human reviews
|
|
57
|
+
them before it lands. The PR summary must include the policy's safe review summary: counts and
|
|
58
|
+
entity types only, never raw values, ranges, tokens, source snippets, or scanner output. If
|
|
59
|
+
auto-merge cannot be enabled (the host doesn't support it, branch protection forbids it, or the
|
|
60
|
+
policy requires review), leave the PR open and note that a human must merge.
|
|
58
61
|
|
|
59
62
|
## Rules
|
|
60
63
|
- **Bookend every ingest with git hygiene:** sync the branch with the default remote branch *before*
|
|
@@ -71,6 +74,10 @@ writes nothing). The point is to ingest on top of fresh state, never stale state
|
|
|
71
74
|
select `--scanner gitleaks` for local parity with Gitleaks, and may use
|
|
72
75
|
`--scanner trufflehog` as a stricter optional verification pass when installed;
|
|
73
76
|
if a selected scanner is unavailable, keep the ingest blocked for review.
|
|
77
|
+
- Redaction and review policy is centralized in `scripts/wiki-safety.mjs`. Treat
|
|
78
|
+
`reviewRequired` or any summarized finding as a hard auto-merge stop. Use the
|
|
79
|
+
generated PR summary text as-is or preserve its shape so reviewers see entity
|
|
80
|
+
types and counts without raw sensitive values.
|
|
74
81
|
- Connector execution and the connector contract are detailed in the connector skills (M2+); this
|
|
75
82
|
router defines and enforces the ordering and side-effect rules above.
|
|
76
83
|
|