jsdoczoom 0.3.0 → 0.4.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.
package/dist/validate.js CHANGED
@@ -1,6 +1,7 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { dirname, join, relative } from "node:path";
3
- import { isBarrel } from "./barrel.js";
1
+ import { readFile } from "node:fs/promises";
2
+ import { relative } from "node:path";
3
+ import { findMissingBarrels } from "./barrel.js";
4
+ import { processWithCache } from "./cache.js";
4
5
  import { JsdocError } from "./errors.js";
5
6
  import {
6
7
  createValidationLinter,
@@ -9,50 +10,28 @@ import {
9
10
  } from "./eslint-engine.js";
10
11
  import { discoverFiles } from "./file-discovery.js";
11
12
  import { GUIDANCE } from "./skill-text.js";
12
- import { VALIDATION_STATUS_PRIORITY } from "./types.js";
13
+ import { DEFAULT_CACHE_DIR, VALIDATION_STATUS_PRIORITY } from "./types.js";
13
14
 
14
15
  /**
15
16
  * Classify a single file against validation requirements.
16
17
  *
17
18
  * Reads the file, lints it with the ESLint engine, and maps the
18
19
  * resulting messages to a single ValidationStatus using priority order.
20
+ * Results are cached by file content hash.
19
21
  */
20
- async function classifyFile(eslint, filePath, cwd) {
22
+ async function classifyFile(eslint, filePath, cwd, config) {
21
23
  const relativePath = relative(cwd, filePath);
22
24
  let sourceText;
23
25
  try {
24
- sourceText = readFileSync(filePath, "utf-8");
26
+ sourceText = await readFile(filePath, "utf-8");
25
27
  } catch {
26
28
  throw new JsdocError("FILE_NOT_FOUND", `File not found: ${filePath}`);
27
29
  }
28
- const messages = await lintFileForValidation(eslint, sourceText, filePath);
29
- const status = mapToValidationStatus(messages);
30
- return { path: relativePath, status };
31
- }
32
- /** Minimum number of .ts/.tsx files in a directory to require a barrel. */
33
- const BARREL_THRESHOLD = 3;
34
- /**
35
- * Find directories with more than BARREL_THRESHOLD .ts/.tsx files
36
- * that lack a barrel file (index.ts or index.tsx).
37
- */
38
- function findMissingBarrels(filePaths, cwd) {
39
- const dirCounts = new Map();
40
- for (const filePath of filePaths) {
41
- if (isBarrel(filePath)) continue;
42
- const dir = dirname(filePath);
43
- dirCounts.set(dir, (dirCounts.get(dir) ?? 0) + 1);
44
- }
45
- const missing = [];
46
- for (const [dir, count] of dirCounts) {
47
- if (count <= BARREL_THRESHOLD) continue;
48
- const hasBarrel =
49
- existsSync(join(dir, "index.ts")) || existsSync(join(dir, "index.tsx"));
50
- if (!hasBarrel) {
51
- const rel = relative(cwd, dir) || ".";
52
- missing.push(rel);
53
- }
54
- }
55
- return missing.sort();
30
+ return processWithCache(config, "validate", sourceText, async () => {
31
+ const messages = await lintFileForValidation(eslint, sourceText, filePath);
32
+ const status = mapToValidationStatus(messages);
33
+ return { path: relativePath, status };
34
+ });
56
35
  }
57
36
  /**
58
37
  * Group file statuses into a ValidationResult, applying a limit
@@ -95,11 +74,19 @@ function buildGroupedResult(statuses, missingBarrels, limit) {
95
74
  * @param selector - Selector information (glob or path)
96
75
  * @param cwd - Working directory for resolving paths
97
76
  * @param limit - Max number of invalid file paths to include (default 100)
77
+ * @param gitignore - Whether to respect .gitignore (default true)
78
+ * @param config - Cache configuration (default: enabled with system temp dir)
98
79
  * @returns Grouped validation results
99
80
  * @throws {JsdocError} NO_FILES_MATCHED if glob selector matches no files
100
81
  * @throws {JsdocError} FILE_NOT_FOUND if path selector targets nonexistent file
101
82
  */
102
- export async function validate(selector, cwd, limit = 100, gitignore = true) {
83
+ export async function validate(
84
+ selector,
85
+ cwd,
86
+ limit = 100,
87
+ gitignore = true,
88
+ config = { enabled: true, directory: DEFAULT_CACHE_DIR },
89
+ ) {
103
90
  const files = discoverFiles(selector.pattern, cwd, gitignore);
104
91
  if (files.length === 0) {
105
92
  throw new JsdocError(
@@ -109,7 +96,7 @@ export async function validate(selector, cwd, limit = 100, gitignore = true) {
109
96
  }
110
97
  const eslint = createValidationLinter();
111
98
  const statuses = await Promise.all(
112
- files.map((f) => classifyFile(eslint, f, cwd)),
99
+ files.map((f) => classifyFile(eslint, f, cwd, config)),
113
100
  );
114
101
  const missingBarrels = findMissingBarrels(files, cwd);
115
102
  return buildGroupedResult(statuses, missingBarrels, limit);
@@ -122,15 +109,21 @@ export async function validate(selector, cwd, limit = 100, gitignore = true) {
122
109
  * @param filePaths - List of file paths to validate
123
110
  * @param cwd - Working directory for resolving relative paths
124
111
  * @param limit - Max number of invalid file paths to include (default 100)
112
+ * @param config - Cache configuration (default: enabled with system temp dir)
125
113
  * @returns Grouped validation results
126
114
  */
127
- export async function validateFiles(filePaths, cwd, limit = 100) {
115
+ export async function validateFiles(
116
+ filePaths,
117
+ cwd,
118
+ limit = 100,
119
+ config = { enabled: true, directory: DEFAULT_CACHE_DIR },
120
+ ) {
128
121
  const tsFiles = filePaths.filter(
129
122
  (f) => f.endsWith(".ts") || f.endsWith(".tsx"),
130
123
  );
131
124
  const eslint = createValidationLinter();
132
125
  const statuses = await Promise.all(
133
- tsFiles.map((f) => classifyFile(eslint, f, cwd)),
126
+ tsFiles.map((f) => classifyFile(eslint, f, cwd, config)),
134
127
  );
135
128
  const missingBarrels = findMissingBarrels(tsFiles, cwd);
136
129
  return buildGroupedResult(statuses, missingBarrels, limit);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsdoczoom",
3
- "version": "0.3.0",
3
+ "version": "0.4.5",
4
4
  "description": "CLI tool for extracting JSDoc summaries at configurable depths",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -35,23 +35,27 @@
35
35
  },
36
36
  "scripts": {
37
37
  "typecheck": "tsc --noEmit",
38
- "build": "tsc --build tsconfig.build.json",
38
+ "build": "yarn build:claude-code-hooks && yarn build:cli && yarn build:hooks",
39
+ "build:cli": "tsc --build tsconfig.build.json",
40
+ "build:claude-code-hooks": "cd ../claude-code-hooks && yarn build",
41
+ "build:hooks": "claude-code-hooks -i \"src/hooks/*.ts\" -o \"../../plugins/jsdoczoom/hooks/hooks.json\"",
39
42
  "test": "vitest run",
40
43
  "lint": "biome check --write --unsafe --max-diagnostics 500 .",
41
44
  "release": "bash ../../scripts/release-package.sh jsdoczoom",
42
45
  "release:dry-run": "bash ../../scripts/release-package.sh jsdoczoom --dry-run"
43
46
  },
44
47
  "dependencies": {
45
- "@typescript-eslint/parser": "^8.21.0",
46
- "eslint": "^9.20.0",
47
- "eslint-plugin-jsdoc": "^50.6.4",
48
- "glob": "^11.0.0",
48
+ "@goodfoot/claude-code-hooks": "workspace:*",
49
+ "@typescript-eslint/parser": "^8.55.0",
50
+ "eslint": "^10.0.0",
51
+ "eslint-plugin-jsdoc": "^62.5.5",
52
+ "glob": "^13.0.3",
49
53
  "ignore": "^7.0.5",
50
54
  "typescript": "^5.9.3"
51
55
  },
52
56
  "devDependencies": {
53
- "@biomejs/biome": "2.3.14",
54
- "@types/node": "^24",
55
- "vitest": "4.0.13"
57
+ "@biomejs/biome": "2.4.1",
58
+ "@types/node": "^25.2.3",
59
+ "vitest": "4.0.18"
56
60
  }
57
61
  }
package/types/barrel.d.ts CHANGED
@@ -33,3 +33,13 @@ export declare function getBarrelChildren(
33
33
  barrelPath: string,
34
34
  _cwd: string,
35
35
  ): string[];
36
+ /** Minimum number of .ts/.tsx files in a directory to require a barrel. */
37
+ export declare const BARREL_THRESHOLD = 3;
38
+ /**
39
+ * Find directories with more than BARREL_THRESHOLD .ts/.tsx files
40
+ * that lack a barrel file (index.ts or index.tsx).
41
+ */
42
+ export declare function findMissingBarrels(
43
+ filePaths: string[],
44
+ cwd: string,
45
+ ): string[];
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Content-hash-based disk caching layer for expensive operations (TypeScript
3
+ * compilation, ESLint linting, JSDoc parsing). Uses SHA-256 hashing to cache
4
+ * results keyed by file content, enabling near-instantaneous repeated runs
5
+ * when files haven't changed. All cache errors degrade gracefully without
6
+ * propagating to callers.
7
+ *
8
+ * @summary Content-hash-based disk cache with graceful error handling
9
+ */
10
+ import type { CacheConfig, CacheOperationMode } from "./types.js";
11
+ /**
12
+ * Compute a SHA-256 content hash for the given string.
13
+ *
14
+ * @param content - The content to hash
15
+ * @returns Hex-encoded SHA-256 digest
16
+ */
17
+ export declare function computeContentHash(content: string): string;
18
+ /**
19
+ * Ensure the cache directory exists for the given operation mode.
20
+ * Silently swallows all errors (mkdir failures are handled by read/write ops).
21
+ *
22
+ * @param config - Cache configuration
23
+ * @param mode - Operation mode (drilldown, validate, or lint)
24
+ */
25
+ export declare function ensureCacheDir(
26
+ config: CacheConfig,
27
+ mode: CacheOperationMode,
28
+ ): Promise<void>;
29
+ /**
30
+ * Read a cached entry from disk. Returns null on any error (cache miss, ENOENT,
31
+ * corrupted JSON, permission denied, etc.). Never throws.
32
+ *
33
+ * @param config - Cache configuration
34
+ * @param mode - Operation mode namespace
35
+ * @param hash - Content hash key
36
+ * @returns Parsed cached data or null on any error
37
+ */
38
+ export declare function readCacheEntry<T>(
39
+ config: CacheConfig,
40
+ mode: CacheOperationMode,
41
+ hash: string,
42
+ ): Promise<T | null>;
43
+ /**
44
+ * Write a cache entry to disk using atomic write (write to .tmp then rename).
45
+ * Silently swallows all errors (permission denied, disk full, etc.). Never throws.
46
+ *
47
+ * @param config - Cache configuration
48
+ * @param mode - Operation mode namespace
49
+ * @param hash - Content hash key
50
+ * @param data - Data to cache (must be JSON-serializable)
51
+ */
52
+ export declare function writeCacheEntry<T>(
53
+ config: CacheConfig,
54
+ mode: CacheOperationMode,
55
+ hash: string,
56
+ data: T,
57
+ ): Promise<void>;
58
+ /**
59
+ * Execute a computation with caching. If cache is enabled and entry exists,
60
+ * returns cached result. Otherwise, computes result, stores it (fire-and-forget),
61
+ * and returns it. Degrades gracefully on all cache errors by always computing.
62
+ *
63
+ * @param config - Cache configuration
64
+ * @param mode - Operation mode namespace
65
+ * @param content - File content to hash for cache key
66
+ * @param compute - Function to compute the result on cache miss
67
+ * @returns Cached or computed result
68
+ */
69
+ export declare function processWithCache<T>(
70
+ config: CacheConfig,
71
+ mode: CacheOperationMode,
72
+ content: string,
73
+ compute: () => T | Promise<T>,
74
+ ): Promise<T>;
@@ -1,4 +1,4 @@
1
- import type { DrilldownResult, SelectorInfo } from "./types.js";
1
+ import type { CacheConfig, DrilldownResult, SelectorInfo } from "./types.js";
2
2
  /**
3
3
  * Main entry point for normal-mode processing.
4
4
  *
@@ -18,7 +18,8 @@ export declare function drilldown(
18
18
  cwd: string,
19
19
  gitignore?: boolean,
20
20
  limit?: number,
21
- ): DrilldownResult;
21
+ config?: CacheConfig,
22
+ ): Promise<DrilldownResult>;
22
23
  /**
23
24
  * Process an explicit list of file paths at a given depth.
24
25
  *
@@ -35,4 +36,5 @@ export declare function drilldownFiles(
35
36
  depth: number | undefined,
36
37
  cwd: string,
37
38
  limit?: number,
38
- ): DrilldownResult;
39
+ config?: CacheConfig,
40
+ ): Promise<DrilldownResult>;
@@ -1,3 +1,17 @@
1
+ import { type Ignore } from "ignore";
2
+ /**
3
+ * Walks .gitignore files from cwd to filesystem root, building an ignore
4
+ * filter that glob results pass through. Direct-path lookups bypass the
5
+ * filter since the user explicitly named the file. The ignore instance is
6
+ * created per call -- no caching -- because cwd may differ between invocations.
7
+ *
8
+ * @summary Resolve selector patterns to absolute file paths with gitignore filtering
9
+ */
10
+ /**
11
+ * Walk from `cwd` up to the filesystem root, collecting .gitignore entries.
12
+ * Returns an Ignore instance loaded with all discovered rules.
13
+ */
14
+ export declare function loadGitignore(cwd: string): Ignore;
1
15
  /**
2
16
  * Resolve a selector pattern to a list of .ts/.tsx file paths.
3
17
  *
package/types/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Re-exports all public functions, classes, and types from internal modules.
3
- * This is the sole entry point for programmatic consumers of the jsdoczoom
4
- * package.
2
+ * Progressively explores TypeScript codebase documentation through a 4-level
3
+ * drill-down (summary, description, type declarations, full source), validates
4
+ * file-level JSDoc structure, and lints comprehensive JSDoc quality using
5
+ * ESLint. Barrel files gate their children at shallow depths, revealing
6
+ * individual files only at deeper levels.
5
7
  *
6
- * @summary Public API barrel re-exporting all functions, types, and classes
8
+ * @summary Progressive JSDoc exploration, validation, and linting for TypeScript codebases
7
9
  */
8
10
  export { getBarrelChildren, isBarrel } from "./barrel.js";
9
11
  export { drilldown, drilldownFiles } from "./drilldown.js";
@@ -14,6 +16,9 @@ export { lint, lintFiles } from "./lint.js";
14
16
  export { parseSelector } from "./selector.js";
15
17
  export { generateTypeDeclarations } from "./type-declarations.js";
16
18
  export {
19
+ type CacheConfig,
20
+ type CacheOperationMode,
21
+ DEFAULT_CACHE_DIR,
17
22
  type DrilldownResult,
18
23
  type ErrorCode,
19
24
  type LintDiagnostic,
package/types/lint.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @summary Lint files for comprehensive JSDoc quality using ESLint engine
10
10
  */
11
- import type { LintResult, SelectorInfo } from "./types.js";
11
+ import type { CacheConfig, LintResult, SelectorInfo } from "./types.js";
12
12
  /**
13
13
  * Lint files matching a selector pattern for comprehensive JSDoc quality.
14
14
  *
@@ -20,6 +20,7 @@ import type { LintResult, SelectorInfo } from "./types.js";
20
20
  * @param cwd - Working directory for resolving paths
21
21
  * @param limit - Max number of files with issues to include (default 100)
22
22
  * @param gitignore - Whether to respect .gitignore rules (default true)
23
+ * @param config - Cache configuration (default enabled with DEFAULT_CACHE_DIR)
23
24
  * @returns Lint result with per-file diagnostics and summary
24
25
  * @throws {JsdocError} NO_FILES_MATCHED if glob selector matches no files
25
26
  */
@@ -28,6 +29,7 @@ export declare function lint(
28
29
  cwd: string,
29
30
  limit?: number,
30
31
  gitignore?: boolean,
32
+ config?: CacheConfig,
31
33
  ): Promise<LintResult>;
32
34
  /**
33
35
  * Lint an explicit list of file paths for comprehensive JSDoc quality.
@@ -38,10 +40,12 @@ export declare function lint(
38
40
  * @param filePaths - List of absolute file paths to lint
39
41
  * @param cwd - Working directory for computing relative paths
40
42
  * @param limit - Max number of files with issues to include (default 100)
43
+ * @param config - Cache configuration (default enabled with DEFAULT_CACHE_DIR)
41
44
  * @returns Lint result with per-file diagnostics and summary
42
45
  */
43
46
  export declare function lintFiles(
44
47
  filePaths: string[],
45
48
  cwd: string,
46
49
  limit?: number,
50
+ config?: CacheConfig,
47
51
  ): Promise<LintResult>;
@@ -11,6 +11,6 @@ import type { ValidationStatus } from "./types.js";
11
11
  /** Markdown guidance text for each validation status category */
12
12
  export declare const GUIDANCE: Record<ValidationStatus, string>;
13
13
  export declare const SKILL_TEXT =
14
- '# World-Class JSDoc Guidelines for TypeScript\n\nThese guidelines describe the *properties* of excellent inline JSDoc in TypeScript repositories. They are a target to aim for, not a checklist. Use judgment; clarity beats volume.\n\n## Core principle\n\nTypeScript projects already have explicit types. JSDoc should add **intent, behavior, and constraints** rather than repeat what the type system already expresses.\n\n## Would have (high-signal properties)\n\n- **Intent and constraints**: Explains the non-obvious decision, constraint, or tradeoff (e.g., why a particular algorithm is used, why a heuristic exists, or what the runtime environment forbids).\n- **Behavioral contract**: Clearly states what inputs are accepted, what outputs represent, and how boundary cases are treated.\n- **Domain vocabulary**: Uses project-specific terms consistently so readers can navigate the codebase by vocabulary.\n- **Examples that disambiguate**: Includes `@example` blocks when behavior is otherwise ambiguous (e.g., parsing rules, path formats, or object schemas). Examples are short and realistic.\n- **Runtime effects**: Calls out side effects, temporal dependency (polling vs. event-driven), filesystem reads/writes, or external service behavior when those matter.\n- **Edge-case prompts**: Captures "what happens if..." questions that a reviewer would ask, especially where external APIs or platform behavior has caveats.\n- **Consistency with existing style**: Matches the formatting conventions already established in the codebase (multi-line descriptions, bullet lists where helpful).\n- **Future-proof hints**: Notes invariants and assumptions that must hold if the code evolves.\n- **LLM-friendly structure**: Uses short, self-contained paragraphs written in clear International Business English. Avoids prescriptive headers (e.g., "Why:", "Constraint:") in favor of natural prose that states context, purpose, and caveats directly.\n\n## Would not have (low-signal or risky properties)\n\n- **Type restatements**: Repeating TypeScript types in prose (e.g., "@param options - The options object" when the type is already `Options`). Keep `@param`, `@returns`, and `@throws` tags\u2014just make the descriptions add meaning beyond the type.\n- **Obvious narration**: Comments that paraphrase the code or parameter name without additional insight.\n- **Incorrect authority**: Claims that are not enforced by code (e.g., "never throws" when it can, or "always" without guardrails).\n- **Redundant verbosity**: Long descriptions that could be expressed more directly, or boilerplate that hides the key idea.\n- **Unbounded examples**: Large blocks or full payloads when a minimal example would do.\n- **Out-of-date operational details**: References to tooling, CLI flags, or config knobs that are not enforced or checked.\n- **Implementation leakage**: Unnecessary internal steps or private details that are likely to change and add churn to docs.\n- **Non-ASCII decoration**: Fancy symbols or emojis that do not already exist in the file; keep ASCII unless needed.\n\n## Tag usage cues (not rules)\n\n### IntelliSense tags (always include)\n\nThese tags power IDE hover tooltips, autocomplete, and signature help. Always include them for public APIs, constructors, and functions:\n\n- **`@param`**: Include for every parameter. Describe the parameter\'s purpose, valid ranges, or constraints\u2014not just its type.\n- **`@returns`**: Include when the function returns a value. Describe what the return value represents, especially for edge cases.\n- **`@throws`**: Include when the function can throw. Describe the conditions that cause the error.\n- **`@template`**: Include for generic functions and types. Describe what the type parameter represents.\n\n### Cross-reference tags (use to aid navigation)\n\n- **`@see`**: Link to related functions, types, or external documentation.\n- **`{@link Symbol}`**: Inline reference within descriptions. Creates a clickable link to another symbol.\n\n### Structural tags (use when appropriate)\n\n- Use `@module` at the top of files that define a cohesive domain concept.\n- Use `@example` when parsing or formatting behavior could be misread, or when a type is complex.\n- Use `@deprecated` on exports that are retained for compatibility.\n- Prefer short description + bullets for concepts with multiple facets.\n\n---\n\n## Writing file-level @summary and description\n\nEvery TypeScript file should have a file-level JSDoc block before the first code statement. This block is what jsdoczoom reads for orientation and validation.\n\n### Structure\n\n```typescript\n/**\n * Description paragraph goes here. It explains the file\'s responsibilities,\n * invariants, trade-offs, and failure modes. This is the deepest level of\n * native documentation \u2014 enough for someone to understand why this file\n * exists and how it fits into the broader system.\n *\n * @summary Concise one-line overview for quick orientation when scanning a codebase.\n */\n```\n\n### The @summary tag\n\nThe `@summary` tag provides a one-line overview \u2014 the first thing someone sees when scanning with jsdoczoom at the shallowest depth.\n\n**Good summaries:**\n- State what the file *does* or *is responsible for*, not what it contains\n- Are self-contained \u2014 understandable without reading other files\n- Use domain vocabulary consistently with the rest of the codebase\n- Fit on a single line (joined if multi-line in source)\n\n**Examples:**\n- `@summary Barrel tree model for hierarchical gating in glob mode`\n- `@summary Resolve selector patterns to absolute file paths with gitignore filtering`\n- `@summary CLI entry point \u2014 argument parsing, mode dispatch, and exit code handling`\n\n**Avoid:**\n- `@summary This file contains utility functions` \u2014 says what it *contains*, not what it *does*\n- `@summary Helpers` \u2014 too vague, no domain context\n- `@summary The main module` \u2014 no information about purpose or scope\n\n### The description paragraph\n\nThe description is prose that appears before any `@` tags. It provides the deeper context that the summary cannot \u2014 responsibilities, invariants, trade-offs, and failure modes.\n\n**Good descriptions:**\n- Explain *why* this file exists and what problem it solves\n- State invariants and assumptions that callers or maintainers must know\n- Note trade-offs and design decisions (e.g., "uses priority-order fill to keep the limit algorithm simple")\n- Mention failure modes and edge cases relevant to the file as a whole\n- Are 1-4 sentences, not an essay\n\n**Examples:**\n```typescript\n/**\n * Walks .gitignore files from cwd to filesystem root, building an ignore\n * filter that glob results pass through. Direct-path lookups bypass the\n * filter since the user explicitly named the file. The ignore instance is\n * created per call \u2014 no caching \u2014 because cwd may differ between invocations.\n *\n * @summary Resolve selector patterns to absolute file paths with gitignore filtering\n */\n```\n\n```typescript\n/**\n * Each file is classified into exactly one status category: the first\n * failing check wins (syntax_error > missing_jsdoc > missing_summary >\n * missing_description). Valid files are omitted from output entirely.\n * The limit parameter caps the total number of invalid paths shown,\n * filled in priority order across groups.\n *\n * @summary Validate file-level JSDoc and group results by status category\n */\n```\n\n**Avoid:**\n- Restating the summary in longer words\n- Listing every function in the file\n- Implementation details that change frequently (line numbers, internal variable names)\n\n### Barrel files (index.ts / index.tsx)\n\nBarrel files represent their directory. The `@summary` and description describe the module, not individual files.\n\n- **`@summary`**: What the module does as a unit\n- **Description**: The module\'s capabilities and concerns \u2014 describe concepts, not child filenames\n\n```typescript\n// packages/auth/src/index.ts\n/**\n * Provides session lifecycle management, token validation and refresh,\n * and middleware for route-level access control. OAuth2 provider\n * integration is handled here; cryptographic primitives are delegated\n * to the crypto package.\n *\n * @summary Authentication and authorization module\n */\n```\n\n**Avoid:**\n- Listing child filenames in the description\n- `@summary Exports for the auth module` \u2014 describes the mechanism, not the purpose\n- `@summary Index file` \u2014 no information about what the module does\n\n### Placement\n\nThe file-level JSDoc block must appear **before the first code statement** (imports are fine above it, but the block must precede any `export`, `const`, `function`, `class`, etc.). A common pattern is to place it immediately after imports:\n\n```typescript\nimport { resolve } from "node:path";\nimport { globSync } from "glob";\n\n/**\n * Description paragraph here.\n *\n * @summary One-line overview here\n */\n\nexport function discoverFiles(...) { ... }\n```\n\n### Common lint rules and examples\n\nThese rules are enforced in lint mode (`-l`). Understanding what passes and fails reduces trial-and-rerun cycles.\n\n#### `jsdoc/informative-docs` \u2014 descriptions must add meaning\n\nThis rule rejects descriptions that merely restate the parameter name, type, or containing symbol name. Descriptions must provide behavioral context.\n\n**Fails:**\n- `@param id - The id`\n- `@param options - The options object`\n- `@returns The result`\n- `@param name - The name string`\n\n**Passes:**\n- `@param id - Unique identifier used for cache lookup and deduplication`\n- `@param options - Controls retry behavior, timeout, and error handling strategy`\n- `@returns Parsed configuration with defaults applied for missing fields`\n- `@param name - Display name shown in the navigation sidebar`\n\n**Rule of thumb:** If removing the parameter name from the description leaves no useful information, the description is not informative enough.\n\n#### `jsdoc/check-tag-names` \u2014 allowed tags only\n\nThe lint configuration rejects non-standard JSDoc tags. Common tags to **avoid in JSDoc blocks**:\n- `@remarks` \u2014 move content to the description paragraph (prose before tags)\n- `@packageDocumentation` \u2014 use `@module` instead\n- `@concept`, `@constraint` \u2014 move content to the description paragraph\n\nFramework directives that look like tags (e.g., `@vitest-environment`) should use plain comments instead:\n```typescript\n// @vitest-environment node \u2190 correct (plain comment)\n/** @vitest-environment node */ \u2190 incorrect (treated as JSDoc tag)\n```\n\n#### `jsdoc/require-throws` \u2014 document throw conditions\n\nInclude `@throws` for any function that can throw, including catch-and-rethrow patterns:\n```typescript\n/**\n * @param path - File path to read\n * @returns Parsed configuration object\n * @throws {ConfigError} When the file is missing or contains invalid YAML\n */\n```\n\nIf a function catches errors and rethrows them (wrapped or unwrapped), it still needs `@throws`.\n\n### Nested object parameters\n\nWhen a function accepts an inline object parameter, document each property with a nested `@param` tag:\n\n```typescript\n/**\n * Create a new user account.\n *\n * @param data - Account creation payload\n * @param data.email - Email address used for login and notifications\n * @param data.displayName - Public-facing name shown in the UI\n * @param data.role - Initial permission level assigned to the account\n * @returns The created user record with generated ID\n */\nfunction createUser(data: { email: string; displayName: string; role: Role }): User {\n```\n\nFor React component props, use `props` (or `root0` if destructured) as the root:\n\n```typescript\n/**\n * @param props - Component properties\n * @param props.title - Page heading displayed at the top\n * @param props.onSubmit - Callback invoked when the form is submitted\n */\nfunction MyComponent(props: { title: string; onSubmit: () => void }) {\n```\n\n### Overload documentation\n\nWhen a function has TypeScript overload signatures, document **both** the overload declarations and the implementation signature:\n\n```typescript\n/**\n * Parse a value from a string representation.\n *\n * @param input - Raw string to parse\n * @returns Parsed numeric value\n */\nfunction parse(input: string): number;\n/**\n * Parse a value from a buffer.\n *\n * @param input - Binary buffer to parse\n * @returns Parsed numeric value\n */\nfunction parse(input: Buffer): number;\n/**\n * Parse a value from string or buffer input. String inputs are decoded\n * as UTF-8 before numeric parsing.\n *\n * @param input - String or buffer to parse\n * @returns Parsed numeric value\n * @throws {ParseError} When the input cannot be interpreted as a number\n */\nfunction parse(input: string | Buffer): number {\n // implementation\n}\n```\n\n';
14
+ '# World-Class JSDoc Guidelines for TypeScript\n\nThese guidelines describe the *properties* of excellent inline JSDoc in TypeScript repositories. They are a target to aim for, not a checklist. Use judgment; clarity beats volume.\n\n## Core principle\n\nTypeScript projects already have explicit types. JSDoc should add **intent, behavior, and constraints** rather than repeat what the type system already expresses.\n\n## Would have (high-signal properties)\n\n- **Intent and constraints**: Explains the non-obvious decision, constraint, or tradeoff (e.g., why a particular algorithm is used, why a heuristic exists, or what the runtime environment forbids).\n- **Behavioral contract**: Clearly states what inputs are accepted, what outputs represent, and how boundary cases are treated.\n- **Domain vocabulary**: Uses project-specific terms consistently so readers can navigate the codebase by vocabulary.\n- **Examples that disambiguate**: Includes `@example` blocks when behavior is otherwise ambiguous (e.g., parsing rules, path formats, or object schemas). Examples are short and realistic.\n- **Runtime effects**: Calls out side effects, temporal dependency (polling vs. event-driven), filesystem reads/writes, or external service behavior when those matter.\n- **Edge-case prompts**: Captures "what happens if..." questions that a reviewer would ask, especially where external APIs or platform behavior has caveats.\n- **Consistency with existing style**: Matches the formatting conventions already established in the codebase (multi-line descriptions, bullet lists where helpful).\n- **Future-proof hints**: Notes invariants and assumptions that must hold if the code evolves.\n- **LLM-friendly structure**: Uses short, self-contained paragraphs written in clear International Business English. Avoids prescriptive headers (e.g., "Why:", "Constraint:") in favor of natural prose that states context, purpose, and caveats directly.\n\n## Would not have (low-signal or risky properties)\n\n- **Type restatements**: Repeating TypeScript types in prose (e.g., "@param options - The options object" when the type is already `Options`). Keep `@param`, `@returns`, and `@throws` tags\u2014just make the descriptions add meaning beyond the type.\n- **Obvious narration**: Comments that paraphrase the code or parameter name without additional insight.\n- **Incorrect authority**: Claims that are not enforced by code (e.g., "never throws" when it can, or "always" without guardrails).\n- **Redundant verbosity**: Long descriptions that could be expressed more directly, or boilerplate that hides the key idea.\n- **Unbounded examples**: Large blocks or full payloads when a minimal example would do.\n- **Out-of-date operational details**: References to tooling, CLI flags, or config knobs that are not enforced or checked.\n- **Implementation leakage**: Unnecessary internal steps or private details that are likely to change and add churn to docs.\n- **Non-ASCII decoration**: Fancy symbols or emojis that do not already exist in the file; keep ASCII unless needed.\n\n## Tag usage cues (not rules)\n\n### IntelliSense tags (always include)\n\nThese tags power IDE hover tooltips, autocomplete, and signature help. Always include them for public APIs, constructors, and functions:\n\n- **`@param`**: Include for every parameter. Describe the parameter\'s purpose, valid ranges, or constraints\u2014not just its type.\n- **`@returns`**: Include when the function returns a value. Describe what the return value represents, especially for edge cases.\n- **`@throws`**: Include when the function can throw. Describe the conditions that cause the error.\n- **`@template`**: Include for generic functions and types. Describe what the type parameter represents.\n\n### Cross-reference tags (use to aid navigation)\n\n- **`@see`**: Link to related functions, types, or external documentation.\n- **`{@link Symbol}`**: Inline reference within descriptions. Creates a clickable link to another symbol.\n\n### Structural tags (use when appropriate)\n\n- Use `@module` at the top of files that define a cohesive domain concept.\n- Use `@example` when parsing or formatting behavior could be misread, or when a type is complex.\n- Use `@deprecated` on exports that are retained for compatibility.\n- Prefer short description + bullets for concepts with multiple facets.\n\n---\n\n## Writing file-level @summary and description\n\nEvery TypeScript file should have a file-level JSDoc block before the first code statement. This block is what jsdoczoom reads for orientation and validation.\n\n### Structure\n\n```typescript\n/**\n * Description paragraph goes here. It explains the file\'s responsibilities,\n * invariants, trade-offs, and failure modes. This is the deepest level of\n * native documentation \u2014 enough for someone to understand why this file\n * exists and how it fits into the broader system.\n *\n * @summary Concise one-line overview for quick orientation when scanning a codebase.\n */\n```\n\n### The @summary tag\n\nThe `@summary` tag provides a one-line overview \u2014 the first thing someone sees when scanning with jsdoczoom at the shallowest depth.\n\n**Good summaries:**\n- State what the file *does* or *is responsible for*, not what it contains\n- Are self-contained \u2014 understandable without reading other files\n- Use domain vocabulary consistently with the rest of the codebase\n- Fit on a single line (joined if multi-line in source)\n\n**Examples:**\n- `@summary Barrel tree model for hierarchical gating in glob mode`\n- `@summary Resolve selector patterns to absolute file paths with gitignore filtering`\n- `@summary CLI entry point \u2014 argument parsing, mode dispatch, and exit code handling`\n\n**Avoid:**\n- `@summary This file contains utility functions` \u2014 says what it *contains*, not what it *does*\n- `@summary Helpers` \u2014 too vague, no domain context\n- `@summary The main module` \u2014 no information about purpose or scope\n\n### The description paragraph\n\nThe description is prose that appears before any `@` tags. It provides the deeper context that the summary cannot \u2014 responsibilities, invariants, trade-offs, and failure modes.\n\n**Good descriptions:**\n- Explain *why* this file exists and what problem it solves\n- State invariants and assumptions that callers or maintainers must know\n- Note trade-offs and design decisions (e.g., "uses priority-order fill to keep the limit algorithm simple")\n- Mention failure modes and edge cases relevant to the file as a whole\n- Are 1-4 sentences, not an essay\n\n**Examples:**\n```typescript\n/**\n * Walks .gitignore files from cwd to filesystem root, building an ignore\n * filter that glob results pass through. Direct-path lookups bypass the\n * filter since the user explicitly named the file. The ignore instance is\n * created per call \u2014 no caching \u2014 because cwd may differ between invocations.\n *\n * @summary Resolve selector patterns to absolute file paths with gitignore filtering\n */\n```\n\n```typescript\n/**\n * Each file is classified into exactly one status category: the first\n * failing check wins (syntax_error > missing_jsdoc > missing_summary >\n * missing_description). Valid files are omitted from output entirely.\n * The limit parameter caps the total number of invalid paths shown,\n * filled in priority order across groups.\n *\n * @summary Validate file-level JSDoc and group results by status category\n */\n```\n\n**Avoid:**\n- Restating the summary in longer words\n- Listing every function in the file\n- Implementation details that change frequently (line numbers, internal variable names)\n\n### Barrel files (index.ts / index.tsx)\n\nBarrel files represent their directory. Their `@summary` and description should describe the **cumulative functionality of the directory\'s children**, not the barrel file itself.\n\n- **`@summary`**: The collective purpose of the files in this directory\n- **Description**: The combined capabilities and responsibilities of child modules \u2014 what they do together, not their filenames or the re-export mechanism\n\n```typescript\n// packages/auth/src/index.ts\n/**\n * Provides session lifecycle management, token validation and refresh,\n * and middleware for route-level access control. OAuth2 provider\n * integration is handled here; cryptographic primitives are delegated\n * to the crypto package.\n *\n * @summary Authentication and authorization module\n */\n```\n\n**Avoid:**\n- `@summary Re-exports all functions and types` \u2014 describes the barrel mechanism, not what the children do\n- `@summary Exports for the auth module` \u2014 describes the mechanism, not the purpose\n- `@summary Public API barrel` \u2014 names the pattern rather than describing functionality\n- Listing child filenames or saying "This is the entry point"\n\n### Placement\n\nThe file-level JSDoc block must appear **before the first code statement** (imports are fine above it, but the block must precede any `export`, `const`, `function`, `class`, etc.). A common pattern is to place it immediately after imports:\n\n```typescript\nimport { resolve } from "node:path";\nimport { globSync } from "glob";\n\n/**\n * Description paragraph here.\n *\n * @summary One-line overview here\n */\n\nexport function discoverFiles(...) { ... }\n```\n\n### Common lint rules and examples\n\nThese rules are enforced in lint mode (`-l`). Understanding what passes and fails reduces trial-and-rerun cycles.\n\n#### `jsdoc/informative-docs` \u2014 descriptions must add meaning\n\nThis rule rejects descriptions that merely restate the parameter name, type, or containing symbol name. Descriptions must provide behavioral context.\n\n**Fails:**\n- `@param id - The id`\n- `@param options - The options object`\n- `@returns The result`\n- `@param name - The name string`\n\n**Passes:**\n- `@param id - Unique identifier used for cache lookup and deduplication`\n- `@param options - Controls retry behavior, timeout, and error handling strategy`\n- `@returns Parsed configuration with defaults applied for missing fields`\n- `@param name - Display name shown in the navigation sidebar`\n\n**Rule of thumb:** If removing the parameter name from the description leaves no useful information, the description is not informative enough.\n\n#### `jsdoc/check-tag-names` \u2014 allowed tags only\n\nThe lint configuration rejects non-standard JSDoc tags. Common tags to **avoid in JSDoc blocks**:\n- `@remarks` \u2014 move content to the description paragraph (prose before tags)\n- `@packageDocumentation` \u2014 use `@module` instead\n- `@concept`, `@constraint` \u2014 move content to the description paragraph\n\nFramework directives that look like tags (e.g., `@vitest-environment`) should use plain comments instead:\n```typescript\n// @vitest-environment node \u2190 correct (plain comment)\n/** @vitest-environment node */ \u2190 incorrect (treated as JSDoc tag)\n```\n\n#### `jsdoc/require-throws` \u2014 document throw conditions\n\nInclude `@throws` for any function that can throw, including catch-and-rethrow patterns:\n```typescript\n/**\n * @param path - File path to read\n * @returns Parsed configuration object\n * @throws {ConfigError} When the file is missing or contains invalid YAML\n */\n```\n\nIf a function catches errors and rethrows them (wrapped or unwrapped), it still needs `@throws`.\n\n### Nested object parameters\n\nWhen a function accepts an inline object parameter, document each property with a nested `@param` tag:\n\n```typescript\n/**\n * Create a new user account.\n *\n * @param data - Account creation payload\n * @param data.email - Email address used for login and notifications\n * @param data.displayName - Public-facing name shown in the UI\n * @param data.role - Initial permission level assigned to the account\n * @returns The created user record with generated ID\n */\nfunction createUser(data: { email: string; displayName: string; role: Role }): User {\n```\n\nFor React component props, use `props` (or `root0` if destructured) as the root:\n\n```typescript\n/**\n * @param props - Component properties\n * @param props.title - Page heading displayed at the top\n * @param props.onSubmit - Callback invoked when the form is submitted\n */\nfunction MyComponent(props: { title: string; onSubmit: () => void }) {\n```\n\n### Overload documentation\n\nWhen a function has TypeScript overload signatures, document **both** the overload declarations and the implementation signature:\n\n```typescript\n/**\n * Parse a value from a string representation.\n *\n * @param input - Raw string to parse\n * @returns Parsed numeric value\n */\nfunction parse(input: string): number;\n/**\n * Parse a value from a buffer.\n *\n * @param input - Binary buffer to parse\n * @returns Parsed numeric value\n */\nfunction parse(input: Buffer): number;\n/**\n * Parse a value from string or buffer input. String inputs are decoded\n * as UTF-8 before numeric parsing.\n *\n * @param input - String or buffer to parse\n * @returns Parsed numeric value\n * @throws {ParseError} When the input cannot be interpreted as a number\n */\nfunction parse(input: string | Buffer): number {\n // implementation\n}\n```\n\n';
15
15
  /** Explanation text for each lint rule, used by --explain-rule */
16
16
  export declare const RULE_EXPLANATIONS: Record<string, string>;
package/types/types.d.ts CHANGED
@@ -104,6 +104,7 @@ export interface LintFileResult {
104
104
  /** Overall lint result with summary statistics */
105
105
  export interface LintResult {
106
106
  files: LintFileResult[];
107
+ missingBarrels?: string[];
107
108
  summary: {
108
109
  totalFiles: number;
109
110
  filesWithIssues: number;
@@ -111,3 +112,12 @@ export interface LintResult {
111
112
  truncated?: boolean;
112
113
  };
113
114
  }
115
+ /** Configuration for the disk cache layer */
116
+ export interface CacheConfig {
117
+ enabled: boolean;
118
+ directory: string;
119
+ }
120
+ /** Operation modes that produce cacheable results */
121
+ export type CacheOperationMode = "drilldown" | "validate" | "lint";
122
+ /** Default cache directory under os.tmpdir() */
123
+ export declare const DEFAULT_CACHE_DIR: string;
@@ -1,10 +1,12 @@
1
- import type { SelectorInfo, ValidationResult } from "./types.js";
1
+ import type { CacheConfig, SelectorInfo, ValidationResult } from "./types.js";
2
2
  /**
3
3
  * Validate files matching a selector pattern.
4
4
  *
5
5
  * @param selector - Selector information (glob or path)
6
6
  * @param cwd - Working directory for resolving paths
7
7
  * @param limit - Max number of invalid file paths to include (default 100)
8
+ * @param gitignore - Whether to respect .gitignore (default true)
9
+ * @param config - Cache configuration (default: enabled with system temp dir)
8
10
  * @returns Grouped validation results
9
11
  * @throws {JsdocError} NO_FILES_MATCHED if glob selector matches no files
10
12
  * @throws {JsdocError} FILE_NOT_FOUND if path selector targets nonexistent file
@@ -14,6 +16,7 @@ export declare function validate(
14
16
  cwd: string,
15
17
  limit?: number,
16
18
  gitignore?: boolean,
19
+ config?: CacheConfig,
17
20
  ): Promise<ValidationResult>;
18
21
  /**
19
22
  * Validate an explicit list of file paths.
@@ -23,10 +26,12 @@ export declare function validate(
23
26
  * @param filePaths - List of file paths to validate
24
27
  * @param cwd - Working directory for resolving relative paths
25
28
  * @param limit - Max number of invalid file paths to include (default 100)
29
+ * @param config - Cache configuration (default: enabled with system temp dir)
26
30
  * @returns Grouped validation results
27
31
  */
28
32
  export declare function validateFiles(
29
33
  filePaths: string[],
30
34
  cwd: string,
31
35
  limit?: number,
36
+ config?: CacheConfig,
32
37
  ): Promise<ValidationResult>;