sentinelayer-cli 0.4.5 → 0.6.2
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/README.md +996 -998
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +63 -63
- package/src/agents/jules/config/definition.js +160 -209
- package/src/agents/jules/config/system-prompt.js +182 -175
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +17 -377
- package/src/agents/jules/loop.js +450 -367
- package/src/agents/jules/pulse.js +10 -327
- package/src/agents/jules/stream.js +186 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +309 -308
- package/src/agents/jules/tools/aidenid-email.js +189 -0
- package/src/agents/jules/tools/auth-audit.js +1691 -557
- package/src/agents/jules/tools/dispatch.js +335 -327
- package/src/agents/jules/tools/file-edit.js +2 -180
- package/src/agents/jules/tools/file-read.js +2 -100
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +2 -168
- package/src/agents/jules/tools/grep.js +2 -228
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +2 -161
- package/src/agents/jules/tools/runtime-audit.js +507 -503
- package/src/agents/jules/tools/shell.js +2 -383
- package/src/agents/jules/tools/url-policy.js +100 -100
- package/src/agents/persona-visuals.js +61 -0
- package/src/agents/shared-tools/dispatch-core.js +315 -0
- package/src/agents/shared-tools/file-edit.js +180 -0
- package/src/agents/shared-tools/file-read.js +100 -0
- package/src/agents/shared-tools/glob.js +168 -0
- package/src/agents/shared-tools/grep.js +228 -0
- package/src/agents/shared-tools/index.js +46 -0
- package/src/agents/shared-tools/path-guards.js +161 -0
- package/src/agents/shared-tools/shell.js +383 -0
- package/src/ai/aidenid.js +1009 -972
- package/src/ai/client.js +553 -508
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/proxy.js +137 -0
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +371 -126
- package/src/auth/http.js +611 -270
- package/src/auth/service.js +1106 -891
- package/src/auth/session-store.js +813 -359
- package/src/cli.js +252 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1338
- package/src/commands/ai/provision-governance.js +1272 -1272
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1166 -1166
- package/src/commands/auth.js +419 -375
- package/src/commands/chat.js +191 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +10 -10
- package/src/commands/mcp.js +461 -461
- package/src/commands/omargate.js +29 -21
- package/src/commands/persona.js +20 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +872 -866
- package/src/commands/spec.js +716 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +511 -510
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tracker.js +171 -171
- package/src/daemon/artifact-lineage.js +534 -534
- package/src/daemon/assignment-ledger.js +770 -770
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +626 -626
- package/src/daemon/fix-cycle.js +377 -0
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/jira-lifecycle.js +632 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/pulse.js +327 -0
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/watchdog.js +971 -971
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +918 -918
- package/src/interactive/index.js +97 -95
- package/src/legacy-cli.js +2994 -2592
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +118 -118
- package/src/review/ai-review.js +679 -669
- package/src/review/local-review.js +1305 -1295
- package/src/review/omargate-interactive.js +68 -0
- package/src/review/omargate-orchestrator.js +300 -0
- package/src/review/persona-prompts.js +296 -0
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/scan-modes.js +42 -0
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -67
- package/src/scaffold/templates.js +150 -150
- package/src/scan/generator.js +418 -418
- package/src/scan/gh-secrets.js +107 -107
- package/src/spec/generator.js +519 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +576 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +234 -118
- package/src/telemetry/sync.js +203 -199
- package/src/ui/command-hints.js +13 -0
- package/src/ui/markdown.js +220 -220
|
@@ -1,165 +1,165 @@
|
|
|
1
|
-
function normalizeString(value) {
|
|
2
|
-
return String(value || "").trim();
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function toPosixPath(value) {
|
|
6
|
-
return String(value || "").replace(/\\/g, "/");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function summarizeSeverity(findings = []) {
|
|
10
|
-
const summary = { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
11
|
-
for (const finding of findings) {
|
|
12
|
-
const severity = normalizeString(finding.severity).toUpperCase();
|
|
13
|
-
if (Object.prototype.hasOwnProperty.call(summary, severity)) {
|
|
14
|
-
summary[severity] += 1;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
summary.blocking = summary.P0 > 0 || summary.P1 > 0;
|
|
18
|
-
return summary;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function buildDocumentationInventory(ingest = {}) {
|
|
22
|
-
const indexed = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
23
|
-
const normalized = indexed.map((file) => ({
|
|
24
|
-
path: toPosixPath(file.path),
|
|
25
|
-
loc: Number(file.loc || 0),
|
|
26
|
-
language: file.language,
|
|
27
|
-
}));
|
|
28
|
-
const docFiles = normalized.filter((file) => /(^|\/)(docs?|guides?|adr|readme)/i.test(file.path));
|
|
29
|
-
const codeFiles = normalized.filter(
|
|
30
|
-
(file) => file.loc > 0 && !/(^|\/)(docs?|guides?|adr|readme)/i.test(file.path)
|
|
31
|
-
);
|
|
32
|
-
const docDensity = codeFiles.length > 0 ? docFiles.length / codeFiles.length : 0;
|
|
33
|
-
const undocumentedHotspots = codeFiles
|
|
34
|
-
.filter((file) => file.loc >= 320)
|
|
35
|
-
.filter((file) => {
|
|
36
|
-
const dir = file.path.includes("/") ? file.path.slice(0, file.path.lastIndexOf("/")) : ".";
|
|
37
|
-
return !docFiles.some((doc) => doc.path.startsWith(dir));
|
|
38
|
-
})
|
|
39
|
-
.sort((left, right) => right.loc - left.loc)
|
|
40
|
-
.slice(0, 20)
|
|
41
|
-
.map((file) => ({
|
|
42
|
-
path: file.path,
|
|
43
|
-
loc: file.loc,
|
|
44
|
-
language: file.language,
|
|
45
|
-
severity: file.loc >= 800 ? "P1" : "P2",
|
|
46
|
-
}));
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
docFileCount: docFiles.length,
|
|
50
|
-
codeFileCount: codeFiles.length,
|
|
51
|
-
docDensity,
|
|
52
|
-
undocumentedHotspots,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function buildDocumentationFindings({ findings = [], inventory = {} } = {}) {
|
|
57
|
-
const scoped = Array.isArray(findings) ? findings : [];
|
|
58
|
-
const derived = [];
|
|
59
|
-
for (const finding of scoped) {
|
|
60
|
-
const haystack = `${normalizeString(finding.message)} ${normalizeString(finding.ruleId)} ${normalizeString(
|
|
61
|
-
finding.file
|
|
62
|
-
)}`.toLowerCase();
|
|
63
|
-
if (/doc|documentation|spec|guide|readme|runbook|playbook|adr/.test(haystack)) {
|
|
64
|
-
derived.push(finding);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
for (const hotspot of inventory.undocumentedHotspots || []) {
|
|
69
|
-
const exists = derived.some((finding) => toPosixPath(finding.file) === hotspot.path);
|
|
70
|
-
if (exists) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
derived.push({
|
|
74
|
-
severity: hotspot.severity,
|
|
75
|
-
file: hotspot.path,
|
|
76
|
-
line: 1,
|
|
77
|
-
message: `Large module lacks nearby documentation coverage (${hotspot.loc} LOC).`,
|
|
78
|
-
excerpt: `${hotspot.language || "code"} module at ${hotspot.path}`,
|
|
79
|
-
ruleId: "SL-DOC-001",
|
|
80
|
-
suggestedFix: "Add a runbook/spec section describing responsibilities, dependencies, and failure modes.",
|
|
81
|
-
layer: "documentation",
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return derived.slice(0, 120);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function estimateDocumentationScore(inventory = {}, summary = {}) {
|
|
89
|
-
const densityScore = Math.min(70, Math.round(Number(inventory.docDensity || 0) * 100));
|
|
90
|
-
const penalty = Number(summary.P1 || 0) * 8 + Number(summary.P2 || 0) * 4 + Number(summary.P3 || 0);
|
|
91
|
-
return Math.max(0, Math.min(100, densityScore + 30 - penalty));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function buildRecommendations(inventory = {}, summary = {}) {
|
|
95
|
-
const recommendations = [];
|
|
96
|
-
if (Number(summary.P1 || 0) > 0) {
|
|
97
|
-
recommendations.push("Close P1 documentation gaps for critical modules before release.");
|
|
98
|
-
}
|
|
99
|
-
if (inventory.undocumentedHotspots?.length > 0) {
|
|
100
|
-
recommendations.push("Create runbooks/spec addenda for high-LOC modules lacking nearby docs.");
|
|
101
|
-
}
|
|
102
|
-
if (Number(inventory.docDensity || 0) < 0.2) {
|
|
103
|
-
recommendations.push("Increase docs-to-code density with ADRs and operator-oriented troubleshooting guides.");
|
|
104
|
-
}
|
|
105
|
-
if (recommendations.length === 0) {
|
|
106
|
-
recommendations.push("Documentation posture is stable; maintain drift checks between code and specs.");
|
|
107
|
-
}
|
|
108
|
-
return recommendations;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function runDocumentationSpecialist({ findings = [], ingest = {} } = {}) {
|
|
112
|
-
const inventory = buildDocumentationInventory(ingest);
|
|
113
|
-
const documentationFindings = buildDocumentationFindings({
|
|
114
|
-
findings,
|
|
115
|
-
inventory,
|
|
116
|
-
});
|
|
117
|
-
const summary = summarizeSeverity(documentationFindings);
|
|
118
|
-
const documentationScore = estimateDocumentationScore(inventory, summary);
|
|
119
|
-
const confidence =
|
|
120
|
-
documentationFindings.length > 0 ? Math.max(0.72, 1 - documentationFindings.length * 0.0025) : 0.9;
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
schemaVersion: "1.0.0",
|
|
124
|
-
generatedAt: new Date().toISOString(),
|
|
125
|
-
summary: {
|
|
126
|
-
...summary,
|
|
127
|
-
findingCount: documentationFindings.length,
|
|
128
|
-
documentationScore,
|
|
129
|
-
},
|
|
130
|
-
confidence,
|
|
131
|
-
inventory,
|
|
132
|
-
recommendations: buildRecommendations(inventory, summary),
|
|
133
|
-
findings: documentationFindings,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function renderDocumentationSpecialistMarkdown(report = {}) {
|
|
138
|
-
const hotspots = (report.inventory?.undocumentedHotspots || [])
|
|
139
|
-
.map((item) => `- ${item.path} (${item.loc} LOC, severity=${item.severity})`)
|
|
140
|
-
.join("\n");
|
|
141
|
-
const recommendations = (report.recommendations || []).map((item) => `- ${item}`).join("\n");
|
|
142
|
-
|
|
143
|
-
return `# DOCUMENTATION_AGENT_REPORT
|
|
144
|
-
|
|
145
|
-
Generated: ${report.generatedAt}
|
|
146
|
-
Documentation score: ${report.summary?.documentationScore ?? 0}/100
|
|
147
|
-
Confidence: ${((report.confidence || 0) * 100).toFixed(0)}%
|
|
148
|
-
|
|
149
|
-
Summary:
|
|
150
|
-
- Findings: P0=${report.summary?.P0 ?? 0} P1=${report.summary?.P1 ?? 0} P2=${report.summary?.P2 ?? 0} P3=${report.summary?.P3 ?? 0}
|
|
151
|
-
- Blocking: ${report.summary?.blocking ? "yes" : "no"}
|
|
152
|
-
- Total findings: ${report.summary?.findingCount ?? 0}
|
|
153
|
-
|
|
154
|
-
Documentation inventory:
|
|
155
|
-
- doc files: ${report.inventory?.docFileCount ?? 0}
|
|
156
|
-
- code files: ${report.inventory?.codeFileCount ?? 0}
|
|
157
|
-
- doc density: ${((report.inventory?.docDensity || 0) * 100).toFixed(1)}%
|
|
158
|
-
|
|
159
|
-
Undocumented hotspots:
|
|
160
|
-
${hotspots || "- none"}
|
|
161
|
-
|
|
162
|
-
Recommendations:
|
|
163
|
-
${recommendations || "- none"}
|
|
164
|
-
`;
|
|
165
|
-
}
|
|
1
|
+
function normalizeString(value) {
|
|
2
|
+
return String(value || "").trim();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function toPosixPath(value) {
|
|
6
|
+
return String(value || "").replace(/\\/g, "/");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function summarizeSeverity(findings = []) {
|
|
10
|
+
const summary = { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
11
|
+
for (const finding of findings) {
|
|
12
|
+
const severity = normalizeString(finding.severity).toUpperCase();
|
|
13
|
+
if (Object.prototype.hasOwnProperty.call(summary, severity)) {
|
|
14
|
+
summary[severity] += 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
summary.blocking = summary.P0 > 0 || summary.P1 > 0;
|
|
18
|
+
return summary;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildDocumentationInventory(ingest = {}) {
|
|
22
|
+
const indexed = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
23
|
+
const normalized = indexed.map((file) => ({
|
|
24
|
+
path: toPosixPath(file.path),
|
|
25
|
+
loc: Number(file.loc || 0),
|
|
26
|
+
language: file.language,
|
|
27
|
+
}));
|
|
28
|
+
const docFiles = normalized.filter((file) => /(^|\/)(docs?|guides?|adr|readme)/i.test(file.path));
|
|
29
|
+
const codeFiles = normalized.filter(
|
|
30
|
+
(file) => file.loc > 0 && !/(^|\/)(docs?|guides?|adr|readme)/i.test(file.path)
|
|
31
|
+
);
|
|
32
|
+
const docDensity = codeFiles.length > 0 ? docFiles.length / codeFiles.length : 0;
|
|
33
|
+
const undocumentedHotspots = codeFiles
|
|
34
|
+
.filter((file) => file.loc >= 320)
|
|
35
|
+
.filter((file) => {
|
|
36
|
+
const dir = file.path.includes("/") ? file.path.slice(0, file.path.lastIndexOf("/")) : ".";
|
|
37
|
+
return !docFiles.some((doc) => doc.path.startsWith(dir));
|
|
38
|
+
})
|
|
39
|
+
.sort((left, right) => right.loc - left.loc)
|
|
40
|
+
.slice(0, 20)
|
|
41
|
+
.map((file) => ({
|
|
42
|
+
path: file.path,
|
|
43
|
+
loc: file.loc,
|
|
44
|
+
language: file.language,
|
|
45
|
+
severity: file.loc >= 800 ? "P1" : "P2",
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
docFileCount: docFiles.length,
|
|
50
|
+
codeFileCount: codeFiles.length,
|
|
51
|
+
docDensity,
|
|
52
|
+
undocumentedHotspots,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildDocumentationFindings({ findings = [], inventory = {} } = {}) {
|
|
57
|
+
const scoped = Array.isArray(findings) ? findings : [];
|
|
58
|
+
const derived = [];
|
|
59
|
+
for (const finding of scoped) {
|
|
60
|
+
const haystack = `${normalizeString(finding.message)} ${normalizeString(finding.ruleId)} ${normalizeString(
|
|
61
|
+
finding.file
|
|
62
|
+
)}`.toLowerCase();
|
|
63
|
+
if (/doc|documentation|spec|guide|readme|runbook|playbook|adr/.test(haystack)) {
|
|
64
|
+
derived.push(finding);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const hotspot of inventory.undocumentedHotspots || []) {
|
|
69
|
+
const exists = derived.some((finding) => toPosixPath(finding.file) === hotspot.path);
|
|
70
|
+
if (exists) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
derived.push({
|
|
74
|
+
severity: hotspot.severity,
|
|
75
|
+
file: hotspot.path,
|
|
76
|
+
line: 1,
|
|
77
|
+
message: `Large module lacks nearby documentation coverage (${hotspot.loc} LOC).`,
|
|
78
|
+
excerpt: `${hotspot.language || "code"} module at ${hotspot.path}`,
|
|
79
|
+
ruleId: "SL-DOC-001",
|
|
80
|
+
suggestedFix: "Add a runbook/spec section describing responsibilities, dependencies, and failure modes.",
|
|
81
|
+
layer: "documentation",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return derived.slice(0, 120);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function estimateDocumentationScore(inventory = {}, summary = {}) {
|
|
89
|
+
const densityScore = Math.min(70, Math.round(Number(inventory.docDensity || 0) * 100));
|
|
90
|
+
const penalty = Number(summary.P1 || 0) * 8 + Number(summary.P2 || 0) * 4 + Number(summary.P3 || 0);
|
|
91
|
+
return Math.max(0, Math.min(100, densityScore + 30 - penalty));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildRecommendations(inventory = {}, summary = {}) {
|
|
95
|
+
const recommendations = [];
|
|
96
|
+
if (Number(summary.P1 || 0) > 0) {
|
|
97
|
+
recommendations.push("Close P1 documentation gaps for critical modules before release.");
|
|
98
|
+
}
|
|
99
|
+
if (inventory.undocumentedHotspots?.length > 0) {
|
|
100
|
+
recommendations.push("Create runbooks/spec addenda for high-LOC modules lacking nearby docs.");
|
|
101
|
+
}
|
|
102
|
+
if (Number(inventory.docDensity || 0) < 0.2) {
|
|
103
|
+
recommendations.push("Increase docs-to-code density with ADRs and operator-oriented troubleshooting guides.");
|
|
104
|
+
}
|
|
105
|
+
if (recommendations.length === 0) {
|
|
106
|
+
recommendations.push("Documentation posture is stable; maintain drift checks between code and specs.");
|
|
107
|
+
}
|
|
108
|
+
return recommendations;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function runDocumentationSpecialist({ findings = [], ingest = {} } = {}) {
|
|
112
|
+
const inventory = buildDocumentationInventory(ingest);
|
|
113
|
+
const documentationFindings = buildDocumentationFindings({
|
|
114
|
+
findings,
|
|
115
|
+
inventory,
|
|
116
|
+
});
|
|
117
|
+
const summary = summarizeSeverity(documentationFindings);
|
|
118
|
+
const documentationScore = estimateDocumentationScore(inventory, summary);
|
|
119
|
+
const confidence =
|
|
120
|
+
documentationFindings.length > 0 ? Math.max(0.72, 1 - documentationFindings.length * 0.0025) : 0.9;
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
schemaVersion: "1.0.0",
|
|
124
|
+
generatedAt: new Date().toISOString(),
|
|
125
|
+
summary: {
|
|
126
|
+
...summary,
|
|
127
|
+
findingCount: documentationFindings.length,
|
|
128
|
+
documentationScore,
|
|
129
|
+
},
|
|
130
|
+
confidence,
|
|
131
|
+
inventory,
|
|
132
|
+
recommendations: buildRecommendations(inventory, summary),
|
|
133
|
+
findings: documentationFindings,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function renderDocumentationSpecialistMarkdown(report = {}) {
|
|
138
|
+
const hotspots = (report.inventory?.undocumentedHotspots || [])
|
|
139
|
+
.map((item) => `- ${item.path} (${item.loc} LOC, severity=${item.severity})`)
|
|
140
|
+
.join("\n");
|
|
141
|
+
const recommendations = (report.recommendations || []).map((item) => `- ${item}`).join("\n");
|
|
142
|
+
|
|
143
|
+
return `# DOCUMENTATION_AGENT_REPORT
|
|
144
|
+
|
|
145
|
+
Generated: ${report.generatedAt}
|
|
146
|
+
Documentation score: ${report.summary?.documentationScore ?? 0}/100
|
|
147
|
+
Confidence: ${((report.confidence || 0) * 100).toFixed(0)}%
|
|
148
|
+
|
|
149
|
+
Summary:
|
|
150
|
+
- Findings: P0=${report.summary?.P0 ?? 0} P1=${report.summary?.P1 ?? 0} P2=${report.summary?.P2 ?? 0} P3=${report.summary?.P3 ?? 0}
|
|
151
|
+
- Blocking: ${report.summary?.blocking ? "yes" : "no"}
|
|
152
|
+
- Total findings: ${report.summary?.findingCount ?? 0}
|
|
153
|
+
|
|
154
|
+
Documentation inventory:
|
|
155
|
+
- doc files: ${report.inventory?.docFileCount ?? 0}
|
|
156
|
+
- code files: ${report.inventory?.codeFileCount ?? 0}
|
|
157
|
+
- doc density: ${((report.inventory?.docDensity || 0) * 100).toFixed(1)}%
|
|
158
|
+
|
|
159
|
+
Undocumented hotspots:
|
|
160
|
+
${hotspots || "- none"}
|
|
161
|
+
|
|
162
|
+
Recommendations:
|
|
163
|
+
${recommendations || "- none"}
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
function normalizeString(value) {
|
|
2
|
-
return String(value || "").trim();
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function toPosixPath(value) {
|
|
6
|
-
return String(value || "").replace(/\\/g, "/");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function summarizeSeverity(findings = []) {
|
|
10
|
-
const summary = { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
11
|
-
for (const finding of findings) {
|
|
12
|
-
const severity = normalizeString(finding.severity).toUpperCase();
|
|
13
|
-
if (Object.prototype.hasOwnProperty.call(summary, severity)) {
|
|
14
|
-
summary[severity] += 1;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
summary.blocking = summary.P0 > 0 || summary.P1 > 0;
|
|
18
|
-
return summary;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function buildRuntimeHotspots(ingest = {}) {
|
|
22
|
-
const indexed = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
23
|
-
return indexed
|
|
24
|
-
.filter((file) => Number(file.loc || 0) >= 280)
|
|
25
|
-
.sort((left, right) => Number(right.loc || 0) - Number(left.loc || 0))
|
|
26
|
-
.slice(0, 20)
|
|
27
|
-
.map((file) => ({
|
|
28
|
-
path: toPosixPath(file.path),
|
|
29
|
-
loc: Number(file.loc || 0),
|
|
30
|
-
language: file.language,
|
|
31
|
-
severity: Number(file.loc || 0) >= 900 ? "P1" : "P2",
|
|
32
|
-
}));
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function buildPerformanceFindings({ findings = [], runtimeHotspots = [] } = {}) {
|
|
36
|
-
const scoped = Array.isArray(findings) ? findings : [];
|
|
37
|
-
const derived = [];
|
|
38
|
-
for (const finding of scoped) {
|
|
39
|
-
const haystack = `${normalizeString(finding.message)} ${normalizeString(finding.ruleId)} ${normalizeString(
|
|
40
|
-
finding.file
|
|
41
|
-
)}`.toLowerCase();
|
|
42
|
-
if (/latency|loop|n\+1|query|performance|cache|hot path|throughput|timeout/.test(haystack)) {
|
|
43
|
-
derived.push(finding);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
for (const hotspot of runtimeHotspots) {
|
|
48
|
-
const exists = derived.some((finding) => toPosixPath(finding.file) === hotspot.path);
|
|
49
|
-
if (exists) {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
derived.push({
|
|
53
|
-
severity: hotspot.severity,
|
|
54
|
-
file: hotspot.path,
|
|
55
|
-
line: 1,
|
|
56
|
-
message: `Runtime hotspot candidate detected (${hotspot.loc} LOC).`,
|
|
57
|
-
excerpt: `${hotspot.language || "code"} module at ${hotspot.path}`,
|
|
58
|
-
ruleId: "SL-PERF-001",
|
|
59
|
-
suggestedFix: "Profile this path and split heavy loops/queries into bounded units with caching.",
|
|
60
|
-
layer: "performance",
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return derived.slice(0, 120);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function estimatePerformanceScore(runtimeHotspots = [], summary = {}) {
|
|
68
|
-
const penalty =
|
|
69
|
-
runtimeHotspots.length * 2 +
|
|
70
|
-
Number(summary.P1 || 0) * 8 +
|
|
71
|
-
Number(summary.P2 || 0) * 4 +
|
|
72
|
-
Number(summary.P3 || 0);
|
|
73
|
-
return Math.max(0, 100 - Math.min(100, penalty));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function buildRecommendations({ runtimeHotspots = [], summary = {} } = {}) {
|
|
77
|
-
const recommendations = [];
|
|
78
|
-
if (Number(summary.P1 || 0) > 0) {
|
|
79
|
-
recommendations.push("Resolve P1 performance findings and add regression benchmarks before merge.");
|
|
80
|
-
}
|
|
81
|
-
if (runtimeHotspots.length > 0) {
|
|
82
|
-
recommendations.push("Profile top hotspot modules and document expected p95/p99 behavior for each.");
|
|
83
|
-
}
|
|
84
|
-
if (Number(summary.P2 || 0) >= 4) {
|
|
85
|
-
recommendations.push("Introduce staged load checks for high-change paths to prevent latency drift.");
|
|
86
|
-
}
|
|
87
|
-
if (recommendations.length === 0) {
|
|
88
|
-
recommendations.push("Performance posture is stable; continue periodic profiling on critical paths.");
|
|
89
|
-
}
|
|
90
|
-
return recommendations;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function runPerformanceSpecialist({ findings = [], ingest = {} } = {}) {
|
|
94
|
-
const runtimeHotspots = buildRuntimeHotspots(ingest);
|
|
95
|
-
const performanceFindings = buildPerformanceFindings({
|
|
96
|
-
findings,
|
|
97
|
-
runtimeHotspots,
|
|
98
|
-
});
|
|
99
|
-
const summary = summarizeSeverity(performanceFindings);
|
|
100
|
-
const performanceScore = estimatePerformanceScore(runtimeHotspots, summary);
|
|
101
|
-
const confidence =
|
|
102
|
-
performanceFindings.length > 0 ? Math.max(0.74, 1 - performanceFindings.length * 0.0025) : 0.9;
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
schemaVersion: "1.0.0",
|
|
106
|
-
generatedAt: new Date().toISOString(),
|
|
107
|
-
summary: {
|
|
108
|
-
...summary,
|
|
109
|
-
findingCount: performanceFindings.length,
|
|
110
|
-
performanceScore,
|
|
111
|
-
},
|
|
112
|
-
confidence,
|
|
113
|
-
runtimeHotspots,
|
|
114
|
-
recommendations: buildRecommendations({
|
|
115
|
-
runtimeHotspots,
|
|
116
|
-
summary,
|
|
117
|
-
}),
|
|
118
|
-
findings: performanceFindings,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export function renderPerformanceSpecialistMarkdown(report = {}) {
|
|
123
|
-
const hotspots = (report.runtimeHotspots || [])
|
|
124
|
-
.map((item) => `- ${item.path} (${item.loc} LOC, severity=${item.severity})`)
|
|
125
|
-
.join("\n");
|
|
126
|
-
const recommendations = (report.recommendations || []).map((item) => `- ${item}`).join("\n");
|
|
127
|
-
|
|
128
|
-
return `# PERFORMANCE_AGENT_REPORT
|
|
129
|
-
|
|
130
|
-
Generated: ${report.generatedAt}
|
|
131
|
-
Performance score: ${report.summary?.performanceScore ?? 0}/100
|
|
132
|
-
Confidence: ${((report.confidence || 0) * 100).toFixed(0)}%
|
|
133
|
-
|
|
134
|
-
Summary:
|
|
135
|
-
- Findings: P0=${report.summary?.P0 ?? 0} P1=${report.summary?.P1 ?? 0} P2=${report.summary?.P2 ?? 0} P3=${report.summary?.P3 ?? 0}
|
|
136
|
-
- Blocking: ${report.summary?.blocking ? "yes" : "no"}
|
|
137
|
-
- Total findings: ${report.summary?.findingCount ?? 0}
|
|
138
|
-
|
|
139
|
-
Runtime hotspots:
|
|
140
|
-
${hotspots || "- none"}
|
|
141
|
-
|
|
142
|
-
Recommendations:
|
|
143
|
-
${recommendations || "- none"}
|
|
144
|
-
`;
|
|
145
|
-
}
|
|
1
|
+
function normalizeString(value) {
|
|
2
|
+
return String(value || "").trim();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function toPosixPath(value) {
|
|
6
|
+
return String(value || "").replace(/\\/g, "/");
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function summarizeSeverity(findings = []) {
|
|
10
|
+
const summary = { P0: 0, P1: 0, P2: 0, P3: 0 };
|
|
11
|
+
for (const finding of findings) {
|
|
12
|
+
const severity = normalizeString(finding.severity).toUpperCase();
|
|
13
|
+
if (Object.prototype.hasOwnProperty.call(summary, severity)) {
|
|
14
|
+
summary[severity] += 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
summary.blocking = summary.P0 > 0 || summary.P1 > 0;
|
|
18
|
+
return summary;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildRuntimeHotspots(ingest = {}) {
|
|
22
|
+
const indexed = Array.isArray(ingest.indexedFiles?.files) ? ingest.indexedFiles.files : [];
|
|
23
|
+
return indexed
|
|
24
|
+
.filter((file) => Number(file.loc || 0) >= 280)
|
|
25
|
+
.sort((left, right) => Number(right.loc || 0) - Number(left.loc || 0))
|
|
26
|
+
.slice(0, 20)
|
|
27
|
+
.map((file) => ({
|
|
28
|
+
path: toPosixPath(file.path),
|
|
29
|
+
loc: Number(file.loc || 0),
|
|
30
|
+
language: file.language,
|
|
31
|
+
severity: Number(file.loc || 0) >= 900 ? "P1" : "P2",
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildPerformanceFindings({ findings = [], runtimeHotspots = [] } = {}) {
|
|
36
|
+
const scoped = Array.isArray(findings) ? findings : [];
|
|
37
|
+
const derived = [];
|
|
38
|
+
for (const finding of scoped) {
|
|
39
|
+
const haystack = `${normalizeString(finding.message)} ${normalizeString(finding.ruleId)} ${normalizeString(
|
|
40
|
+
finding.file
|
|
41
|
+
)}`.toLowerCase();
|
|
42
|
+
if (/latency|loop|n\+1|query|performance|cache|hot path|throughput|timeout/.test(haystack)) {
|
|
43
|
+
derived.push(finding);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const hotspot of runtimeHotspots) {
|
|
48
|
+
const exists = derived.some((finding) => toPosixPath(finding.file) === hotspot.path);
|
|
49
|
+
if (exists) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
derived.push({
|
|
53
|
+
severity: hotspot.severity,
|
|
54
|
+
file: hotspot.path,
|
|
55
|
+
line: 1,
|
|
56
|
+
message: `Runtime hotspot candidate detected (${hotspot.loc} LOC).`,
|
|
57
|
+
excerpt: `${hotspot.language || "code"} module at ${hotspot.path}`,
|
|
58
|
+
ruleId: "SL-PERF-001",
|
|
59
|
+
suggestedFix: "Profile this path and split heavy loops/queries into bounded units with caching.",
|
|
60
|
+
layer: "performance",
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return derived.slice(0, 120);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function estimatePerformanceScore(runtimeHotspots = [], summary = {}) {
|
|
68
|
+
const penalty =
|
|
69
|
+
runtimeHotspots.length * 2 +
|
|
70
|
+
Number(summary.P1 || 0) * 8 +
|
|
71
|
+
Number(summary.P2 || 0) * 4 +
|
|
72
|
+
Number(summary.P3 || 0);
|
|
73
|
+
return Math.max(0, 100 - Math.min(100, penalty));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function buildRecommendations({ runtimeHotspots = [], summary = {} } = {}) {
|
|
77
|
+
const recommendations = [];
|
|
78
|
+
if (Number(summary.P1 || 0) > 0) {
|
|
79
|
+
recommendations.push("Resolve P1 performance findings and add regression benchmarks before merge.");
|
|
80
|
+
}
|
|
81
|
+
if (runtimeHotspots.length > 0) {
|
|
82
|
+
recommendations.push("Profile top hotspot modules and document expected p95/p99 behavior for each.");
|
|
83
|
+
}
|
|
84
|
+
if (Number(summary.P2 || 0) >= 4) {
|
|
85
|
+
recommendations.push("Introduce staged load checks for high-change paths to prevent latency drift.");
|
|
86
|
+
}
|
|
87
|
+
if (recommendations.length === 0) {
|
|
88
|
+
recommendations.push("Performance posture is stable; continue periodic profiling on critical paths.");
|
|
89
|
+
}
|
|
90
|
+
return recommendations;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function runPerformanceSpecialist({ findings = [], ingest = {} } = {}) {
|
|
94
|
+
const runtimeHotspots = buildRuntimeHotspots(ingest);
|
|
95
|
+
const performanceFindings = buildPerformanceFindings({
|
|
96
|
+
findings,
|
|
97
|
+
runtimeHotspots,
|
|
98
|
+
});
|
|
99
|
+
const summary = summarizeSeverity(performanceFindings);
|
|
100
|
+
const performanceScore = estimatePerformanceScore(runtimeHotspots, summary);
|
|
101
|
+
const confidence =
|
|
102
|
+
performanceFindings.length > 0 ? Math.max(0.74, 1 - performanceFindings.length * 0.0025) : 0.9;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
schemaVersion: "1.0.0",
|
|
106
|
+
generatedAt: new Date().toISOString(),
|
|
107
|
+
summary: {
|
|
108
|
+
...summary,
|
|
109
|
+
findingCount: performanceFindings.length,
|
|
110
|
+
performanceScore,
|
|
111
|
+
},
|
|
112
|
+
confidence,
|
|
113
|
+
runtimeHotspots,
|
|
114
|
+
recommendations: buildRecommendations({
|
|
115
|
+
runtimeHotspots,
|
|
116
|
+
summary,
|
|
117
|
+
}),
|
|
118
|
+
findings: performanceFindings,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function renderPerformanceSpecialistMarkdown(report = {}) {
|
|
123
|
+
const hotspots = (report.runtimeHotspots || [])
|
|
124
|
+
.map((item) => `- ${item.path} (${item.loc} LOC, severity=${item.severity})`)
|
|
125
|
+
.join("\n");
|
|
126
|
+
const recommendations = (report.recommendations || []).map((item) => `- ${item}`).join("\n");
|
|
127
|
+
|
|
128
|
+
return `# PERFORMANCE_AGENT_REPORT
|
|
129
|
+
|
|
130
|
+
Generated: ${report.generatedAt}
|
|
131
|
+
Performance score: ${report.summary?.performanceScore ?? 0}/100
|
|
132
|
+
Confidence: ${((report.confidence || 0) * 100).toFixed(0)}%
|
|
133
|
+
|
|
134
|
+
Summary:
|
|
135
|
+
- Findings: P0=${report.summary?.P0 ?? 0} P1=${report.summary?.P1 ?? 0} P2=${report.summary?.P2 ?? 0} P3=${report.summary?.P3 ?? 0}
|
|
136
|
+
- Blocking: ${report.summary?.blocking ? "yes" : "no"}
|
|
137
|
+
- Total findings: ${report.summary?.findingCount ?? 0}
|
|
138
|
+
|
|
139
|
+
Runtime hotspots:
|
|
140
|
+
${hotspots || "- none"}
|
|
141
|
+
|
|
142
|
+
Recommendations:
|
|
143
|
+
${recommendations || "- none"}
|
|
144
|
+
`;
|
|
145
|
+
}
|