@dailephd/my-dev-kit-lab 0.3.1 → 0.3.3
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 +24 -18
- package/dist/scripts/audits/runAudit.js +8 -2
- package/dist/scripts/security/validate.js +16 -47
- package/dist/src/audits/codeRot/detectors/deadCodeCandidateDetector.js +191 -1
- package/dist/src/audits/codeRot/detectors/docsCodeMismatchDetector.js +202 -4
- package/dist/src/audits/codeRot/detectors/duplicateImplementationDetector.js +42 -9
- package/dist/src/audits/codeRot/detectors/testRotDetector.js +241 -1
- package/dist/src/audits/codeRot/utils/filePatternUtils.js +19 -1
- package/dist/src/audits/codeRot/utils/jvmSourceFactsUtils.js +153 -0
- package/dist/src/audits/core/auditConfig.js +2 -2
- package/dist/src/audits/core/auditRunner.js +19 -0
- package/dist/src/audits/core/auditTypes.js +14 -7
- package/dist/src/audits/core/javaAnalyzer.js +282 -0
- package/dist/src/audits/core/jvmProjectMetadata.js +106 -0
- package/dist/src/audits/core/kotlinAnalyzer.js +280 -0
- package/dist/src/audits/core/languageAnalyzerRegistry.js +12 -3
- package/dist/src/audits/core/projectInventory.js +76 -3
- package/dist/src/audits/core/pythonAnalyzer.js +328 -0
- package/dist/src/audits/core/pythonProjectMetadata.js +80 -0
- package/dist/src/audits/report/auditReportModel.js +3 -0
- package/dist/src/audits/report/renderAuditJsonReport.js +2 -0
- package/dist/src/audits/report/renderAuditTextReport.js +40 -0
- package/dist/src/audits/security/mapSecurityFindingToAuditIssue.js +70 -0
- package/dist/src/audits/security/securityAuditAdapter.js +70 -0
- package/dist/src/audits/security/securityAuditTypes.js +17 -0
- package/dist/src/securityValidation/report/buildSecurityReport.js +31 -0
- package/dist/src/securityValidation/report/writeSecurityReportFiles.js +28 -0
- package/dist/src/securityValidation/validate/resolveTarget.js +9 -0
- package/package.json +6 -8
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# my-dev-kit-lab
|
|
2
2
|
|
|
3
|
-
my-dev-kit-lab is the experiment, evidence, reporting, security-validation, and audit companion for my-dev-kit. It runs reproducible experiments that test whether my-dev-kit's graph-guided retrieval helps coding-agent workflows, collects metrics, renders reports, generates plots, captures screenshots, builds gallery outputs, performs automated CLI/package security validation, and runs the generic audit framework.
|
|
4
|
-
|
|
5
|
-
The current published baseline is `v0.3.
|
|
6
|
-
|
|
7
|
-
The checked-out
|
|
3
|
+
my-dev-kit-lab is the experiment, evidence, reporting, security-validation, and audit companion for my-dev-kit. It runs reproducible experiments that test whether my-dev-kit's graph-guided retrieval helps coding-agent workflows, collects metrics, renders reports, generates plots, captures screenshots, builds gallery outputs, performs automated CLI/package security validation, and runs the generic audit framework.
|
|
4
|
+
|
|
5
|
+
The current published baseline is `v0.3.2`. `v0.3.0` introduced the generic audit framework and the first implemented code-rot detector family; `v0.3.1` added the language-aware code-rot substrate plus TypeScript/JavaScript support; `v0.3.2` added Python-aware code-rot support and a first security-validation audit adapter. The generic experiment-plugin framework was introduced in `v0.2.0`. The first plugin is `context-strategy-comparison`, which preserves the existing raw-full-file vs my-dev-kit-guided workflow through the plugin runner.
|
|
6
|
+
|
|
7
|
+
The checked-out implementation is ahead of the published baseline: release-prepared, not-yet-published `v0.3.3` work adds Java/Kotlin source-facts support and JVM-aware code-rot detector integration on top of the published `v0.3.2` baseline. Package metadata is now `0.3.3` as part of release preparation. Java/Kotlin support is conservative static analysis only: dependency-free scanners, static Gradle/Maven metadata detection, and no compiler, classpath, Gradle, Maven, Android, or target-test execution. Cross-language stability, Android validation, framework-aware profiles, and the `quality`/`project`/`all` audit types remain future roadmap work.
|
|
8
8
|
|
|
9
9
|
my-dev-kit is most useful when the repository is larger than the task. It helps coding agents work with large codebases through reusable structural indexing, graph-guided retrieval, targeted source slices, and auditable context selection. Results are scoped evidence; the project does not claim that my-dev-kit always saves tokens.
|
|
10
10
|
|
|
@@ -33,8 +33,10 @@ my-dev-kit is most useful when the repository is larger than the task. It helps
|
|
|
33
33
|
- First experiment plugin: `context-strategy-comparison`
|
|
34
34
|
- Target-aware experiment execution for local projects via `experiment:run -- --target <path>`
|
|
35
35
|
- Plugin-aware JSON and HTML reports with plugin, target, variant, metric, artifact, warning, skip, and failure metadata
|
|
36
|
-
- Security validation framework: dependency audit, package tarball inspection, CLI adversarial tests, static scans (CodeQL/Semgrep), bounded fuzz smoke, attack-scenario checks, and structured verdict/report output — runnable against any local project via `security:validate --target <path
|
|
37
|
-
- Generic audit framework
|
|
36
|
+
- Security validation framework: dependency audit, package tarball inspection, CLI adversarial tests, static scans (CodeQL/Semgrep), bounded fuzz smoke, attack-scenario checks, and structured verdict/report output — runnable against any local project via `security:validate --target <path>`. `security:validate` remains the standalone, focused security-validation command; it is not replaced by the audit adapter below.
|
|
37
|
+
- Generic audit framework, current published baseline: `npm run audit` runs heuristic, conservative code-rot detectors and writes text/JSON reports under `reports/audits/code-rot/` by default. `code-rot` and `security` are the currently implemented audit types (`--types code-rot`, `--types security`, or combined `--types code-rot,security`); `quality`, `project`, and `all` audit types are planned and fail cleanly instead of running. `npm run audit` supports `--target`, `--types`, and other flags — see [docs/COMMANDS.md](docs/COMMANDS.md). Audit does not modify target files and does not auto-fix issues.
|
|
38
|
+
- Language-aware code-rot support, current checked-out package state: normalized language/file-role inventory, a source-facts model and collector, TypeScript/JavaScript, Python, Java, and Kotlin analyzers, and conservative detector integrations for `dead-code-candidate`, `duplicate-implementation-candidate`, `test-rot`, and `docs-code-mismatch`. Python project metadata remains a top-level audit report field. Java/Kotlin support adds static JVM project metadata detection for Gradle/Maven/source-set shape and conservative source-facts evidence, but no compiler parsing, type resolution, classpath resolution, Gradle/Maven execution, Android validation, or target-project test execution. No `SourceFacts` schema change or new audit command was added for `v0.3.3`.
|
|
39
|
+
- Security-validation audit adapter, current checked-out package state: `npm run audit -- --types security` runs security validation through the shared audit/report surface by calling the same `runSecurityValidation()` internals `security:validate` uses, maps security findings into audit-issue-shaped entries, and adds a `securitySummary` field (verdict, check counts, finding counts, and links to the original `reports/security/*.txt`/`*.json` report) to audit JSON/text reports. `npm run audit -- --types code-rot,security` runs both audit types together. The original `security:validate` command and its `reports/security/` output are unchanged.
|
|
38
40
|
|
|
39
41
|
---
|
|
40
42
|
|
|
@@ -203,16 +205,18 @@ See [docs/METRICS.md](docs/METRICS.md) for full metric definitions.
|
|
|
203
205
|
|
|
204
206
|
---
|
|
205
207
|
|
|
206
|
-
## Current baseline release positioning
|
|
207
|
-
|
|
208
|
-
my-dev-kit-lab is at a working baseline. The raw-vs-indexed experiment pipeline is fully implemented and produces reproducible artifacts. Real-agent campaign support exists for Codex and Claude. The current published npm baseline is `v0.3.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
- `v0.
|
|
215
|
-
-
|
|
208
|
+
## Current baseline release positioning
|
|
209
|
+
|
|
210
|
+
my-dev-kit-lab is at a working baseline. The raw-vs-indexed experiment pipeline is fully implemented and produces reproducible artifacts. Real-agent campaign support exists for Codex and Claude. The current published npm baseline is `v0.3.2`, which includes the generic audit framework, the code-rot detector family, the language-aware code-rot substrate with TypeScript/JavaScript and Python support, and a first security-validation audit adapter, on top of the earlier experiment and automated security-validation work.
|
|
211
|
+
|
|
212
|
+
The release-prepared, not-yet-published `v0.3.3` implementation extends that baseline with Java/Kotlin analyzers, JVM metadata detection, Java/Kotlin detector support, and Java/Kotlin/Gradle/Maven docs-code-mismatch checks. This work is documented here as checked-out behavior, not as a published package state; package metadata is now `0.3.3`, while `v0.3.2` remains the previous published baseline until publication completes.
|
|
213
|
+
|
|
214
|
+
Planned roadmap direction after the published baseline:
|
|
215
|
+
|
|
216
|
+
- `v0.3.3`: Java/Kotlin code-rot support (release-prepared, not yet published)
|
|
217
|
+
- `v0.3.4`: cross-language stability for the language-aware code-rot track
|
|
218
|
+
- `v0.4.0` through `v0.4.2`: Android automated security validation, including an Android-specific extension of the security audit adapter
|
|
219
|
+
- manual pentest: post-v1 / version TBD
|
|
216
220
|
|
|
217
221
|
---
|
|
218
222
|
|
|
@@ -220,6 +224,8 @@ Planned roadmap direction after the published baseline:
|
|
|
220
224
|
|
|
221
225
|
my-dev-kit-lab owns a release-security validation track for **my-dev-kit**. This work is separate from the experiment pipeline and does not replace the generic experiment-plugin roadmap. Its purpose is to generate release-validation evidence for the local CLI/package before release preparation.
|
|
222
226
|
|
|
227
|
+
`security:validate` remains the standalone, focused command for this validation and is unaffected by the current checked-out state. `npm run audit -- --types security` additionally adapts the same `security:validate` internals into the shared audit/report surface (mapped issues, a `securitySummary` field, and links to the original `reports/security/` output) — it does not replace or duplicate `security:validate`'s own checks or reports.
|
|
228
|
+
|
|
223
229
|
This is not a web application pentest framework. **my-dev-kit** is a local CLI/package, so the validation model is CLI/package adversarial testing focused on whether it remains:
|
|
224
230
|
|
|
225
231
|
- local-first
|
|
@@ -250,7 +256,7 @@ Each security command can validate my-dev-kit-lab itself or another local projec
|
|
|
250
256
|
|
|
251
257
|
`security:validate` supports `--checks`, `--profile`, `--format`, `--fail-on`, `--out`, and `--report-prefix`. The no-flag path remains backward compatible and still runs the classic implemented check groups: `deps`, `package`, `static`, `cli-adversarial`, and `fuzz`. Supplying `--profile` without `--checks` swaps in that profile's default checks; supplying explicit `--checks` always wins over profile defaults.
|
|
252
258
|
|
|
253
|
-
The current attack-scenario checks cover boundary, subprocess, secrets, and network assumptions. They are automated adversarial checks, not a manual pentest. Manual pentest is deferred until after `v1.0.0`. `verdictImpact` metadata from each registered scenario drives blocker categorization in report reasoning, and narrowed `--checks` runs are labeled as scoped rather than described as a full release gate.
|
|
259
|
+
The current attack-scenario checks cover boundary, subprocess, secrets, and network assumptions. They are automated adversarial checks, not a manual pentest. Manual pentest is deferred until after `v1.0.0`. `verdictImpact` metadata from each registered scenario drives blocker categorization in report reasoning, and narrowed `--checks` runs are labeled as scoped rather than described as a full release gate.
|
|
254
260
|
|
|
255
261
|
JSON report poisoning/config-injection checks use a baseline-diff schema guard so legitimate additive JSON fields do not fail the guard while payload-created trusted top-level fields still do. Text reports are sanitized to strip ANSI/control-byte payloads before rendering.
|
|
256
262
|
|
|
@@ -18,7 +18,7 @@ import { writeAuditReports } from "../../src/audits/report/writeAuditReports.js"
|
|
|
18
18
|
// renderAuditJsonReport.ts, renderAuditTextReport.ts, writeAuditReports.ts.
|
|
19
19
|
// Mirrors the structure of scripts/security/validate.ts.
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
|
-
const USAGE = "Usage: npm run audit -- [--target <path>] [--types code-rot] " +
|
|
21
|
+
const USAGE = "Usage: npm run audit -- [--target <path>] [--types code-rot,security] " +
|
|
22
22
|
"[--include docs,tests,package,architecture,cli] [--format text,json] " +
|
|
23
23
|
"[--fail-on blocker|high|medium|low|none] [--out <path>]";
|
|
24
24
|
const rawArgs = process.argv.slice(2);
|
|
@@ -80,9 +80,15 @@ if (model.summary.detectorErrorCount > 0) {
|
|
|
80
80
|
console.log(` [${e.id}] ${e.message}`);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
if (model.summary.noDetectorsRegistered) {
|
|
83
|
+
if (model.summary.noDetectorsRegistered && !model.securitySummary.ran) {
|
|
84
84
|
console.log(`Note: no code-rot detectors are registered for this run's --types/--include selection -- this run collected inventory/source-of-truth data only.`);
|
|
85
85
|
}
|
|
86
|
+
if (model.securitySummary.ran) {
|
|
87
|
+
console.log(`\nSecurity validation: verdict=${model.securitySummary.verdictLabel} checks=${model.securitySummary.totalChecks} findings(blocker=${model.securitySummary.findingCounts.blocker} major=${model.securitySummary.findingCounts.major} minor=${model.securitySummary.findingCounts.minor} info=${model.securitySummary.findingCounts.informational})`);
|
|
88
|
+
if (model.securitySummary.reportPaths.text) {
|
|
89
|
+
console.log(` Full report: ${model.securitySummary.reportPaths.text}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
86
92
|
console.log(`\nInventory: ${model.inventory.totalScannedFileCount} file(s) scanned, ${model.inventory.skippedFileCount} skipped`);
|
|
87
93
|
console.log(` source=${model.inventory.filesByCategory.source} tests=${model.inventory.filesByCategory.tests} docs=${model.inventory.filesByCategory.docs} package=${model.inventory.filesByCategory.package} config=${model.inventory.filesByCategory.config} scripts=${model.inventory.filesByCategory.scripts} ci=${model.inventory.filesByCategory.ci}`);
|
|
88
94
|
console.log(`Source of truth: package=${model.sourceOfTruth.packageName ?? "(none)"}@${model.sourceOfTruth.packageVersion ?? "?"} readme=${model.sourceOfTruth.hasReadme} changelog=${model.sourceOfTruth.hasChangelog} ciWorkflows=${model.sourceOfTruth.ciWorkflowCount}`);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import fs from "node:fs";
|
|
4
2
|
import { runSecurityValidation } from "../../src/securityValidation/validate/runSecurityValidation.js";
|
|
5
3
|
import { resolveValidationTarget, reportFilenamePrefix } from "../../src/securityValidation/validate/resolveTarget.js";
|
|
6
|
-
import {
|
|
4
|
+
import { buildSecurityReportFromSummary } from "../../src/securityValidation/report/buildSecurityReport.js";
|
|
5
|
+
import { renderTextReport } from "../../src/securityValidation/report/renderSecurityReport.js";
|
|
6
|
+
import { writeSecurityReportFiles } from "../../src/securityValidation/report/writeSecurityReportFiles.js";
|
|
7
7
|
import { parseSecurityValidateArgs, normalizeSecurityValidateConfig, applyProfileDefaultChecksIfApplicable, } from "../../src/securityValidation/validate/cliOptions.js";
|
|
8
8
|
import { resolveAttackProfile } from "../../src/securityValidation/attackScenarios/attackProfile.js";
|
|
9
9
|
import { findingsBreachFailOnThreshold } from "../../src/securityValidation/validate/verdict.js";
|
|
@@ -81,55 +81,24 @@ const summary = await runSecurityValidation({
|
|
|
81
81
|
// downgrade an existing blocker.
|
|
82
82
|
const failOnBreached = findingsBreachFailOnThreshold(summary.findings, config.failOnThreshold);
|
|
83
83
|
// Build report object
|
|
84
|
-
const report = {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
packageName: summary.packageName,
|
|
92
|
-
packageVersion: summary.packageVersion,
|
|
93
|
-
branch: summary.auditedBranch,
|
|
94
|
-
commit: summary.auditedCommit,
|
|
95
|
-
isSelf: summary.isSelf,
|
|
96
|
-
generatedAt: summary.finishedAt,
|
|
97
|
-
totalDurationMs: new Date(summary.finishedAt).getTime() - new Date(summary.startedAt).getTime(),
|
|
98
|
-
profile: config.profile,
|
|
99
|
-
selectedChecks: config.checks,
|
|
100
|
-
failOnThreshold: config.failOnThreshold,
|
|
101
|
-
formats: config.formats,
|
|
102
|
-
failOnBreached,
|
|
103
|
-
isFullReleaseGate: summary.isFullReleaseGate,
|
|
104
|
-
},
|
|
105
|
-
sections: [],
|
|
106
|
-
allChecks: summary.checks,
|
|
107
|
-
allFindings: summary.findings,
|
|
108
|
-
verdict: summary.verdict,
|
|
109
|
-
recommendedNextStep: summary.recommendedNextStep,
|
|
110
|
-
attackResults: summary.attackResults,
|
|
111
|
-
verdictReasonSummary: summary.verdictReasonSummary,
|
|
112
|
-
};
|
|
84
|
+
const report = buildSecurityReportFromSummary(summary, {
|
|
85
|
+
profile: config.profile,
|
|
86
|
+
selectedChecks: config.checks,
|
|
87
|
+
failOnThreshold: config.failOnThreshold,
|
|
88
|
+
formats: config.formats,
|
|
89
|
+
failOnBreached,
|
|
90
|
+
});
|
|
113
91
|
const textReport = renderTextReport(report);
|
|
114
|
-
const jsonReport = renderJsonReport(report);
|
|
115
92
|
// Determine output directory
|
|
116
93
|
const reportsDir = config.out;
|
|
117
|
-
if (!fs.existsSync(reportsDir)) {
|
|
118
|
-
fs.mkdirSync(reportsDir, { recursive: true });
|
|
119
|
-
}
|
|
120
94
|
// Determine report filename prefix
|
|
121
95
|
const prefix = args.reportPrefix ?? reportFilenamePrefix(target);
|
|
122
|
-
const txtPath =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
if (config.formats.includes("json")) {
|
|
130
|
-
fs.writeFileSync(jsonPath, jsonReport, "utf8");
|
|
131
|
-
writtenPaths.push(jsonPath);
|
|
132
|
-
}
|
|
96
|
+
const { textPath: txtPath, jsonPath, writtenPaths } = writeSecurityReportFiles({
|
|
97
|
+
outDir: reportsDir,
|
|
98
|
+
prefix,
|
|
99
|
+
report,
|
|
100
|
+
formats: config.formats,
|
|
101
|
+
});
|
|
133
102
|
// Print report to stdout
|
|
134
103
|
console.log(textReport);
|
|
135
104
|
console.log(`\nReports written:`);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { readBoundedFileText } from "../utils/boundedRead.js";
|
|
3
|
-
import { baseNameNoExt } from "../utils/filePatternUtils.js";
|
|
3
|
+
import { baseNameNoExt, isGenericOrTestPrefixedDeclarationName } from "../utils/filePatternUtils.js";
|
|
4
4
|
import { deduplicateIssuesById, makeCodeRotIssue } from "../utils/issueFactories.js";
|
|
5
5
|
import { collectRelativeImportBasenames, getParsedSourceFacts, indexSourceFactsByPath } from "../utils/sourceFactsLookup.js";
|
|
6
|
+
import { isJvmLifecycleOrConventionalDeclarationName, jvmImportSimpleName } from "../utils/jvmSourceFactsUtils.js";
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
8
|
// v0.3.0 Batch 4 — dead-code candidate detector.
|
|
8
9
|
//
|
|
@@ -50,6 +51,10 @@ export const DEAD_CODE_CANDIDATE_DETECTOR = {
|
|
|
50
51
|
if (ctx.config.include.includes("tests")) {
|
|
51
52
|
issues.push(...findUnreferencedFixtures(ctx));
|
|
52
53
|
}
|
|
54
|
+
if (ctx.config.include.includes("cli") || ctx.config.include.includes("architecture")) {
|
|
55
|
+
issues.push(...findPossiblyUnreferencedPythonDeclarations(ctx));
|
|
56
|
+
issues.push(...findPossiblyUnreferencedJvmDeclarations(ctx));
|
|
57
|
+
}
|
|
53
58
|
return deduplicateIssuesById(issues);
|
|
54
59
|
},
|
|
55
60
|
};
|
|
@@ -250,6 +255,191 @@ function buildSourceFactsEvidence(facts) {
|
|
|
250
255
|
confidence: "medium",
|
|
251
256
|
};
|
|
252
257
|
}
|
|
258
|
+
// v0.3.2 Batch 2 -- conservative, declaration-level Python dead-code
|
|
259
|
+
// candidate check. TS/JS module-level "unused exported symbol" detection is
|
|
260
|
+
// explicitly SKIPPED in this file's own header comment (no safe/cheap way
|
|
261
|
+
// without a real type-checker) -- this Python-only check is narrower and
|
|
262
|
+
// safer than that skipped TS/JS idea would be, because Python source facts
|
|
263
|
+
// already carry declarations *and* a repo-wide cross-file "imported name"
|
|
264
|
+
// index (unlike anything comparable for TS/JS bare specifiers), so the
|
|
265
|
+
// false-positive surface is much smaller. Every one of the skip conditions
|
|
266
|
+
// below is a deliberate, spec-required conservatism -- see the requirements
|
|
267
|
+
// list in this batch's task description. Findings stay at the lowest
|
|
268
|
+
// severity/confidence tier in this detector's own schema (matches
|
|
269
|
+
// findUnreferencedFixtures()'s tier) and use "candidate"/"may indicate"
|
|
270
|
+
// wording throughout, never "unused" or "dead".
|
|
271
|
+
function findPossiblyUnreferencedPythonDeclarations(ctx) {
|
|
272
|
+
if (!ctx.sourceFacts)
|
|
273
|
+
return [];
|
|
274
|
+
const index = indexSourceFactsByPath(ctx.sourceFacts);
|
|
275
|
+
// Cross-file "this name was imported somewhere" signal -- a declaration
|
|
276
|
+
// named here is referenced by at least one `from module import name`
|
|
277
|
+
// elsewhere in the scanned Python file set, which is the strongest signal
|
|
278
|
+
// this conservative scan can offer (Python source facts carry no
|
|
279
|
+
// call/identifier reference tracking -- see pythonAnalyzer.ts's header
|
|
280
|
+
// comment on why `references` stays empty by design).
|
|
281
|
+
const importedNamesAcrossProject = new Set();
|
|
282
|
+
for (const facts of index.values()) {
|
|
283
|
+
if (facts.language !== "python" || facts.parseStatus !== "parsed")
|
|
284
|
+
continue;
|
|
285
|
+
for (const imp of facts.imports) {
|
|
286
|
+
for (const name of imp.importedNames ?? [])
|
|
287
|
+
importedNamesAcrossProject.add(name);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const issues = [];
|
|
291
|
+
for (const facts of index.values()) {
|
|
292
|
+
if (facts.language !== "python" || facts.role !== "source" || facts.parseStatus !== "parsed")
|
|
293
|
+
continue;
|
|
294
|
+
// __init__.py commonly exists only to re-export a package's public
|
|
295
|
+
// surface or to mark a directory as a package -- its declarations (if
|
|
296
|
+
// any) are conservatively excluded entirely rather than guessed at.
|
|
297
|
+
if (path.basename(facts.relativePath) === "__init__.py")
|
|
298
|
+
continue;
|
|
299
|
+
for (const decl of facts.declarations) {
|
|
300
|
+
// Methods are excluded entirely -- this detector has no existing
|
|
301
|
+
// method-level dead-code evidence model for any language (TS/JS
|
|
302
|
+
// methods are never flagged either), and Python method visibility is
|
|
303
|
+
// even harder to infer safely (inherited/overridden/dispatched via
|
|
304
|
+
// duck typing).
|
|
305
|
+
if (decl.kind === "method")
|
|
306
|
+
continue;
|
|
307
|
+
if (!decl.exported)
|
|
308
|
+
continue; // leading-underscore convention already applied by pythonAnalyzer.ts
|
|
309
|
+
if (isGenericOrTestPrefixedDeclarationName(decl.name))
|
|
310
|
+
continue; // main/run/setup/teardown/__init__/__call__/test_*
|
|
311
|
+
// A name explicitly listed in a real `__all__` is the file's own
|
|
312
|
+
// strongest "this is public API" declaration -- skip rather than
|
|
313
|
+
// second-guess it.
|
|
314
|
+
if (facts.exports.some((e) => e.name === decl.name))
|
|
315
|
+
continue;
|
|
316
|
+
if (importedNamesAcrossProject.has(decl.name))
|
|
317
|
+
continue;
|
|
318
|
+
issues.push(makeCodeRotIssue({
|
|
319
|
+
auditType: "code-rot",
|
|
320
|
+
detectorId: DETECTOR_ID,
|
|
321
|
+
idCues: ["unreferenced-python-declaration", facts.relativePath, decl.kind, decl.name],
|
|
322
|
+
title: `Python ${decl.kind} "${decl.name}" in "${facts.relativePath}" has no detected cross-file import (weak signal)`,
|
|
323
|
+
description: `"${decl.name}" (${decl.kind}) is declared in "${facts.relativePath}" and does not appear as an imported name in any other scanned Python file. This is a deterministic source-facts signal from a conservative, non-semantic scan -- it does not resolve dynamic imports, framework/plugin discovery, string-based dispatch, or usage from outside the scanned project, so it may indicate dead code but is not proof. Confirm manually before removing.`,
|
|
324
|
+
severity: "info",
|
|
325
|
+
confidence: "low",
|
|
326
|
+
falsePositiveRisk: "high",
|
|
327
|
+
category: "dead-code-candidate",
|
|
328
|
+
recommendedAction: "Confirm the declaration is genuinely unused before removing it.",
|
|
329
|
+
suggestedFixStrategy: `Remove "${decl.name}" from "${facts.relativePath}" if confirmed unused, or reference it from another module.`,
|
|
330
|
+
validationCommands: ["npm run audit -- --types code-rot --include cli"],
|
|
331
|
+
releaseBlocking: false,
|
|
332
|
+
implementationBlocking: false,
|
|
333
|
+
autoFixEligible: false,
|
|
334
|
+
evidence: [
|
|
335
|
+
{
|
|
336
|
+
kind: "reference",
|
|
337
|
+
message: `Source facts: the Python analyzer parsed this file and found no other scanned Python file importing the name "${decl.name}". Static-analysis limitation -- confidence reduced because this scan cannot see dynamic imports or usage outside the scanned file set.`,
|
|
338
|
+
filePath: facts.relativePath,
|
|
339
|
+
source: DETECTOR_ID,
|
|
340
|
+
confidence: "low",
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
affectedFiles: [facts.relativePath],
|
|
344
|
+
}));
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return issues;
|
|
348
|
+
}
|
|
349
|
+
// v0.3.3 Batch 2 -- conservative, declaration-level Java/Kotlin dead-code
|
|
350
|
+
// candidate check. Same spirit and lowest severity/confidence tier as
|
|
351
|
+
// findPossiblyUnreferencedPythonDeclarations() above, adapted for the two
|
|
352
|
+
// key differences in what Java/Kotlin source facts can safely support:
|
|
353
|
+
//
|
|
354
|
+
// - Method/constructor declarations (DeclarationFactKind "method") are
|
|
355
|
+
// EXCLUDED ENTIRELY, for both languages. There is no call-tracking
|
|
356
|
+
// evidence in this project's source-facts model for any language
|
|
357
|
+
// (`references` stays empty everywhere -- see javaAnalyzer.ts's and
|
|
358
|
+
// kotlinAnalyzer.ts's own header comments), so a method-level check
|
|
359
|
+
// would have zero real evidence behind it and would flag nearly every
|
|
360
|
+
// method with equal (lack of) signal -- pure noise. This mirrors the
|
|
361
|
+
// existing Python check's own "methods are excluded entirely" rule
|
|
362
|
+
// exactly (see above), just extended to two more languages. This also
|
|
363
|
+
// conservatively covers Java constructors without needing a dedicated
|
|
364
|
+
// constructor detector, since javaAnalyzer.ts represents constructors
|
|
365
|
+
// as kind "method" too.
|
|
366
|
+
// - Kotlin top-level `fun` declarations (kind "function") ARE
|
|
367
|
+
// considered, the direct analogue of Python's module-level `def` --
|
|
368
|
+
// but Java has no equivalent (every Java method lives inside a
|
|
369
|
+
// class/interface/enum), so only class/interface/enum kinds are
|
|
370
|
+
// considered for Java.
|
|
371
|
+
//
|
|
372
|
+
// `exported` already folds Java's public/protected/private/package-private
|
|
373
|
+
// and Kotlin's public/internal/protected/private visibility into a single
|
|
374
|
+
// boolean (see both analyzers' "Visibility mapping" header comments) --
|
|
375
|
+
// exported (public) declarations are skipped entirely here, since a public
|
|
376
|
+
// API is expected to be used from outside the scanned file set and this
|
|
377
|
+
// scan has no way to see that.
|
|
378
|
+
function findPossiblyUnreferencedJvmDeclarations(ctx) {
|
|
379
|
+
if (!ctx.sourceFacts)
|
|
380
|
+
return [];
|
|
381
|
+
const index = indexSourceFactsByPath(ctx.sourceFacts);
|
|
382
|
+
// Cross-file "this simple name was imported somewhere" signal, combined
|
|
383
|
+
// across Java and Kotlin since either language can import the other in a
|
|
384
|
+
// mixed JVM project -- the strongest signal this conservative scan can
|
|
385
|
+
// offer, same role as Python's importedNamesAcrossProject above.
|
|
386
|
+
const importedSimpleNamesAcrossProject = new Set();
|
|
387
|
+
for (const facts of index.values()) {
|
|
388
|
+
if ((facts.language !== "java" && facts.language !== "kotlin") || facts.parseStatus !== "parsed")
|
|
389
|
+
continue;
|
|
390
|
+
for (const imp of facts.imports) {
|
|
391
|
+
importedSimpleNamesAcrossProject.add(jvmImportSimpleName(imp.source));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const issues = [];
|
|
395
|
+
for (const facts of index.values()) {
|
|
396
|
+
if (facts.language !== "java" && facts.language !== "kotlin")
|
|
397
|
+
continue;
|
|
398
|
+
if (facts.role !== "source" || facts.parseStatus !== "parsed")
|
|
399
|
+
continue;
|
|
400
|
+
for (const decl of facts.declarations) {
|
|
401
|
+
if (decl.kind === "method")
|
|
402
|
+
continue; // methods/constructors -- see header comment
|
|
403
|
+
if (facts.language === "java" && decl.kind === "function")
|
|
404
|
+
continue; // never produced by javaAnalyzer.ts, defensive
|
|
405
|
+
if (decl.exported)
|
|
406
|
+
continue; // public (Java) / public-by-default-or-explicit (Kotlin)
|
|
407
|
+
if (isJvmLifecycleOrConventionalDeclarationName(decl.name))
|
|
408
|
+
continue;
|
|
409
|
+
if (importedSimpleNamesAcrossProject.has(decl.name))
|
|
410
|
+
continue;
|
|
411
|
+
const languageLabel = facts.language === "java" ? "Java" : "Kotlin";
|
|
412
|
+
issues.push(makeCodeRotIssue({
|
|
413
|
+
auditType: "code-rot",
|
|
414
|
+
detectorId: DETECTOR_ID,
|
|
415
|
+
idCues: [`unreferenced-${facts.language}-declaration`, facts.relativePath, decl.kind, decl.name],
|
|
416
|
+
title: `${languageLabel} ${decl.kind} "${decl.name}" in "${facts.relativePath}" has no detected cross-file import (weak signal)`,
|
|
417
|
+
description: `"${decl.name}" (${decl.kind}) is declared in "${facts.relativePath}" and is not exported/public, and does not appear as an imported simple name in any other scanned Java/Kotlin file. This is a deterministic static source-facts signal from a conservative, non-semantic scan -- no compiler/classpath analysis was performed, and no Gradle/Maven execution was performed, so this cannot see reflection-based access, dependency-injection wiring, framework/plugin discovery, or usage from outside the scanned project. It may indicate a candidate for removal but is not proof. Confirm manually before removing.`,
|
|
418
|
+
severity: "info",
|
|
419
|
+
confidence: "low",
|
|
420
|
+
falsePositiveRisk: "high",
|
|
421
|
+
category: "dead-code-candidate",
|
|
422
|
+
recommendedAction: "Confirm the declaration is genuinely unused before removing it.",
|
|
423
|
+
suggestedFixStrategy: `Remove "${decl.name}" from "${facts.relativePath}" if confirmed unused, or reference it from another module.`,
|
|
424
|
+
validationCommands: ["npm run audit -- --types code-rot --include cli"],
|
|
425
|
+
releaseBlocking: false,
|
|
426
|
+
implementationBlocking: false,
|
|
427
|
+
autoFixEligible: false,
|
|
428
|
+
evidence: [
|
|
429
|
+
{
|
|
430
|
+
kind: "reference",
|
|
431
|
+
message: `Source facts: the ${languageLabel} analyzer parsed this file and found no other scanned Java/Kotlin file importing the simple name "${decl.name}". Static-analysis limitation -- confidence reduced because this scan cannot see reflection/DI usage, framework discovery, or usage outside the scanned file set, and no compiler/classpath analysis was performed.`,
|
|
432
|
+
filePath: facts.relativePath,
|
|
433
|
+
source: DETECTOR_ID,
|
|
434
|
+
confidence: "low",
|
|
435
|
+
},
|
|
436
|
+
],
|
|
437
|
+
affectedFiles: [facts.relativePath],
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return issues;
|
|
442
|
+
}
|
|
253
443
|
function findOldOrDeprecatedFilesNotInDocs(ctx, docsConcat) {
|
|
254
444
|
const issues = [];
|
|
255
445
|
for (const file of ctx.inventory.files) {
|