@aiready/doc-drift 0.14.14 → 0.14.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -32,13 +32,258 @@ var import_core2 = require("@aiready/core");
32
32
  // src/analyzer.ts
33
33
  var import_core = require("@aiready/core");
34
34
  var import_fs = require("fs");
35
+
36
+ // src/constants.ts
37
+ var DESCRIPTIVE_PARAMS = /* @__PURE__ */ new Set([
38
+ "data",
39
+ "item",
40
+ "value",
41
+ "key",
42
+ "index",
43
+ "id",
44
+ "name",
45
+ "type",
46
+ "config",
47
+ "options",
48
+ "params",
49
+ "args",
50
+ "input",
51
+ "output",
52
+ "result",
53
+ "response",
54
+ "request",
55
+ "callback",
56
+ "handler",
57
+ "event",
58
+ "error",
59
+ "message",
60
+ "context",
61
+ "file",
62
+ "path",
63
+ "url",
64
+ "source",
65
+ "target",
66
+ "content",
67
+ "body",
68
+ "payload",
69
+ "user",
70
+ "userid",
71
+ "user_id",
72
+ "starttime",
73
+ "endtime",
74
+ "duration",
75
+ "timeout",
76
+ "retry",
77
+ "count",
78
+ "limit",
79
+ "offset",
80
+ "page",
81
+ "size",
82
+ "length",
83
+ "width",
84
+ "height",
85
+ "depth",
86
+ "color",
87
+ "background",
88
+ "foreground",
89
+ "border",
90
+ "margin",
91
+ "padding",
92
+ "position",
93
+ "top",
94
+ "bottom",
95
+ "left",
96
+ "right",
97
+ "center",
98
+ "start",
99
+ "end",
100
+ "begin",
101
+ "finish",
102
+ "complete",
103
+ "pending",
104
+ "active",
105
+ "inactive",
106
+ "enabled",
107
+ "disabled",
108
+ "visible",
109
+ "hidden",
110
+ "open",
111
+ "closed",
112
+ "locked",
113
+ "unlocked",
114
+ "read",
115
+ "write",
116
+ "append",
117
+ "prepend",
118
+ "insert",
119
+ "replace",
120
+ "merge",
121
+ "split",
122
+ "join",
123
+ "filter",
124
+ "map",
125
+ "reduce",
126
+ "find",
127
+ "sort",
128
+ "reverse",
129
+ "unique",
130
+ "flatten",
131
+ "compact",
132
+ "chunk",
133
+ "zip",
134
+ "unzip",
135
+ "completed",
136
+ "failed",
137
+ "healthy",
138
+ "high",
139
+ "medium",
140
+ "low",
141
+ "running",
142
+ "stopped",
143
+ "title",
144
+ "subtitle",
145
+ "label",
146
+ "description",
147
+ "version",
148
+ "timestamp",
149
+ "date",
150
+ "time",
151
+ "status",
152
+ "mode",
153
+ "action",
154
+ "effect",
155
+ "resource",
156
+ "principal",
157
+ "statement",
158
+ "sid",
159
+ "allow",
160
+ "deny",
161
+ "roi",
162
+ "unifiedbudget",
163
+ "filepath",
164
+ "rootdir",
165
+ "fp",
166
+ "email",
167
+ "username",
168
+ "password",
169
+ "token",
170
+ "secret",
171
+ "apikey",
172
+ "api_key",
173
+ "baseurl",
174
+ "base_url",
175
+ "endpoint",
176
+ "method",
177
+ "headers",
178
+ "queryparams",
179
+ "query_params",
180
+ "bodyparams",
181
+ "body_params",
182
+ "formdata",
183
+ "form_data",
184
+ "filename",
185
+ "file_name",
186
+ "filetype",
187
+ "file_type",
188
+ "filesize",
189
+ "file_size",
190
+ "filecontent",
191
+ "file_content",
192
+ "fileurl",
193
+ "file_url",
194
+ "fileid",
195
+ "file_id",
196
+ "filekey",
197
+ "file_key",
198
+ "filepath",
199
+ "file_path",
200
+ "filedir",
201
+ "file_dir",
202
+ "fileext",
203
+ "file_ext",
204
+ "filebase",
205
+ "file_base",
206
+ "filenamewithoutext",
207
+ "file_name_without_ext",
208
+ "filedirname",
209
+ "file_dirname",
210
+ "filebasename",
211
+ "file_basename",
212
+ "fileextname",
213
+ "file_extname",
214
+ "fileroot",
215
+ "file_root",
216
+ "filesep",
217
+ "file_sep",
218
+ "filejoin",
219
+ "file_join",
220
+ "fileresolve",
221
+ "file_resolve",
222
+ "filenormalize",
223
+ "file_normalize",
224
+ "exp",
225
+ "ctx",
226
+ "options",
227
+ "done",
228
+ "next",
229
+ "reject",
230
+ "resolve",
231
+ "error",
232
+ "err",
233
+ "req",
234
+ "res",
235
+ "event",
236
+ "payload",
237
+ "metadata",
238
+ "params",
239
+ "props",
240
+ "state",
241
+ "dispatch",
242
+ "action",
243
+ "config",
244
+ "directory",
245
+ "useroptions",
246
+ "resultslength",
247
+ "totalissues",
248
+ "totaltokencost",
249
+ "elapsedtime"
250
+ ]);
251
+
252
+ // src/heuristics.ts
253
+ function getMissingParams(exp) {
254
+ if (exp.type !== "function" || !exp.parameters || !exp.documentation) {
255
+ return [];
256
+ }
257
+ const docContent = exp.documentation.content;
258
+ const params = exp.parameters;
259
+ return params.filter((p) => {
260
+ if (DESCRIPTIVE_PARAMS.has(p.toLowerCase())) {
261
+ return false;
262
+ }
263
+ const regex = new RegExp(`\\b${p}\\b`);
264
+ return !regex.test(docContent);
265
+ });
266
+ }
267
+ function isUndocumentedComplexity(exp) {
268
+ if (exp.documentation) return false;
269
+ if (!exp.loc) return false;
270
+ const lines = exp.loc.end.line - exp.loc.start.line;
271
+ return lines > 20;
272
+ }
273
+ function hasTemporalDrift(bodyModified, docModified) {
274
+ if (bodyModified <= 0 || docModified <= 0) return false;
275
+ const DRIFT_THRESHOLD_SECONDS = 24 * 60 * 60;
276
+ return bodyModified - docModified > DRIFT_THRESHOLD_SECONDS;
277
+ }
278
+
279
+ // src/analyzer.ts
35
280
  async function analyzeDocDrift(options) {
36
281
  const files = await (0, import_core.scanFiles)(options);
37
282
  const issues = [];
38
283
  let uncommentedExports = 0;
39
284
  let totalExports = 0;
40
285
  let outdatedComments = 0;
41
- let undocumentedComplexity = 0;
286
+ let undocumentedComplexityCount = 0;
42
287
  let actualDrift = 0;
43
288
  let processed = 0;
44
289
  for (const file of files) {
@@ -67,33 +312,22 @@ async function analyzeDocDrift(options) {
67
312
  totalExports++;
68
313
  if (!exp.documentation) {
69
314
  uncommentedExports++;
70
- if (exp.loc) {
71
- const lines = exp.loc.end.line - exp.loc.start.line;
72
- if (lines > 20) undocumentedComplexity++;
73
- }
315
+ if (isUndocumentedComplexity(exp)) undocumentedComplexityCount++;
74
316
  } else {
75
317
  const doc = exp.documentation;
76
- const docContent = doc.content;
77
- if (exp.type === "function" && exp.parameters) {
78
- const params = exp.parameters;
79
- const missingParams = params.filter((p) => {
80
- const regex = new RegExp(`\\b${p}\\b`);
81
- return !regex.test(docContent);
318
+ const missingParams = getMissingParams(exp);
319
+ if (missingParams.length > 0) {
320
+ outdatedComments++;
321
+ issues.push({
322
+ type: import_core.IssueType.DocDrift,
323
+ severity: import_core.Severity.Major,
324
+ message: `Documentation mismatch: function parameters (${missingParams.join(", ")}) are not mentioned in the docs.`,
325
+ location: { file, line: exp.loc?.start.line || 1 }
82
326
  });
83
- if (missingParams.length > 0) {
84
- outdatedComments++;
85
- issues.push({
86
- type: import_core.IssueType.DocDrift,
87
- severity: import_core.Severity.Major,
88
- message: `Documentation mismatch: function parameters (${missingParams.join(", ")}) are not mentioned in the docs.`,
89
- location: { file, line: exp.loc?.start.line || 1 }
90
- });
91
- }
92
327
  }
93
328
  if (exp.loc && doc.loc) {
94
- if (!fileLineStamps) {
329
+ if (!fileLineStamps)
95
330
  fileLineStamps = (0, import_core.getFileCommitTimestamps)(file);
96
- }
97
331
  const bodyModified = (0, import_core.getLineRangeLastModifiedCached)(
98
332
  fileLineStamps,
99
333
  exp.loc.start.line,
@@ -104,17 +338,14 @@ async function analyzeDocDrift(options) {
104
338
  doc.loc.start.line,
105
339
  doc.loc.end.line
106
340
  );
107
- if (bodyModified > 0 && docModified > 0) {
108
- const DRIFT_THRESHOLD_SECONDS = 24 * 60 * 60;
109
- if (bodyModified - docModified > DRIFT_THRESHOLD_SECONDS) {
110
- actualDrift++;
111
- issues.push({
112
- type: import_core.IssueType.DocDrift,
113
- severity: import_core.Severity.Major,
114
- message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1e3).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1e3).toLocaleDateString()}.`,
115
- location: { file, line: doc.loc.start.line }
116
- });
117
- }
341
+ if (hasTemporalDrift(bodyModified, docModified)) {
342
+ actualDrift++;
343
+ issues.push({
344
+ type: import_core.IssueType.DocDrift,
345
+ severity: import_core.Severity.Major,
346
+ message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1e3).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1e3).toLocaleDateString()}.`,
347
+ location: { file, line: doc.loc.start.line }
348
+ });
118
349
  }
119
350
  }
120
351
  }
@@ -129,7 +360,7 @@ async function analyzeDocDrift(options) {
129
360
  uncommentedExports,
130
361
  totalExports,
131
362
  outdatedComments,
132
- undocumentedComplexity,
363
+ undocumentedComplexity: undocumentedComplexityCount,
133
364
  actualDrift
134
365
  });
135
366
  return {
@@ -144,7 +375,7 @@ async function analyzeDocDrift(options) {
144
375
  uncommentedExports,
145
376
  totalExports,
146
377
  outdatedComments,
147
- undocumentedComplexity,
378
+ undocumentedComplexity: undocumentedComplexityCount,
148
379
  actualDrift
149
380
  },
150
381
  recommendations: riskResult.recommendations
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  analyzeDocDrift
3
- } from "./chunk-GZDN7B4K.mjs";
3
+ } from "./chunk-IQBAXGOK.mjs";
4
4
 
5
5
  // src/index.ts
6
6
  import { ToolRegistry } from "@aiready/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiready/doc-drift",
3
- "version": "0.14.14",
3
+ "version": "0.14.16",
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.24.15"
13
+ "@aiready/core": "0.24.19"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^24.0.0",
@@ -33,6 +33,8 @@
33
33
  "test:watch": "vitest",
34
34
  "lint": "eslint src --ext .ts",
35
35
  "type-check": "tsc --noEmit",
36
- "format-check": "prettier --check . --ignore-path ../../.prettierignore"
36
+ "format-check": "prettier --check . --ignore-path ../../.prettierignore",
37
+ "format": "prettier --write . --ignore-path ../../.prettierignore",
38
+ "lint:fix": "eslint . --fix"
37
39
  }
38
40
  }
package/src/analyzer.ts CHANGED
@@ -10,33 +10,27 @@ import {
10
10
  } from '@aiready/core';
11
11
  import type { DocDriftOptions, DocDriftReport, DocDriftIssue } from './types';
12
12
  import { readFileSync } from 'fs';
13
+ import {
14
+ getMissingParams,
15
+ isUndocumentedComplexity,
16
+ hasTemporalDrift,
17
+ } from './heuristics';
13
18
 
14
19
  /**
15
20
  * 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
21
  */
24
22
  export async function analyzeDocDrift(
25
23
  options: DocDriftOptions
26
24
  ): Promise<DocDriftReport> {
27
- // Use core scanFiles which respects .gitignore recursively
28
25
  const files = await scanFiles(options);
29
26
  const issues: DocDriftIssue[] = [];
30
- // const staleSeconds = staleMonths * 30 * 24 * 60 * 60; // Unused, removed
31
27
 
32
28
  let uncommentedExports = 0;
33
29
  let totalExports = 0;
34
30
  let outdatedComments = 0;
35
- let undocumentedComplexity = 0;
31
+ let undocumentedComplexityCount = 0;
36
32
  let actualDrift = 0;
37
33
 
38
- // const now = Math.floor(Date.now() / 1000); // Unused, removed
39
-
40
34
  let processed = 0;
41
35
  for (const file of files) {
42
36
  processed++;
@@ -59,79 +53,57 @@ export async function analyzeDocDrift(
59
53
  }
60
54
 
61
55
  try {
62
- // Initialize parser (it's a singleton in core, but ensures WASM is loaded)
63
56
  await parser.initialize();
64
57
  const parseResult = parser.parse(code, file);
65
58
 
66
59
  let fileLineStamps: Record<number, number> | undefined;
67
60
 
68
61
  for (const exp of parseResult.exports) {
69
- // Only analyze functions and classes for documentation drift
70
62
  if (exp.type === 'function' || exp.type === 'class') {
71
63
  totalExports++;
72
64
 
73
65
  if (!exp.documentation) {
74
66
  uncommentedExports++;
75
-
76
- // Complexity check (heuristic based on line count if range available)
77
- if (exp.loc) {
78
- const lines = exp.loc.end.line - exp.loc.start.line;
79
- if (lines > 20) undocumentedComplexity++;
80
- }
67
+ if (isUndocumentedComplexity(exp)) undocumentedComplexityCount++;
81
68
  } else {
82
69
  const doc = exp.documentation;
83
- const docContent = doc.content;
84
70
 
85
- // Signature mismatch detection (generalized heuristic)
86
- if (exp.type === 'function' && exp.parameters) {
87
- const params = exp.parameters;
88
- // Check if params mentioned in doc (standard @param or simple mention)
89
- const missingParams = params.filter((p) => {
90
- const regex = new RegExp(`\\b${p}\\b`);
91
- return !regex.test(docContent);
71
+ // Check for missing parameters in documentation
72
+ const missingParams = getMissingParams(exp);
73
+ if (missingParams.length > 0) {
74
+ outdatedComments++;
75
+ issues.push({
76
+ type: IssueType.DocDrift,
77
+ severity: Severity.Major,
78
+ message: `Documentation mismatch: function parameters (${missingParams.join(', ')}) are not mentioned in the docs.`,
79
+ location: { file, line: exp.loc?.start.line || 1 },
92
80
  });
93
-
94
- if (missingParams.length > 0) {
95
- outdatedComments++;
96
- issues.push({
97
- type: IssueType.DocDrift,
98
- severity: Severity.Major,
99
- message: `Documentation mismatch: function parameters (${missingParams.join(', ')}) are not mentioned in the docs.`,
100
- location: { file, line: exp.loc?.start.line || 1 },
101
- });
102
- }
103
81
  }
104
82
 
105
- // Timestamp comparison for temporal drift
83
+ // Check for temporal drift
106
84
  if (exp.loc && doc.loc) {
107
- if (!fileLineStamps) {
85
+ if (!fileLineStamps)
108
86
  fileLineStamps = getFileCommitTimestamps(file);
109
- }
110
87
 
111
88
  const bodyModified = getLineRangeLastModifiedCached(
112
89
  fileLineStamps,
113
90
  exp.loc.start.line,
114
91
  exp.loc.end.line
115
92
  );
116
-
117
93
  const docModified = getLineRangeLastModifiedCached(
118
94
  fileLineStamps,
119
95
  doc.loc.start.line,
120
96
  doc.loc.end.line
121
97
  );
122
98
 
123
- if (bodyModified > 0 && docModified > 0) {
124
- // If body was modified more than 1 day AFTER the documentation
125
- const DRIFT_THRESHOLD_SECONDS = 24 * 60 * 60;
126
- if (bodyModified - docModified > DRIFT_THRESHOLD_SECONDS) {
127
- actualDrift++;
128
- issues.push({
129
- type: IssueType.DocDrift,
130
- severity: Severity.Major,
131
- message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1000).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1000).toLocaleDateString()}.`,
132
- location: { file, line: doc.loc.start.line },
133
- });
134
- }
99
+ if (hasTemporalDrift(bodyModified, docModified)) {
100
+ actualDrift++;
101
+ issues.push({
102
+ type: IssueType.DocDrift,
103
+ severity: Severity.Major,
104
+ message: `Documentation drift: logic was modified on ${new Date(bodyModified * 1000).toLocaleDateString()} but documentation was last updated on ${new Date(docModified * 1000).toLocaleDateString()}.`,
105
+ location: { file, line: doc.loc.start.line },
106
+ });
135
107
  }
136
108
  }
137
109
  }
@@ -147,7 +119,7 @@ export async function analyzeDocDrift(
147
119
  uncommentedExports,
148
120
  totalExports,
149
121
  outdatedComments,
150
- undocumentedComplexity,
122
+ undocumentedComplexity: undocumentedComplexityCount,
151
123
  actualDrift,
152
124
  });
153
125
 
@@ -163,7 +135,7 @@ export async function analyzeDocDrift(
163
135
  uncommentedExports,
164
136
  totalExports,
165
137
  outdatedComments,
166
- undocumentedComplexity,
138
+ undocumentedComplexity: undocumentedComplexityCount,
167
139
  actualDrift,
168
140
  },
169
141
  recommendations: riskResult.recommendations,