@aiready/doc-drift 0.13.3 → 0.13.5

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.
@@ -1,6 +1,6 @@
1
1
 
2
2
  
3
- > @aiready/doc-drift@0.13.3 build /Users/pengcao/projects/aiready/packages/doc-drift
3
+ > @aiready/doc-drift@0.13.5 build /Users/pengcao/projects/aiready/packages/doc-drift
4
4
  > tsup src/index.ts src/cli.ts --format cjs,esm --dts
5
5
 
6
6
  CLI Building entry: src/cli.ts, src/index.ts
@@ -9,16 +9,16 @@
9
9
  CLI Target: es2020
10
10
  CJS Build start
11
11
  ESM Build start
12
- CJS dist/index.js 6.86 KB
13
- CJS dist/cli.js 6.48 KB
14
- CJS ⚡️ Build success in 66ms
12
+ CJS dist/cli.js 7.25 KB
13
+ CJS dist/index.js 7.85 KB
14
+ CJS ⚡️ Build success in 122ms
15
+ ESM dist/index.mjs 2.59 KB
15
16
  ESM dist/cli.mjs 1.39 KB
16
- ESM dist/index.mjs 2.37 KB
17
- ESM dist/chunk-5BGWZWHD.mjs 3.81 KB
18
- ESM ⚡️ Build success in 66ms
17
+ ESM dist/chunk-P74XAVQ3.mjs 4.54 KB
18
+ ESM ⚡️ Build success in 124ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 3596ms
20
+ DTS ⚡️ Build success in 5302ms
21
21
  DTS dist/cli.d.ts 108.00 B
22
- DTS dist/index.d.ts 2.12 KB
22
+ DTS dist/index.d.ts 2.66 KB
23
23
  DTS dist/cli.d.mts 108.00 B
24
- DTS dist/index.d.mts 2.12 KB
24
+ DTS dist/index.d.mts 2.66 KB
@@ -1,18 +1,18 @@
1
1
 
2
2
  
3
- > @aiready/doc-drift@0.13.2 test /Users/pengcao/projects/aiready/packages/doc-drift
3
+ > @aiready/doc-drift@0.13.3 test /Users/pengcao/projects/aiready/packages/doc-drift
4
4
  > vitest run
5
5
 
6
6
  [?25l
7
7
   RUN  v4.0.18 /Users/pengcao/projects/aiready/packages/doc-drift
8
8
 
9
- ✓ src/__tests__/scoring.test.ts (2 tests) 4ms
10
- ✓ src/__tests__/provider.test.ts (2 tests) 3ms
11
- ✓ src/__tests__/analyzer.test.ts (1 test) 143ms
9
+ ✓ src/__tests__/scoring.test.ts (2 tests) 12ms
10
+ ✓ src/__tests__/analyzer.test.ts (1 test) 47ms
11
+ ✓ src/__tests__/provider.test.ts (2 tests) 8ms
12
12
 
13
13
   Test Files  3 passed (3)
14
14
   Tests  5 passed (5)
15
-  Start at  22:20:40
16
-  Duration  2.77s (transform 914ms, setup 0ms, import 3.95s, tests 150ms, environment 0ms)
15
+  Start at  10:36:26
16
+  Duration  1.89s (transform 1.09s, setup 0ms, import 3.19s, tests 66ms, environment 0ms)
17
17
 
18
18
  [?25h
@@ -26,6 +26,7 @@ async function analyzeDocDrift(options) {
26
26
  let totalExports = 0;
27
27
  let outdatedComments = 0;
28
28
  let undocumentedComplexity = 0;
29
+ let actualDrift = 0;
29
30
  const now = Math.floor(Date.now() / 1e3);
30
31
  let processed = 0;
31
32
  for (const file of files) {
@@ -75,10 +76,9 @@ async function analyzeDocDrift(options) {
75
76
  message: `Documentation mismatch: function parameters (${missingParams.join(", ")}) are not mentioned in the docs.`,
76
77
  location: { file, line: exp.loc?.start.line || 1 }
77
78
  });
78
- continue;
79
79
  }
80
80
  }
81
- if (exp.loc) {
81
+ if (exp.loc && doc.loc) {
82
82
  if (!fileLineStamps) {
83
83
  fileLineStamps = getFileCommitTimestamps(file);
84
84
  }
@@ -87,8 +87,21 @@ async function analyzeDocDrift(options) {
87
87
  exp.loc.start.line,
88
88
  exp.loc.end.line
89
89
  );
90
- if (bodyModified > 0) {
91
- if (now - bodyModified < staleSeconds / 4 && exp.documentation.isStale === true) {
90
+ const docModified = getLineRangeLastModifiedCached(
91
+ fileLineStamps,
92
+ doc.loc.start.line,
93
+ doc.loc.end.line
94
+ );
95
+ if (bodyModified > 0 && docModified > 0) {
96
+ const DRIFT_THRESHOLD_SECONDS = 24 * 60 * 60;
97
+ if (bodyModified - docModified > DRIFT_THRESHOLD_SECONDS) {
98
+ actualDrift++;
99
+ issues.push({
100
+ type: IssueType.DocDrift,
101
+ severity: Severity.Major,
102
+ message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1e3).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1e3).toLocaleDateString()}.`,
103
+ location: { file, line: doc.loc.start.line }
104
+ });
92
105
  }
93
106
  }
94
107
  }
@@ -104,7 +117,8 @@ async function analyzeDocDrift(options) {
104
117
  uncommentedExports,
105
118
  totalExports,
106
119
  outdatedComments,
107
- undocumentedComplexity
120
+ undocumentedComplexity,
121
+ actualDrift
108
122
  });
109
123
  return {
110
124
  summary: {
@@ -118,7 +132,8 @@ async function analyzeDocDrift(options) {
118
132
  uncommentedExports,
119
133
  totalExports,
120
134
  outdatedComments,
121
- undocumentedComplexity
135
+ undocumentedComplexity,
136
+ actualDrift
122
137
  },
123
138
  recommendations: riskResult.recommendations
124
139
  };
package/dist/cli.js CHANGED
@@ -47,6 +47,7 @@ async function analyzeDocDrift(options) {
47
47
  let totalExports = 0;
48
48
  let outdatedComments = 0;
49
49
  let undocumentedComplexity = 0;
50
+ let actualDrift = 0;
50
51
  const now = Math.floor(Date.now() / 1e3);
51
52
  let processed = 0;
52
53
  for (const file of files) {
@@ -96,10 +97,9 @@ async function analyzeDocDrift(options) {
96
97
  message: `Documentation mismatch: function parameters (${missingParams.join(", ")}) are not mentioned in the docs.`,
97
98
  location: { file, line: exp.loc?.start.line || 1 }
98
99
  });
99
- continue;
100
100
  }
101
101
  }
102
- if (exp.loc) {
102
+ if (exp.loc && doc.loc) {
103
103
  if (!fileLineStamps) {
104
104
  fileLineStamps = (0, import_core.getFileCommitTimestamps)(file);
105
105
  }
@@ -108,8 +108,21 @@ async function analyzeDocDrift(options) {
108
108
  exp.loc.start.line,
109
109
  exp.loc.end.line
110
110
  );
111
- if (bodyModified > 0) {
112
- if (now - bodyModified < staleSeconds / 4 && exp.documentation.isStale === true) {
111
+ const docModified = (0, import_core.getLineRangeLastModifiedCached)(
112
+ fileLineStamps,
113
+ doc.loc.start.line,
114
+ doc.loc.end.line
115
+ );
116
+ if (bodyModified > 0 && docModified > 0) {
117
+ const DRIFT_THRESHOLD_SECONDS = 24 * 60 * 60;
118
+ if (bodyModified - docModified > DRIFT_THRESHOLD_SECONDS) {
119
+ actualDrift++;
120
+ issues.push({
121
+ type: import_core.IssueType.DocDrift,
122
+ severity: import_core.Severity.Major,
123
+ message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1e3).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1e3).toLocaleDateString()}.`,
124
+ location: { file, line: doc.loc.start.line }
125
+ });
113
126
  }
114
127
  }
115
128
  }
@@ -125,7 +138,8 @@ async function analyzeDocDrift(options) {
125
138
  uncommentedExports,
126
139
  totalExports,
127
140
  outdatedComments,
128
- undocumentedComplexity
141
+ undocumentedComplexity,
142
+ actualDrift
129
143
  });
130
144
  return {
131
145
  summary: {
@@ -139,7 +153,8 @@ async function analyzeDocDrift(options) {
139
153
  uncommentedExports,
140
154
  totalExports,
141
155
  outdatedComments,
142
- undocumentedComplexity
156
+ undocumentedComplexity,
157
+ actualDrift
143
158
  },
144
159
  recommendations: riskResult.recommendations
145
160
  };
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __require,
3
3
  analyzeDocDrift
4
- } from "./chunk-5BGWZWHD.mjs";
4
+ } from "./chunk-P74XAVQ3.mjs";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";
package/dist/index.d.mts CHANGED
@@ -49,11 +49,23 @@ interface DocDriftReport {
49
49
  outdatedComments: number;
50
50
  /** Count of complex functions without sufficient documentation */
51
51
  undocumentedComplexity: number;
52
+ /** Number of functions where code changed after docs */
53
+ actualDrift: number;
52
54
  };
53
55
  /** AI-generated remediation advice */
54
56
  recommendations: string[];
55
57
  }
56
58
 
59
+ /**
60
+ * Analyzes documentation drift across a set of files.
61
+ * This tool detects:
62
+ * 1. Missing documentation for complex functions/classes.
63
+ * 2. Signature mismatches (parameters not mentioned in docs).
64
+ * 3. Temporal drift (logic changed after documentation was last updated).
65
+ *
66
+ * @param options - Analysis configuration including include/exclude patterns and drift thresholds.
67
+ * @returns A comprehensive report with drift scores and specific issues.
68
+ */
57
69
  declare function analyzeDocDrift(options: DocDriftOptions): Promise<DocDriftReport>;
58
70
 
59
71
  /**
package/dist/index.d.ts CHANGED
@@ -49,11 +49,23 @@ interface DocDriftReport {
49
49
  outdatedComments: number;
50
50
  /** Count of complex functions without sufficient documentation */
51
51
  undocumentedComplexity: number;
52
+ /** Number of functions where code changed after docs */
53
+ actualDrift: number;
52
54
  };
53
55
  /** AI-generated remediation advice */
54
56
  recommendations: string[];
55
57
  }
56
58
 
59
+ /**
60
+ * Analyzes documentation drift across a set of files.
61
+ * This tool detects:
62
+ * 1. Missing documentation for complex functions/classes.
63
+ * 2. Signature mismatches (parameters not mentioned in docs).
64
+ * 3. Temporal drift (logic changed after documentation was last updated).
65
+ *
66
+ * @param options - Analysis configuration including include/exclude patterns and drift thresholds.
67
+ * @returns A comprehensive report with drift scores and specific issues.
68
+ */
57
69
  declare function analyzeDocDrift(options: DocDriftOptions): Promise<DocDriftReport>;
58
70
 
59
71
  /**
package/dist/index.js CHANGED
@@ -42,6 +42,7 @@ async function analyzeDocDrift(options) {
42
42
  let totalExports = 0;
43
43
  let outdatedComments = 0;
44
44
  let undocumentedComplexity = 0;
45
+ let actualDrift = 0;
45
46
  const now = Math.floor(Date.now() / 1e3);
46
47
  let processed = 0;
47
48
  for (const file of files) {
@@ -91,10 +92,9 @@ async function analyzeDocDrift(options) {
91
92
  message: `Documentation mismatch: function parameters (${missingParams.join(", ")}) are not mentioned in the docs.`,
92
93
  location: { file, line: exp.loc?.start.line || 1 }
93
94
  });
94
- continue;
95
95
  }
96
96
  }
97
- if (exp.loc) {
97
+ if (exp.loc && doc.loc) {
98
98
  if (!fileLineStamps) {
99
99
  fileLineStamps = (0, import_core.getFileCommitTimestamps)(file);
100
100
  }
@@ -103,8 +103,21 @@ async function analyzeDocDrift(options) {
103
103
  exp.loc.start.line,
104
104
  exp.loc.end.line
105
105
  );
106
- if (bodyModified > 0) {
107
- if (now - bodyModified < staleSeconds / 4 && exp.documentation.isStale === true) {
106
+ const docModified = (0, import_core.getLineRangeLastModifiedCached)(
107
+ fileLineStamps,
108
+ doc.loc.start.line,
109
+ doc.loc.end.line
110
+ );
111
+ if (bodyModified > 0 && docModified > 0) {
112
+ const DRIFT_THRESHOLD_SECONDS = 24 * 60 * 60;
113
+ if (bodyModified - docModified > DRIFT_THRESHOLD_SECONDS) {
114
+ actualDrift++;
115
+ issues.push({
116
+ type: import_core.IssueType.DocDrift,
117
+ severity: import_core.Severity.Major,
118
+ message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1e3).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1e3).toLocaleDateString()}.`,
119
+ location: { file, line: doc.loc.start.line }
120
+ });
108
121
  }
109
122
  }
110
123
  }
@@ -120,7 +133,8 @@ async function analyzeDocDrift(options) {
120
133
  uncommentedExports,
121
134
  totalExports,
122
135
  outdatedComments,
123
- undocumentedComplexity
136
+ undocumentedComplexity,
137
+ actualDrift
124
138
  });
125
139
  return {
126
140
  summary: {
@@ -134,7 +148,8 @@ async function analyzeDocDrift(options) {
134
148
  uncommentedExports,
135
149
  totalExports,
136
150
  outdatedComments,
137
- undocumentedComplexity
151
+ undocumentedComplexity,
152
+ actualDrift
138
153
  },
139
154
  recommendations: riskResult.recommendations
140
155
  };
@@ -173,26 +188,33 @@ function calculateDocDriftScore(report) {
173
188
  uncommentedExports: rawData.uncommentedExports,
174
189
  totalExports: rawData.totalExports,
175
190
  outdatedComments: rawData.outdatedComments,
176
- undocumentedComplexity: rawData.undocumentedComplexity
191
+ undocumentedComplexity: rawData.undocumentedComplexity,
192
+ actualDrift: rawData.actualDrift
177
193
  });
178
194
  const factors = [
179
195
  {
180
- name: "Uncommented Exports",
196
+ name: "Undocumented Complexity",
181
197
  impact: -Math.min(
182
- 20,
183
- rawData.uncommentedExports / Math.max(1, rawData.totalExports) * 100 * 0.2
198
+ 50,
199
+ rawData.undocumentedComplexity / Math.max(1, rawData.totalExports) / 0.2 * 100 * 0.5
184
200
  ),
185
- description: `${rawData.uncommentedExports} uncommented exports`
201
+ description: `${rawData.undocumentedComplexity} complex functions lack docs (high risk)`
186
202
  },
187
203
  {
188
- name: "Outdated Comments",
189
- impact: -Math.min(60, rawData.outdatedComments * 15 * 0.6),
190
- description: `${rawData.outdatedComments} outdated comments`
204
+ name: "Outdated/Incomplete Comments",
205
+ impact: -Math.min(
206
+ 30,
207
+ rawData.outdatedComments / Math.max(1, rawData.totalExports) / 0.4 * 100 * 0.3
208
+ ),
209
+ description: `${rawData.outdatedComments} functions with parameter-mismatch in docs`
191
210
  },
192
211
  {
193
- name: "Undocumented Complexity",
194
- impact: -Math.min(20, rawData.undocumentedComplexity * 10 * 0.2),
195
- description: `${rawData.undocumentedComplexity} complex functions lack docs`
212
+ name: "Uncommented Exports",
213
+ impact: -Math.min(
214
+ 20,
215
+ rawData.uncommentedExports / Math.max(1, rawData.totalExports) / 0.8 * 100 * 0.2
216
+ ),
217
+ description: `${rawData.uncommentedExports} uncommented exports`
196
218
  }
197
219
  ];
198
220
  const recommendations = riskResult.recommendations.map((rec) => ({
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  analyzeDocDrift
3
- } from "./chunk-5BGWZWHD.mjs";
3
+ } from "./chunk-P74XAVQ3.mjs";
4
4
 
5
5
  // src/index.ts
6
6
  import { ToolRegistry } from "@aiready/core";
@@ -44,26 +44,33 @@ function calculateDocDriftScore(report) {
44
44
  uncommentedExports: rawData.uncommentedExports,
45
45
  totalExports: rawData.totalExports,
46
46
  outdatedComments: rawData.outdatedComments,
47
- undocumentedComplexity: rawData.undocumentedComplexity
47
+ undocumentedComplexity: rawData.undocumentedComplexity,
48
+ actualDrift: rawData.actualDrift
48
49
  });
49
50
  const factors = [
50
51
  {
51
- name: "Uncommented Exports",
52
+ name: "Undocumented Complexity",
52
53
  impact: -Math.min(
53
- 20,
54
- rawData.uncommentedExports / Math.max(1, rawData.totalExports) * 100 * 0.2
54
+ 50,
55
+ rawData.undocumentedComplexity / Math.max(1, rawData.totalExports) / 0.2 * 100 * 0.5
55
56
  ),
56
- description: `${rawData.uncommentedExports} uncommented exports`
57
+ description: `${rawData.undocumentedComplexity} complex functions lack docs (high risk)`
57
58
  },
58
59
  {
59
- name: "Outdated Comments",
60
- impact: -Math.min(60, rawData.outdatedComments * 15 * 0.6),
61
- description: `${rawData.outdatedComments} outdated comments`
60
+ name: "Outdated/Incomplete Comments",
61
+ impact: -Math.min(
62
+ 30,
63
+ rawData.outdatedComments / Math.max(1, rawData.totalExports) / 0.4 * 100 * 0.3
64
+ ),
65
+ description: `${rawData.outdatedComments} functions with parameter-mismatch in docs`
62
66
  },
63
67
  {
64
- name: "Undocumented Complexity",
65
- impact: -Math.min(20, rawData.undocumentedComplexity * 10 * 0.2),
66
- description: `${rawData.undocumentedComplexity} complex functions lack docs`
68
+ name: "Uncommented Exports",
69
+ impact: -Math.min(
70
+ 20,
71
+ rawData.uncommentedExports / Math.max(1, rawData.totalExports) / 0.8 * 100 * 0.2
72
+ ),
73
+ description: `${rawData.uncommentedExports} uncommented exports`
67
74
  }
68
75
  ];
69
76
  const recommendations = riskResult.recommendations.map((rec) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/doc-drift",
3
- "version": "0.13.3",
3
+ "version": "0.13.5",
4
4
  "description": "AI-Readiness: Documentation Drift Detection",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -10,7 +10,7 @@
10
10
  "commander": "^14.0.0",
11
11
  "glob": "^13.0.0",
12
12
  "picocolors": "^1.0.0",
13
- "@aiready/core": "0.23.3"
13
+ "@aiready/core": "0.23.6"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^24.0.0",
@@ -76,6 +76,7 @@ export function complexFunction(data: any) {
76
76
  expect(report.rawData.uncommentedExports).toBe(1);
77
77
  expect(report.rawData.outdatedComments).toBe(1);
78
78
  expect(report.rawData.undocumentedComplexity).toBe(1);
79
+ expect(report.rawData.actualDrift).toBe(0);
79
80
 
80
81
  expect(report.issues.length).toBeGreaterThan(0);
81
82
  expect(report.issues.some((i) => i.message.includes('b'))).toBe(true);
@@ -21,6 +21,7 @@ describe('Doc Drift Provider', () => {
21
21
  totalExports: 5,
22
22
  outdatedComments: 0,
23
23
  undocumentedComplexity: 0,
24
+ actualDrift: 0,
24
25
  },
25
26
  recommendations: [],
26
27
  });
@@ -17,6 +17,7 @@ describe('Doc Drift Scoring', () => {
17
17
  totalExports: 50,
18
18
  outdatedComments: 2,
19
19
  undocumentedComplexity: 1,
20
+ actualDrift: 0,
20
21
  },
21
22
  recommendations: [
22
23
  'Update or remove 2 outdated comments that contradict the code.',
package/src/analyzer.ts CHANGED
@@ -11,6 +11,16 @@ import {
11
11
  import type { DocDriftOptions, DocDriftReport, DocDriftIssue } from './types';
12
12
  import { readFileSync } from 'fs';
13
13
 
14
+ /**
15
+ * Analyzes documentation drift across a set of files.
16
+ * This tool detects:
17
+ * 1. Missing documentation for complex functions/classes.
18
+ * 2. Signature mismatches (parameters not mentioned in docs).
19
+ * 3. Temporal drift (logic changed after documentation was last updated).
20
+ *
21
+ * @param options - Analysis configuration including include/exclude patterns and drift thresholds.
22
+ * @returns A comprehensive report with drift scores and specific issues.
23
+ */
14
24
  export async function analyzeDocDrift(
15
25
  options: DocDriftOptions
16
26
  ): Promise<DocDriftReport> {
@@ -24,6 +34,7 @@ export async function analyzeDocDrift(
24
34
  let totalExports = 0;
25
35
  let outdatedComments = 0;
26
36
  let undocumentedComplexity = 0;
37
+ let actualDrift = 0;
27
38
 
28
39
  const now = Math.floor(Date.now() / 1000);
29
40
 
@@ -76,7 +87,6 @@ export async function analyzeDocDrift(
76
87
  if (exp.type === 'function' && exp.parameters) {
77
88
  const params = exp.parameters;
78
89
  // Check if params mentioned in doc (standard @param or simple mention)
79
- // Use regex with word boundaries to avoid partial matches (e.g. 'b' in 'numbers')
80
90
  const missingParams = params.filter((p) => {
81
91
  const regex = new RegExp(`\\b${p}\\b`, 'i');
82
92
  return !regex.test(docContent);
@@ -90,36 +100,39 @@ export async function analyzeDocDrift(
90
100
  message: `Documentation mismatch: function parameters (${missingParams.join(', ')}) are not mentioned in the docs.`,
91
101
  location: { file, line: exp.loc?.start.line || 1 },
92
102
  });
93
- continue;
94
103
  }
95
104
  }
96
105
 
97
- // Timestamp comparison
98
- if (exp.loc) {
106
+ // Timestamp comparison for temporal drift
107
+ if (exp.loc && doc.loc) {
99
108
  if (!fileLineStamps) {
100
109
  fileLineStamps = getFileCommitTimestamps(file);
101
110
  }
102
111
 
103
- // We don't have exact lines for the doc node in ExportInfo yet,
104
- // but we know it precedes the export. Using export start as a proxy for drift check.
105
112
  const bodyModified = getLineRangeLastModifiedCached(
106
113
  fileLineStamps,
107
114
  exp.loc.start.line,
108
115
  exp.loc.end.line
109
116
  );
110
117
 
111
- if (bodyModified > 0) {
112
- // If body was modified much later than the "stale" threshold
113
- if (
114
- now - bodyModified < staleSeconds / 4 &&
115
- exp.documentation.isStale === true
116
- ) {
117
- // This would require isStale to be set by the parser if it knew history
118
- // For now, we compare body modification vs current time if docs look very old (heuristic)
119
- }
118
+ const docModified = getLineRangeLastModifiedCached(
119
+ fileLineStamps,
120
+ doc.loc.start.line,
121
+ doc.loc.end.line
122
+ );
120
123
 
121
- // If the file itself is very old but has no issues, it's fine.
122
- // Doc-drift is really about implementation changing without doc updates.
124
+ if (bodyModified > 0 && docModified > 0) {
125
+ // If body was modified more than 1 day AFTER the documentation
126
+ const DRIFT_THRESHOLD_SECONDS = 24 * 60 * 60;
127
+ if (bodyModified - docModified > DRIFT_THRESHOLD_SECONDS) {
128
+ actualDrift++;
129
+ issues.push({
130
+ type: IssueType.DocDrift,
131
+ severity: Severity.Major,
132
+ message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1000).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1000).toLocaleDateString()}.`,
133
+ location: { file, line: doc.loc.start.line },
134
+ });
135
+ }
123
136
  }
124
137
  }
125
138
  }
@@ -136,6 +149,7 @@ export async function analyzeDocDrift(
136
149
  totalExports,
137
150
  outdatedComments,
138
151
  undocumentedComplexity,
152
+ actualDrift,
139
153
  });
140
154
 
141
155
  return {
@@ -151,6 +165,7 @@ export async function analyzeDocDrift(
151
165
  totalExports,
152
166
  outdatedComments,
153
167
  undocumentedComplexity,
168
+ actualDrift,
154
169
  },
155
170
  recommendations: riskResult.recommendations,
156
171
  };
package/src/scoring.ts CHANGED
@@ -16,28 +16,41 @@ export function calculateDocDriftScore(
16
16
  totalExports: rawData.totalExports,
17
17
  outdatedComments: rawData.outdatedComments,
18
18
  undocumentedComplexity: rawData.undocumentedComplexity,
19
+ actualDrift: rawData.actualDrift,
19
20
  });
20
21
 
21
22
  const factors: ToolScoringOutput['factors'] = [
22
23
  {
23
- name: 'Uncommented Exports',
24
+ name: 'Undocumented Complexity',
24
25
  impact: -Math.min(
25
- 20,
26
- (rawData.uncommentedExports / Math.max(1, rawData.totalExports)) *
26
+ 50,
27
+ (rawData.undocumentedComplexity /
28
+ Math.max(1, rawData.totalExports) /
29
+ 0.2) *
27
30
  100 *
28
- 0.2
31
+ 0.5
29
32
  ),
30
- description: `${rawData.uncommentedExports} uncommented exports`,
33
+ description: `${rawData.undocumentedComplexity} complex functions lack docs (high risk)`,
31
34
  },
32
35
  {
33
- name: 'Outdated Comments',
34
- impact: -Math.min(60, rawData.outdatedComments * 15 * 0.6),
35
- description: `${rawData.outdatedComments} outdated comments`,
36
+ name: 'Outdated/Incomplete Comments',
37
+ impact: -Math.min(
38
+ 30,
39
+ (rawData.outdatedComments / Math.max(1, rawData.totalExports) / 0.4) *
40
+ 100 *
41
+ 0.3
42
+ ),
43
+ description: `${rawData.outdatedComments} functions with parameter-mismatch in docs`,
36
44
  },
37
45
  {
38
- name: 'Undocumented Complexity',
39
- impact: -Math.min(20, rawData.undocumentedComplexity * 10 * 0.2),
40
- description: `${rawData.undocumentedComplexity} complex functions lack docs`,
46
+ name: 'Uncommented Exports',
47
+ impact: -Math.min(
48
+ 20,
49
+ (rawData.uncommentedExports / Math.max(1, rawData.totalExports) / 0.8) *
50
+ 100 *
51
+ 0.2
52
+ ),
53
+ description: `${rawData.uncommentedExports} uncommented exports`,
41
54
  },
42
55
  ];
43
56
 
package/src/types.ts CHANGED
@@ -50,6 +50,8 @@ export interface DocDriftReport {
50
50
  outdatedComments: number;
51
51
  /** Count of complex functions without sufficient documentation */
52
52
  undocumentedComplexity: number;
53
+ /** Number of functions where code changed after docs */
54
+ actualDrift: number;
53
55
  };
54
56
  /** AI-generated remediation advice */
55
57
  recommendations: string[];