knosky 0.6.3 → 0.7.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 +149 -93
- package/CREDITS.md +14 -14
- package/LICENSE.md +76 -76
- package/LIMITATIONS.md +33 -23
- package/PRIVACY.md +30 -30
- package/README.md +170 -117
- package/SECURITY.md +78 -46
- package/action/post-comment.mjs +94 -89
- package/action.yml +62 -62
- package/bin/knosky.mjs +279 -105
- package/core/CONTRACT.md +70 -70
- package/core/append-only-checkpoint.mjs +215 -0
- package/core/audit-writer.mjs +317 -0
- package/core/benchmark-results.mjs +225 -225
- package/core/bundle.mjs +178 -178
- package/core/churn.mjs +23 -23
- package/core/ci.mjs +268 -268
- package/core/comparison.mjs +189 -189
- package/core/config.mjs +189 -189
- package/core/constants.mjs +13 -13
- package/core/contract.mjs +123 -123
- package/core/cross-repo.mjs +111 -111
- package/core/decision-codes.mjs +92 -0
- package/core/destination.mjs +161 -161
- package/core/district-classification.mjs +111 -0
- package/core/doctor-scorecard.mjs +369 -0
- package/core/domain-store.mjs +347 -0
- package/core/edges.mjs +43 -43
- package/core/escalate.mjs +68 -68
- package/core/freshness.mjs +198 -194
- package/core/fs-indexer.mjs +218 -218
- package/core/key-store.mjs +348 -348
- package/core/layout.mjs +46 -46
- package/core/ledger.mjs +176 -141
- package/core/local-ipc-identity.mjs +500 -0
- package/core/lod.mjs +155 -155
- package/core/mode-b.mjs +410 -0
- package/core/multi-model-benchmark.mjs +405 -405
- package/core/net-lockdown.mjs +421 -0
- package/core/onboarding.mjs +223 -223
- package/core/operator-auth.mjs +317 -0
- package/core/overlays.mjs +45 -45
- package/core/policy-lattice.mjs +142 -0
- package/core/pr-comment.mjs +198 -198
- package/core/protocol-spec.mjs +460 -460
- package/core/provenance.mjs +320 -0
- package/core/retrieve.mjs +63 -63
- package/core/route.mjs +304 -304
- package/core/schema.mjs +275 -275
- package/core/signing-tiers.mjs +1265 -0
- package/core/swarm-bench.mjs +106 -0
- package/core/swarm-coordinator.mjs +867 -0
- package/core/trust-root-rekey.mjs +410 -0
- package/mcp/server.mjs +264 -108
- package/package.json +56 -46
- package/renderer/art/kenney/buildingTiles_sheet.xml +130 -130
- package/renderer/art/kenney/cityDetails_sheet.xml +12 -12
- package/renderer/art/kenney/landscapeTiles_sheet.xml +129 -129
- package/renderer/art/kenney/sheet_allCars.xml +545 -545
- package/renderer/build-rich.mjs +43 -43
- package/renderer/city.template.html +808 -808
- package/ssot/decision-codes.json +133 -0
- package/ssot/ladder-l0-l3.md +232 -0
- package/ssot/tool-menu.json +130 -0
package/core/pr-comment.mjs
CHANGED
|
@@ -1,198 +1,198 @@
|
|
|
1
|
-
// KnoSky PR-comment renderer (KSV2-CI2) — claims-disciplined public Markdown.
|
|
2
|
-
// Consumes CI1 artifacts (routeJson + safetyJson) and renders the polished
|
|
3
|
-
// PR-GPS comment that knosky ci posts on a pull request.
|
|
4
|
-
//
|
|
5
|
-
// CLAIMS DISCIPLINE — BINDING (D-162 §8):
|
|
6
|
-
// USE: "advisory PR-GPS report" / "advisory route you can ignore"
|
|
7
|
-
// USE: "route cache / freshness"
|
|
8
|
-
// USE: "reads metadata only / never uploads your code"
|
|
9
|
-
// USE: "network-silent (verify with --verify-airgap)"
|
|
10
|
-
// USE: "open protocol / reference implementation"
|
|
11
|
-
// NEVER: CI gate / blocks the build / gates the build
|
|
12
|
-
// NEVER: learns / learns your repo
|
|
13
|
-
// NEVER: understands your code
|
|
14
|
-
// NEVER: zero data risk
|
|
15
|
-
// NEVER: official standard
|
|
16
|
-
// NEVER: air-gap guarantee
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Internal helpers
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Format a confidence fraction [0..1] as a percent string, or 'n/a'.
|
|
24
|
-
* @param {unknown} val
|
|
25
|
-
* @returns {string}
|
|
26
|
-
*/
|
|
27
|
-
function fmtConfidence(val) {
|
|
28
|
-
if (typeof val === 'number' && isFinite(val)) {
|
|
29
|
-
return Math.round(val * 100) + '%';
|
|
30
|
-
}
|
|
31
|
-
return 'n/a';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Extract path + reason from a waypoint entry (string | { path, reason }).
|
|
36
|
-
* @param {unknown} wp
|
|
37
|
-
* @returns {{ path: string, reason: string }}
|
|
38
|
-
*/
|
|
39
|
-
function parsWaypoint(wp) {
|
|
40
|
-
if (typeof wp === 'string') return { path: wp, reason: '' };
|
|
41
|
-
const path = (wp && typeof wp.path === 'string') ? wp.path : '';
|
|
42
|
-
const reason = (wp && typeof wp.reason === 'string') ? wp.reason : '';
|
|
43
|
-
return { path, reason };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
// renderPrComment — main export
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Render the KnoSky PR-GPS comment as a Markdown string.
|
|
52
|
-
*
|
|
53
|
-
* This is a DRAFT — the final public wording is Paul's critical gate.
|
|
54
|
-
*
|
|
55
|
-
* @param {object} opts
|
|
56
|
-
* @param {object} opts.routeJson CI1 route artifact ({ routes, base, head, advisory, … })
|
|
57
|
-
* @param {object} opts.safetyJson CI1 safety artifact ({ secrets_found, absolute_paths, … })
|
|
58
|
-
* @returns {string} Markdown comment body.
|
|
59
|
-
*/
|
|
60
|
-
export function renderPrComment({ routeJson = {}, safetyJson = {} } = {}) {
|
|
61
|
-
const lines = [];
|
|
62
|
-
|
|
63
|
-
// -------------------------------------------------------------------------
|
|
64
|
-
// Header
|
|
65
|
-
// -------------------------------------------------------------------------
|
|
66
|
-
lines.push('## 🧭 KnoSky PR-GPS');
|
|
67
|
-
lines.push('');
|
|
68
|
-
lines.push(
|
|
69
|
-
'> **DRAFT — advisory PR-GPS report.** ' +
|
|
70
|
-
'This is an advisory route you can ignore — it never gates or enforces a build outcome.',
|
|
71
|
-
);
|
|
72
|
-
lines.push('');
|
|
73
|
-
|
|
74
|
-
// -------------------------------------------------------------------------
|
|
75
|
-
// Per-file route sections
|
|
76
|
-
// -------------------------------------------------------------------------
|
|
77
|
-
const routes = Array.isArray(routeJson.routes) ? routeJson.routes : [];
|
|
78
|
-
|
|
79
|
-
if (routes.length === 0) {
|
|
80
|
-
lines.push('_No changed files detected in this PR — nothing to navigate._');
|
|
81
|
-
lines.push('');
|
|
82
|
-
} else {
|
|
83
|
-
for (const { file, route: routeDoc } of routes) {
|
|
84
|
-
lines.push(`### \`${file}\``);
|
|
85
|
-
lines.push('');
|
|
86
|
-
|
|
87
|
-
// Top ~5 waypoints
|
|
88
|
-
const waypoints = (Array.isArray(routeDoc && routeDoc.route) ? routeDoc.route : []).slice(0, 5);
|
|
89
|
-
if (waypoints.length === 0) {
|
|
90
|
-
lines.push('_No route waypoints found in the route cache for this file._');
|
|
91
|
-
} else {
|
|
92
|
-
lines.push('**Advisory route waypoints** _(top ' + waypoints.length + ', from route cache):_');
|
|
93
|
-
lines.push('');
|
|
94
|
-
for (const wp of waypoints) {
|
|
95
|
-
const { path, reason } = parsWaypoint(wp);
|
|
96
|
-
lines.push(`- \`${path}\`${reason ? ' — ' + reason : ''}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
lines.push('');
|
|
100
|
-
|
|
101
|
-
// Confidence
|
|
102
|
-
const conf = fmtConfidence(routeDoc && routeDoc.confidence);
|
|
103
|
-
lines.push(`**Confidence:** ${conf}`);
|
|
104
|
-
lines.push('');
|
|
105
|
-
|
|
106
|
-
// Freshness / coverage caveats
|
|
107
|
-
const caveats = Array.isArray(routeDoc && routeDoc.caveats) ? routeDoc.caveats : [];
|
|
108
|
-
const freshnessOrCoverage = caveats.filter(c =>
|
|
109
|
-
/recently changed|stale|freshness|coverage/i.test(c),
|
|
110
|
-
);
|
|
111
|
-
if (freshnessOrCoverage.length > 0) {
|
|
112
|
-
lines.push('**Route freshness / coverage caveats:**');
|
|
113
|
-
lines.push('');
|
|
114
|
-
for (const c of freshnessOrCoverage) lines.push(`- ${c}`);
|
|
115
|
-
lines.push('');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Related tests
|
|
119
|
-
const tests = Array.isArray(routeDoc && routeDoc.tests) ? routeDoc.tests : [];
|
|
120
|
-
if (tests.length > 0) {
|
|
121
|
-
lines.push('**Related tests:**');
|
|
122
|
-
lines.push('');
|
|
123
|
-
for (const t of tests) {
|
|
124
|
-
const p = typeof t === 'string' ? t : (t && t.path);
|
|
125
|
-
if (p) lines.push(`- \`${p}\``);
|
|
126
|
-
}
|
|
127
|
-
lines.push('');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Related docs
|
|
131
|
-
const docs = Array.isArray(routeDoc && routeDoc.docs) ? routeDoc.docs : [];
|
|
132
|
-
if (docs.length > 0) {
|
|
133
|
-
lines.push('**Related docs:**');
|
|
134
|
-
lines.push('');
|
|
135
|
-
for (const d of docs) {
|
|
136
|
-
const p = typeof d === 'string' ? d : (d && d.path);
|
|
137
|
-
if (p) lines.push(`- \`${p}\``);
|
|
138
|
-
}
|
|
139
|
-
lines.push('');
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// -------------------------------------------------------------------------
|
|
145
|
-
// Suggested reviewer / agent prompt block
|
|
146
|
-
// -------------------------------------------------------------------------
|
|
147
|
-
lines.push('---');
|
|
148
|
-
lines.push('');
|
|
149
|
-
lines.push('### 💬 Suggested reviewer / agent prompt');
|
|
150
|
-
lines.push('');
|
|
151
|
-
|
|
152
|
-
if (routes.length > 0) {
|
|
153
|
-
// Collect top waypoints across all changed files (unique, capped at 5)
|
|
154
|
-
const seen = new Set();
|
|
155
|
-
const topPaths = [];
|
|
156
|
-
for (const { route: routeDoc } of routes) {
|
|
157
|
-
const wps = Array.isArray(routeDoc && routeDoc.route) ? routeDoc.route : [];
|
|
158
|
-
for (const wp of wps.slice(0, 3)) {
|
|
159
|
-
const { path } = parsWaypoint(wp);
|
|
160
|
-
if (path && !seen.has(path)) {
|
|
161
|
-
seen.add(path);
|
|
162
|
-
topPaths.push(path);
|
|
163
|
-
if (topPaths.length >= 5) break;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (topPaths.length >= 5) break;
|
|
167
|
-
}
|
|
168
|
-
const waypointList = topPaths.map(p => `\`${p}\``).join(', ');
|
|
169
|
-
lines.push(
|
|
170
|
-
`> Start your review at: ${waypointList || '_see routes above_'}. ` +
|
|
171
|
-
'Advisory only — verify before acting.',
|
|
172
|
-
);
|
|
173
|
-
} else {
|
|
174
|
-
lines.push(
|
|
175
|
-
'> No route waypoints available for this PR. ' +
|
|
176
|
-
'Advisory only — verify before acting.',
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
lines.push('');
|
|
181
|
-
|
|
182
|
-
// -------------------------------------------------------------------------
|
|
183
|
-
// Footer — safety / privacy
|
|
184
|
-
// -------------------------------------------------------------------------
|
|
185
|
-
lines.push('---');
|
|
186
|
-
lines.push('');
|
|
187
|
-
const secretsFound = (safetyJson && typeof safetyJson.secrets_found === 'number')
|
|
188
|
-
? safetyJson.secrets_found
|
|
189
|
-
: 0;
|
|
190
|
-
lines.push(
|
|
191
|
-
'_KnoSky reads metadata only — it never uploads your code. ' +
|
|
192
|
-
`Secrets scan: **${secretsFound}** potential secret(s) found in emitted artifacts. ` +
|
|
193
|
-
'network-silent (verify with --verify-airgap). ' +
|
|
194
|
-
'open protocol / reference implementation._',
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
return lines.join('\n');
|
|
198
|
-
}
|
|
1
|
+
// KnoSky PR-comment renderer (KSV2-CI2) — claims-disciplined public Markdown.
|
|
2
|
+
// Consumes CI1 artifacts (routeJson + safetyJson) and renders the polished
|
|
3
|
+
// PR-GPS comment that knosky ci posts on a pull request.
|
|
4
|
+
//
|
|
5
|
+
// CLAIMS DISCIPLINE — BINDING (D-162 §8):
|
|
6
|
+
// USE: "advisory PR-GPS report" / "advisory route you can ignore"
|
|
7
|
+
// USE: "route cache / freshness"
|
|
8
|
+
// USE: "reads metadata only / never uploads your code"
|
|
9
|
+
// USE: "network-silent (verify with --verify-airgap)"
|
|
10
|
+
// USE: "open protocol / reference implementation"
|
|
11
|
+
// NEVER: CI gate / blocks the build / gates the build
|
|
12
|
+
// NEVER: learns / learns your repo
|
|
13
|
+
// NEVER: understands your code
|
|
14
|
+
// NEVER: zero data risk
|
|
15
|
+
// NEVER: official standard
|
|
16
|
+
// NEVER: air-gap guarantee
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Internal helpers
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Format a confidence fraction [0..1] as a percent string, or 'n/a'.
|
|
24
|
+
* @param {unknown} val
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
function fmtConfidence(val) {
|
|
28
|
+
if (typeof val === 'number' && isFinite(val)) {
|
|
29
|
+
return Math.round(val * 100) + '%';
|
|
30
|
+
}
|
|
31
|
+
return 'n/a';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract path + reason from a waypoint entry (string | { path, reason }).
|
|
36
|
+
* @param {unknown} wp
|
|
37
|
+
* @returns {{ path: string, reason: string }}
|
|
38
|
+
*/
|
|
39
|
+
function parsWaypoint(wp) {
|
|
40
|
+
if (typeof wp === 'string') return { path: wp, reason: '' };
|
|
41
|
+
const path = (wp && typeof wp.path === 'string') ? wp.path : '';
|
|
42
|
+
const reason = (wp && typeof wp.reason === 'string') ? wp.reason : '';
|
|
43
|
+
return { path, reason };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// renderPrComment — main export
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Render the KnoSky PR-GPS comment as a Markdown string.
|
|
52
|
+
*
|
|
53
|
+
* This is a DRAFT — the final public wording is Paul's critical gate.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} opts
|
|
56
|
+
* @param {object} opts.routeJson CI1 route artifact ({ routes, base, head, advisory, … })
|
|
57
|
+
* @param {object} opts.safetyJson CI1 safety artifact ({ secrets_found, absolute_paths, … })
|
|
58
|
+
* @returns {string} Markdown comment body.
|
|
59
|
+
*/
|
|
60
|
+
export function renderPrComment({ routeJson = {}, safetyJson = {} } = {}) {
|
|
61
|
+
const lines = [];
|
|
62
|
+
|
|
63
|
+
// -------------------------------------------------------------------------
|
|
64
|
+
// Header
|
|
65
|
+
// -------------------------------------------------------------------------
|
|
66
|
+
lines.push('## 🧭 KnoSky PR-GPS');
|
|
67
|
+
lines.push('');
|
|
68
|
+
lines.push(
|
|
69
|
+
'> **DRAFT — advisory PR-GPS report.** ' +
|
|
70
|
+
'This is an advisory route you can ignore — it never gates or enforces a build outcome.',
|
|
71
|
+
);
|
|
72
|
+
lines.push('');
|
|
73
|
+
|
|
74
|
+
// -------------------------------------------------------------------------
|
|
75
|
+
// Per-file route sections
|
|
76
|
+
// -------------------------------------------------------------------------
|
|
77
|
+
const routes = Array.isArray(routeJson.routes) ? routeJson.routes : [];
|
|
78
|
+
|
|
79
|
+
if (routes.length === 0) {
|
|
80
|
+
lines.push('_No changed files detected in this PR — nothing to navigate._');
|
|
81
|
+
lines.push('');
|
|
82
|
+
} else {
|
|
83
|
+
for (const { file, route: routeDoc } of routes) {
|
|
84
|
+
lines.push(`### \`${file}\``);
|
|
85
|
+
lines.push('');
|
|
86
|
+
|
|
87
|
+
// Top ~5 waypoints
|
|
88
|
+
const waypoints = (Array.isArray(routeDoc && routeDoc.route) ? routeDoc.route : []).slice(0, 5);
|
|
89
|
+
if (waypoints.length === 0) {
|
|
90
|
+
lines.push('_No route waypoints found in the route cache for this file._');
|
|
91
|
+
} else {
|
|
92
|
+
lines.push('**Advisory route waypoints** _(top ' + waypoints.length + ', from route cache):_');
|
|
93
|
+
lines.push('');
|
|
94
|
+
for (const wp of waypoints) {
|
|
95
|
+
const { path, reason } = parsWaypoint(wp);
|
|
96
|
+
lines.push(`- \`${path}\`${reason ? ' — ' + reason : ''}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
lines.push('');
|
|
100
|
+
|
|
101
|
+
// Confidence
|
|
102
|
+
const conf = fmtConfidence(routeDoc && routeDoc.confidence);
|
|
103
|
+
lines.push(`**Confidence:** ${conf}`);
|
|
104
|
+
lines.push('');
|
|
105
|
+
|
|
106
|
+
// Freshness / coverage caveats
|
|
107
|
+
const caveats = Array.isArray(routeDoc && routeDoc.caveats) ? routeDoc.caveats : [];
|
|
108
|
+
const freshnessOrCoverage = caveats.filter(c =>
|
|
109
|
+
/recently changed|stale|freshness|coverage/i.test(c),
|
|
110
|
+
);
|
|
111
|
+
if (freshnessOrCoverage.length > 0) {
|
|
112
|
+
lines.push('**Route freshness / coverage caveats:**');
|
|
113
|
+
lines.push('');
|
|
114
|
+
for (const c of freshnessOrCoverage) lines.push(`- ${c}`);
|
|
115
|
+
lines.push('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Related tests
|
|
119
|
+
const tests = Array.isArray(routeDoc && routeDoc.tests) ? routeDoc.tests : [];
|
|
120
|
+
if (tests.length > 0) {
|
|
121
|
+
lines.push('**Related tests:**');
|
|
122
|
+
lines.push('');
|
|
123
|
+
for (const t of tests) {
|
|
124
|
+
const p = typeof t === 'string' ? t : (t && t.path);
|
|
125
|
+
if (p) lines.push(`- \`${p}\``);
|
|
126
|
+
}
|
|
127
|
+
lines.push('');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Related docs
|
|
131
|
+
const docs = Array.isArray(routeDoc && routeDoc.docs) ? routeDoc.docs : [];
|
|
132
|
+
if (docs.length > 0) {
|
|
133
|
+
lines.push('**Related docs:**');
|
|
134
|
+
lines.push('');
|
|
135
|
+
for (const d of docs) {
|
|
136
|
+
const p = typeof d === 'string' ? d : (d && d.path);
|
|
137
|
+
if (p) lines.push(`- \`${p}\``);
|
|
138
|
+
}
|
|
139
|
+
lines.push('');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
// Suggested reviewer / agent prompt block
|
|
146
|
+
// -------------------------------------------------------------------------
|
|
147
|
+
lines.push('---');
|
|
148
|
+
lines.push('');
|
|
149
|
+
lines.push('### 💬 Suggested reviewer / agent prompt');
|
|
150
|
+
lines.push('');
|
|
151
|
+
|
|
152
|
+
if (routes.length > 0) {
|
|
153
|
+
// Collect top waypoints across all changed files (unique, capped at 5)
|
|
154
|
+
const seen = new Set();
|
|
155
|
+
const topPaths = [];
|
|
156
|
+
for (const { route: routeDoc } of routes) {
|
|
157
|
+
const wps = Array.isArray(routeDoc && routeDoc.route) ? routeDoc.route : [];
|
|
158
|
+
for (const wp of wps.slice(0, 3)) {
|
|
159
|
+
const { path } = parsWaypoint(wp);
|
|
160
|
+
if (path && !seen.has(path)) {
|
|
161
|
+
seen.add(path);
|
|
162
|
+
topPaths.push(path);
|
|
163
|
+
if (topPaths.length >= 5) break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (topPaths.length >= 5) break;
|
|
167
|
+
}
|
|
168
|
+
const waypointList = topPaths.map(p => `\`${p}\``).join(', ');
|
|
169
|
+
lines.push(
|
|
170
|
+
`> Start your review at: ${waypointList || '_see routes above_'}. ` +
|
|
171
|
+
'Advisory only — verify before acting.',
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
lines.push(
|
|
175
|
+
'> No route waypoints available for this PR. ' +
|
|
176
|
+
'Advisory only — verify before acting.',
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
lines.push('');
|
|
181
|
+
|
|
182
|
+
// -------------------------------------------------------------------------
|
|
183
|
+
// Footer — safety / privacy
|
|
184
|
+
// -------------------------------------------------------------------------
|
|
185
|
+
lines.push('---');
|
|
186
|
+
lines.push('');
|
|
187
|
+
const secretsFound = (safetyJson && typeof safetyJson.secrets_found === 'number')
|
|
188
|
+
? safetyJson.secrets_found
|
|
189
|
+
: 0;
|
|
190
|
+
lines.push(
|
|
191
|
+
'_KnoSky reads metadata only — it never uploads your code. ' +
|
|
192
|
+
`Secrets scan: **${secretsFound}** potential secret(s) found in emitted artifacts. ` +
|
|
193
|
+
'network-silent (verify with --verify-airgap). ' +
|
|
194
|
+
'open protocol / reference implementation._',
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
return lines.join('\n');
|
|
198
|
+
}
|