eslint-plugin-traceability 1.7.0 → 1.8.0

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +73 -32
  3. package/docs/ci-cd-pipeline.md +224 -0
  4. package/docs/cli-integration.md +22 -0
  5. package/docs/code-quality-refactor-opportunities-2025-12-03.md +78 -0
  6. package/docs/config-presets.md +38 -0
  7. package/docs/conventional-commits-guide.md +185 -0
  8. package/docs/custom-rules-development-guide.md +659 -0
  9. package/docs/decisions/0001-allow-dynamic-require-for-built-plugins.md +26 -0
  10. package/docs/decisions/001-typescript-for-eslint-plugin.accepted.md +111 -0
  11. package/docs/decisions/002-jest-for-eslint-testing.accepted.md +137 -0
  12. package/docs/decisions/003-code-quality-ratcheting-plan.md +48 -0
  13. package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +196 -0
  14. package/docs/decisions/005-github-actions-validation-tooling.accepted.md +144 -0
  15. package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +227 -0
  16. package/docs/decisions/007-github-releases-over-changelog.accepted.md +216 -0
  17. package/docs/decisions/008-ci-audit-flags.accepted.md +60 -0
  18. package/docs/decisions/009-security-focused-lint-rules.accepted.md +64 -0
  19. package/docs/decisions/010-implements-annotation-for-multi-story-requirements.proposed.md +184 -0
  20. package/docs/decisions/adr-0001-console-usage-for-cli-guards.md +190 -0
  21. package/docs/decisions/adr-accept-dev-dep-risk-glob.md +40 -0
  22. package/docs/decisions/adr-commit-branch-tests.md +54 -0
  23. package/docs/decisions/adr-maintenance-cli-interface.md +140 -0
  24. package/docs/decisions/adr-pre-push-parity.md +112 -0
  25. package/docs/decisions/code-quality-ratcheting-plan.md +53 -0
  26. package/docs/dependency-health.md +238 -0
  27. package/docs/eslint-9-setup-guide.md +517 -0
  28. package/docs/eslint-plugin-development-guide.md +487 -0
  29. package/docs/functionality-coverage-2025-12-03.md +250 -0
  30. package/docs/jest-testing-guide.md +100 -0
  31. package/docs/rules/prefer-implements-annotation.md +219 -0
  32. package/docs/rules/require-branch-annotation.md +71 -0
  33. package/docs/rules/require-req-annotation.md +203 -0
  34. package/docs/rules/require-story-annotation.md +159 -0
  35. package/docs/rules/valid-annotation-format.md +418 -0
  36. package/docs/rules/valid-req-reference.md +153 -0
  37. package/docs/rules/valid-story-reference.md +120 -0
  38. package/docs/security-incidents/2025-11-17-glob-cli-incident.md +45 -0
  39. package/docs/security-incidents/2025-11-18-brace-expansion-redos.md +45 -0
  40. package/docs/security-incidents/2025-11-18-bundled-dev-deps-accepted-risk.md +93 -0
  41. package/docs/security-incidents/2025-11-18-tar-race-condition.md +43 -0
  42. package/docs/security-incidents/2025-12-03-dependency-health-review.md +58 -0
  43. package/docs/security-incidents/SECURITY-INCIDENT-2025-11-18-semantic-release-bundled-npm.known-error.md +104 -0
  44. package/docs/security-incidents/SECURITY-INCIDENT-TEMPLATE.md +37 -0
  45. package/docs/security-incidents/dependency-override-rationale.md +57 -0
  46. package/docs/security-incidents/dev-deps-high.json +116 -0
  47. package/docs/security-incidents/handling-procedure.md +54 -0
  48. package/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md +92 -0
  49. package/docs/stories/002.0-DEV-ESLINT-CONFIG.story.md +82 -0
  50. package/docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md +112 -0
  51. package/docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md +153 -0
  52. package/docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md +138 -0
  53. package/docs/stories/006.0-DEV-FILE-VALIDATION.story.md +144 -0
  54. package/docs/stories/007.0-DEV-ERROR-REPORTING.story.md +163 -0
  55. package/docs/stories/008.0-DEV-AUTO-FIX.story.md +150 -0
  56. package/docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md +117 -0
  57. package/docs/stories/010.0-DEV-DEEP-VALIDATION.story.md +124 -0
  58. package/docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md +149 -0
  59. package/docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md +216 -0
  60. package/docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md +236 -0
  61. package/docs/stories/developer-story.map.md +120 -0
  62. package/docs/ts-jest-presets-guide.md +548 -0
  63. package/lib/src/index.d.ts +2 -2
  64. package/lib/src/index.js +2 -0
  65. package/lib/src/maintenance/batch.d.ts +5 -0
  66. package/lib/src/maintenance/batch.js +5 -0
  67. package/lib/src/maintenance/cli.js +34 -212
  68. package/lib/src/maintenance/commands.d.ts +32 -0
  69. package/lib/src/maintenance/commands.js +139 -0
  70. package/lib/src/maintenance/detect.d.ts +2 -0
  71. package/lib/src/maintenance/detect.js +4 -0
  72. package/lib/src/maintenance/flags.d.ts +99 -0
  73. package/lib/src/maintenance/flags.js +121 -0
  74. package/lib/src/maintenance/report.d.ts +2 -0
  75. package/lib/src/maintenance/report.js +2 -0
  76. package/lib/src/maintenance/update.d.ts +4 -0
  77. package/lib/src/maintenance/update.js +4 -0
  78. package/lib/src/rules/helpers/require-story-io.d.ts +3 -0
  79. package/lib/src/rules/helpers/require-story-io.js +20 -6
  80. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +30 -0
  81. package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
  82. package/lib/src/rules/helpers/valid-annotation-options.js +15 -4
  83. package/lib/src/rules/helpers/valid-annotation-utils.js +5 -0
  84. package/lib/src/rules/helpers/valid-implements-utils.d.ts +75 -0
  85. package/lib/src/rules/helpers/valid-implements-utils.js +149 -0
  86. package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +3 -4
  87. package/lib/src/rules/prefer-implements-annotation.d.ts +39 -0
  88. package/lib/src/rules/prefer-implements-annotation.js +276 -0
  89. package/lib/src/rules/valid-annotation-format.js +87 -28
  90. package/lib/src/rules/valid-req-reference.js +71 -0
  91. package/lib/src/utils/reqAnnotationDetection.d.ts +4 -1
  92. package/lib/src/utils/reqAnnotationDetection.js +43 -15
  93. package/lib/tests/maintenance/cli.test.js +89 -0
  94. package/lib/tests/plugin-default-export-and-configs.test.js +3 -0
  95. package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
  96. package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
  97. package/lib/tests/rules/require-req-annotation.test.js +8 -1
  98. package/lib/tests/rules/require-story-annotation.test.js +9 -4
  99. package/lib/tests/rules/valid-annotation-format.test.js +78 -0
  100. package/lib/tests/rules/valid-req-reference.test.js +34 -0
  101. package/lib/tests/utils/ts-language-options.d.ts +1 -7
  102. package/lib/tests/utils/ts-language-options.js +8 -5
  103. package/package.json +7 -3
  104. package/user-docs/api-reference.md +507 -0
  105. package/user-docs/eslint-9-setup-guide.md +639 -0
  106. package/user-docs/examples.md +74 -0
  107. package/user-docs/migration-guide.md +158 -0
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseCliInput = parseCliInput;
7
+ exports.normalizeCliArgs = normalizeCliArgs;
8
+ exports.parseFlags = parseFlags;
9
+ /**
10
+ * Flag parsing and normalization logic for the traceability-maint CLI.
11
+ *
12
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
13
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
14
+ */
15
+ const path_1 = __importDefault(require("path"));
16
+ /**
17
+ * Parse raw Node.js argv into a structured CLI input for the maintenance tools.
18
+ *
19
+ * This performs only minimal, predictable splitting to support higher-level
20
+ * flag parsing in a safe and testable way.
21
+ *
22
+ * @param argv - Raw argv array, usually process.argv.
23
+ * @returns ParsedCliInput with node, script, subcommand, and remaining args.
24
+ *
25
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
26
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
27
+ */
28
+ function parseCliInput(argv) {
29
+ const [node = "", script = "", ...rest] = argv;
30
+ const [subcommand, ...args] = rest;
31
+ return { argv, node, script, subcommand, args };
32
+ }
33
+ /**
34
+ * Normalize raw argv into a subcommand-centric view for flag parsing.
35
+ *
36
+ * Uses parseCliInput to safely separate Node/V8 internals from the
37
+ * subcommand and its arguments, then exposes only the pieces relevant
38
+ * to subcommand-specific flag parsing.
39
+ *
40
+ * @param rawArgv - Raw argv array, usually process.argv.
41
+ * @returns NormalizedCliArgs with subcommand and remaining args.
42
+ *
43
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
44
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
45
+ */
46
+ function normalizeCliArgs(rawArgv) {
47
+ const { subcommand, args } = parseCliInput(rawArgv);
48
+ return { subcommand, args };
49
+ }
50
+ /**
51
+ * Initialize default flags for the maintenance CLI.
52
+ *
53
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
54
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
55
+ */
56
+ function createDefaultFlags() {
57
+ return {
58
+ root: process.cwd(),
59
+ json: false,
60
+ };
61
+ }
62
+ /**
63
+ * Handle a single CLI argument and update the flags accordingly.
64
+ *
65
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
66
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
67
+ */
68
+ function applyFlag(flags, args, index) {
69
+ const arg = args[index];
70
+ if (arg === "--root" && typeof args[index + 1] === "string") {
71
+ flags.root = path_1.default.resolve(args[index + 1]);
72
+ return index + 1;
73
+ }
74
+ if (arg === "--json") {
75
+ flags.json = true;
76
+ return index;
77
+ }
78
+ if (arg === "--format" && typeof args[index + 1] === "string") {
79
+ const value = args[index + 1];
80
+ if (value === "text" || value === "json") {
81
+ flags.format = value;
82
+ }
83
+ else {
84
+ throw new Error(`Invalid format: ${value}. Expected 'text' or 'json'.`);
85
+ }
86
+ return index + 1;
87
+ }
88
+ if (arg === "--from" && typeof args[index + 1] === "string") {
89
+ flags.from = args[index + 1];
90
+ return index + 1;
91
+ }
92
+ if (arg === "--to" && typeof args[index + 1] === "string") {
93
+ flags.to = args[index + 1];
94
+ return index + 1;
95
+ }
96
+ if (arg === "--dry-run") {
97
+ flags.dryRun = true;
98
+ return index;
99
+ }
100
+ return index;
101
+ }
102
+ /**
103
+ * Basic flag parser for maintenance CLI subcommands.
104
+ *
105
+ * Consumes already-normalized CLI arguments (subcommand and its raw args)
106
+ * and produces a ParsedFlags structure with minimal, predictable behavior.
107
+ *
108
+ * @param normalized - Normalized CLI arguments for a maintenance subcommand.
109
+ * @returns ParsedFlags with defaults applied and any recognized flags set.
110
+ *
111
+ * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
112
+ * @req REQ-MAINT-SAFE - Provide predictable, minimal argument parsing
113
+ */
114
+ function parseFlags(normalized) {
115
+ const flags = createDefaultFlags();
116
+ const { args } = normalized;
117
+ for (let i = 0; i < args.length; i += 1) {
118
+ i = applyFlag(flags, args, i);
119
+ }
120
+ return flags;
121
+ }
@@ -3,5 +3,7 @@
3
3
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
4
4
  * @req REQ-MAINT-REPORT - Generate maintenance report
5
5
  * @req REQ-MAINT-SAFE - Ensure operations are safe and reversible
6
+ * @param codebasePath The workspace root to scan for stale maintenance annotations.
7
+ * @returns An empty string when no stale annotations are found, or a newline-separated list of stale `@story` paths.
6
8
  */
7
9
  export declare function generateMaintenanceReport(codebasePath: string): string;
@@ -7,6 +7,8 @@ const detect_1 = require("./detect");
7
7
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
8
8
  * @req REQ-MAINT-REPORT - Generate maintenance report
9
9
  * @req REQ-MAINT-SAFE - Ensure operations are safe and reversible
10
+ * @param codebasePath The workspace root to scan for stale maintenance annotations.
11
+ * @returns An empty string when no stale annotations are found, or a newline-separated list of stale `@story` paths.
10
12
  */
11
13
  function generateMaintenanceReport(codebasePath) {
12
14
  const staleAnnotations = (0, detect_1.detectStaleAnnotations)(codebasePath);
@@ -2,5 +2,9 @@
2
2
  * Update annotation references when story files are moved or renamed
3
3
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
4
4
  * @req REQ-MAINT-UPDATE - Update annotation references
5
+ * @param codebasePath Absolute or workspace-root path whose files will be updated in-place.
6
+ * @param oldPath The original @story path to search for in annotation comments.
7
+ * @param newPath The replacement @story path that will replace occurrences of oldPath.
8
+ * @returns The number of @story annotations that were updated across the codebase.
5
9
  */
6
10
  export declare function updateAnnotationReferences(codebasePath: string, oldPath: string, newPath: string): number;
@@ -79,6 +79,10 @@ function processFileForAnnotationUpdates(fullPath, regex, newPath, replacementCo
79
79
  * Update annotation references when story files are moved or renamed
80
80
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
81
81
  * @req REQ-MAINT-UPDATE - Update annotation references
82
+ * @param codebasePath Absolute or workspace-root path whose files will be updated in-place.
83
+ * @param oldPath The original @story path to search for in annotation comments.
84
+ * @param newPath The replacement @story path that will replace occurrences of oldPath.
85
+ * @returns The number of @story annotations that were updated across the codebase.
82
86
  */
83
87
  function updateAnnotationReferences(codebasePath, oldPath, newPath) {
84
88
  /**
@@ -29,7 +29,10 @@ export declare function linesBeforeHasStory(sourceCode: any, node: any, lookback
29
29
  export declare function parentChainHasStory(sourceCode: any, node: any): boolean;
30
30
  /**
31
31
  * Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
32
+ * Also accepts @implements annotations as satisfying story presence for this rule.
32
33
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
34
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
33
35
  * @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
36
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @implements annotations as satisfying story presence in fallback checks
34
37
  */
35
38
  export declare function fallbackTextBeforeHasStory(sourceCode: any, node: any): boolean;
@@ -23,11 +23,17 @@ exports.LOOKBACK_LINES = 4;
23
23
  exports.FALLBACK_WINDOW = 800;
24
24
  /**
25
25
  * Shared predicate to determine if a given comment node contains an @story marker.
26
+ * Also treats @implements annotations as satisfying story presence checks.
26
27
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
28
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
27
29
  * @req REQ-ANNOTATION-REQUIRED - Centralize @story detection logic for comment value inspection
30
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @implements annotations as satisfying story presence checks
28
31
  */
29
32
  function commentContainsStory(comment) {
30
- return typeof comment?.value === "string" && comment.value.includes("@story");
33
+ if (typeof comment?.value !== "string") {
34
+ return false;
35
+ }
36
+ return (comment.value.includes("@story") || comment.value.includes("@implements"));
31
37
  }
32
38
  /**
33
39
  * Safely extract the physical source lines array from sourceCode for scanning.
@@ -55,17 +61,20 @@ function getNodeStartLine(node) {
55
61
  * Generic helper to scan a range of physical source lines for the presence of an @story marker.
56
62
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
57
63
  * @req REQ-ANNOTATION-REQUIRED - Reuse line scanning logic for story annotations across helpers
64
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @implements annotations as valid markers during line scanning
58
65
  */
59
66
  function scanLinesForMarker(lines, from, to) {
60
- // Walk each physical line in the configured lookback window to search for an inline @story marker.
67
+ // Walk each physical line in the configured lookback window to search for an inline @story or @implements marker.
61
68
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
62
69
  // @req REQ-ANNOTATION-REQUIRED - Scan preceding lines for existing story annotations
63
70
  for (let i = from; i < to; i++) {
64
71
  const text = lines[i];
65
- // Treat any line containing "@story" as evidence that the function is already annotated.
72
+ // Treat any line containing "@story" or "@implements" as evidence that the function is already annotated.
66
73
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
67
74
  // @req REQ-ANNOTATION-REQUIRED - Detect explicit @story markers in raw source text
68
- if (typeof text === "string" && text.includes("@story")) {
75
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect explicit @implements markers in raw source text
76
+ if (typeof text === "string" &&
77
+ (text.includes("@story") || text.includes("@implements"))) {
69
78
  return true;
70
79
  }
71
80
  }
@@ -125,8 +134,11 @@ function parentChainHasStory(sourceCode, node) {
125
134
  }
126
135
  /**
127
136
  * Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
137
+ * Also accepts @implements annotations as satisfying story presence for this rule.
128
138
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
139
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
129
140
  * @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
141
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @implements annotations as satisfying story presence in fallback checks
130
142
  */
131
143
  function fallbackTextBeforeHasStory(sourceCode, node) {
132
144
  // Skip fallback text inspection when the sourceCode API or node range information is not available.
@@ -149,10 +161,12 @@ function fallbackTextBeforeHasStory(sourceCode, node) {
149
161
  // @req REQ-ANNOTATION-REQUIRED - Restrict fallback text scanning to a safe, fixed-size window
150
162
  const start = Math.max(0, range[0] - exports.FALLBACK_WINDOW);
151
163
  const textBefore = sourceCode.getText().slice(start, range[0]);
152
- // Detect any @story marker that appears within the bounded fallback window.
164
+ // Detect any @story or @implements marker that appears within the bounded fallback window.
153
165
  // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
154
166
  // @req REQ-ANNOTATION-REQUIRED - Recognize story annotations discovered via fallback text scanning
155
- if (typeof textBefore === "string" && textBefore.includes("@story")) {
167
+ // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Recognize @implements annotations discovered via fallback text scanning
168
+ if (typeof textBefore === "string" &&
169
+ (textBefore.includes("@story") || textBefore.includes("@implements"))) {
156
170
  return true;
157
171
  }
158
172
  }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Internal helpers and types for the valid-annotation-format rule.
3
+ *
4
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
5
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
6
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
7
+ * @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
8
+ * @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
9
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
10
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
11
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
12
+ */
13
+ /**
14
+ * Pending annotation state tracked while iterating through comment lines.
15
+ */
16
+ export interface PendingAnnotation {
17
+ type: "story" | "req";
18
+ value: string;
19
+ hasValue: boolean;
20
+ }
21
+ /**
22
+ * Normalize a raw comment line to make annotation parsing more robust.
23
+ *
24
+ * This function trims whitespace, keeps any annotation tags that appear
25
+ * later in the line, and supports common JSDoc styles such as leading "*".
26
+ *
27
+ * It detects @story, @req, and @implements tags while preserving the rest
28
+ * of the line for downstream logic.
29
+ */
30
+ export declare function normalizeCommentLine(rawLine: string): string;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Internal helpers and types for the valid-annotation-format rule.
4
+ *
5
+ * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
6
+ * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
7
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
8
+ * @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
9
+ * @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
10
+ * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
11
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
12
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.normalizeCommentLine = normalizeCommentLine;
16
+ /**
17
+ * Normalize a raw comment line to make annotation parsing more robust.
18
+ *
19
+ * This function trims whitespace, keeps any annotation tags that appear
20
+ * later in the line, and supports common JSDoc styles such as leading "*".
21
+ *
22
+ * It detects @story, @req, and @implements tags while preserving the rest
23
+ * of the line for downstream logic.
24
+ */
25
+ function normalizeCommentLine(rawLine) {
26
+ const trimmed = rawLine.trim();
27
+ if (!trimmed) {
28
+ return "";
29
+ }
30
+ const annotationMatch = trimmed.match(/@story\b|@req\b|@implements\b/);
31
+ if (!annotationMatch || annotationMatch.index === undefined) {
32
+ const withoutLeadingStar = trimmed.replace(/^\*\s?/, "");
33
+ return withoutLeadingStar;
34
+ }
35
+ return trimmed.slice(annotationMatch.index);
36
+ }
@@ -70,8 +70,7 @@ function normalizeUserOptions(rawOptions) {
70
70
  * @req REQ-REGEX-VALIDATION
71
71
  * @req REQ-BACKWARD-COMPAT
72
72
  */
73
- // eslint-disable-next-line max-params -- Small, centralized helper; keeping parameters explicit is clearer than introducing an options object here.
74
- function resolvePattern(nestedPattern, nestedFieldName, flatPattern, flatFieldName, defaultPattern) {
73
+ function resolvePattern({ nestedPattern, nestedFieldName, flatPattern, flatFieldName, defaultPattern, }) {
75
74
  const effective = typeof nestedPattern === "string"
76
75
  ? { value: nestedPattern, field: nestedFieldName }
77
76
  : typeof flatPattern === "string"
@@ -120,8 +119,20 @@ function resolveOptions(rawOptions) {
120
119
  const flatReqPattern = user?.requirementIdPattern;
121
120
  const nestedReqExample = user?.req?.example;
122
121
  const flatReqExample = user?.requirementIdExample;
123
- const storyPattern = resolvePattern(nestedStoryPattern, "story.pattern", flatStoryPattern, "storyPathPattern", getDefaultStoryPattern());
124
- const reqPattern = resolvePattern(nestedReqPattern, "req.pattern", flatReqPattern, "requirementIdPattern", getDefaultReqPattern());
122
+ const storyPattern = resolvePattern({
123
+ nestedPattern: nestedStoryPattern,
124
+ nestedFieldName: "story.pattern",
125
+ flatPattern: flatStoryPattern,
126
+ flatFieldName: "storyPathPattern",
127
+ defaultPattern: getDefaultStoryPattern(),
128
+ });
129
+ const reqPattern = resolvePattern({
130
+ nestedPattern: nestedReqPattern,
131
+ nestedFieldName: "req.pattern",
132
+ flatPattern: flatReqPattern,
133
+ flatFieldName: "requirementIdPattern",
134
+ defaultPattern: getDefaultReqPattern(),
135
+ });
125
136
  const storyExample = resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
126
137
  const reqExample = resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
127
138
  resolvedDefaults = {
@@ -57,18 +57,23 @@ function collapseAnnotationValue(value) {
57
57
  * @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
58
58
  */
59
59
  function getFixedStoryPath(original) {
60
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-SAFE - Skip auto-fix entirely for paths containing directory traversal segments ("..").
60
61
  if (original.includes("..")) {
61
62
  return null;
62
63
  }
64
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT - Do not modify paths that already end with ".story.md" since they are already in the correct format.
63
65
  if (/\.story\.md$/.test(original)) {
64
66
  return null;
65
67
  }
68
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - Append the missing ".md" extension when the path already ends with ".story", preserving the existing base path.
66
69
  if (/\.story$/.test(original)) {
67
70
  return `${original}.md`;
68
71
  }
72
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - Upgrade plain ".md" paths to ".story.md" while preserving the rest of the path unchanged.
69
73
  if (/\.md$/.test(original)) {
70
74
  return original.replace(/\.md$/, ".story.md");
71
75
  }
76
+ // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE - For paths with no extension, conservatively append ".story.md" to create a canonical story file path.
72
77
  return `${original}.story.md`;
73
78
  }
74
79
  /**
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Helpers for @implements annotation validation used by valid-annotation-format.
3
+ *
4
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
5
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
6
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
7
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
8
+ */
9
+ import type { ResolvedAnnotationOptions } from "./valid-annotation-options";
10
+ /**
11
+ * Minimum number of tokens required for a valid @implements value:
12
+ * - one story path
13
+ * - at least one requirement ID
14
+ *
15
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
16
+ * @req REQ-IMPLEMENTS-PARSE
17
+ */
18
+ export declare const MIN_IMPLEMENTS_TOKENS = 2;
19
+ /**
20
+ * Report a completely missing @implements value (no story path or req IDs).
21
+ *
22
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
23
+ * @req REQ-FORMAT-VALIDATION
24
+ */
25
+ export declare function reportMissingImplementsValue(context: any, comment: any, options: ResolvedAnnotationOptions): void;
26
+ /**
27
+ * Report a value that has only a story path and no requirement IDs.
28
+ *
29
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
30
+ * @req REQ-FORMAT-VALIDATION
31
+ */
32
+ export declare function reportMissingImplementsReqIds(context: any, comment: any, options: ResolvedAnnotationOptions): void;
33
+ /**
34
+ * Report an invalid story path inside @implements.
35
+ *
36
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
37
+ * @req REQ-FORMAT-VALIDATION
38
+ */
39
+ export declare function reportInvalidImplementsStoryPath(context: any, comment: any, storyPath: string, options: ResolvedAnnotationOptions): void;
40
+ /**
41
+ * Report an invalid requirement ID token inside @implements.
42
+ *
43
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
44
+ * @req REQ-FORMAT-VALIDATION
45
+ * @req REQ-MIXED-SUPPORT
46
+ */
47
+ export declare function reportInvalidImplementsReqId(context: any, comment: any, reqId: string, options: ResolvedAnnotationOptions): void;
48
+ type ImplementsDeps = {
49
+ MIN_IMPLEMENTS_TOKENS: number;
50
+ reportMissingImplementsValue: typeof reportMissingImplementsValue;
51
+ reportMissingImplementsReqIds: typeof reportMissingImplementsReqIds;
52
+ reportInvalidImplementsStoryPath: typeof reportInvalidImplementsStoryPath;
53
+ reportInvalidImplementsReqId: typeof reportInvalidImplementsReqId;
54
+ };
55
+ /**
56
+ * Validate an @implements annotation value.
57
+ *
58
+ * This helper encapsulates the logic previously in valid-annotation-format.ts:
59
+ * - trims the raw value
60
+ * - splits into tokens
61
+ * - enforces MIN_IMPLEMENTS_TOKENS
62
+ * - validates the story path using options.storyPattern
63
+ * - validates each requirement ID using options.reqPattern
64
+ * - delegates reporting to the provided helpers
65
+ *
66
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
67
+ * @req REQ-IMPLEMENTS-PARSE
68
+ * @req REQ-FORMAT-VALIDATION
69
+ * @req REQ-MIXED-SUPPORT
70
+ */
71
+ export declare function validateImplementsAnnotationHelper(deps: ImplementsDeps, context: any, comment: any, args: {
72
+ rawValue: string | null | undefined;
73
+ options: ResolvedAnnotationOptions;
74
+ }): void;
75
+ export {};
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MIN_IMPLEMENTS_TOKENS = void 0;
4
+ exports.reportMissingImplementsValue = reportMissingImplementsValue;
5
+ exports.reportMissingImplementsReqIds = reportMissingImplementsReqIds;
6
+ exports.reportInvalidImplementsStoryPath = reportInvalidImplementsStoryPath;
7
+ exports.reportInvalidImplementsReqId = reportInvalidImplementsReqId;
8
+ exports.validateImplementsAnnotationHelper = validateImplementsAnnotationHelper;
9
+ const valid_annotation_utils_1 = require("./valid-annotation-utils");
10
+ /**
11
+ * Minimum number of tokens required for a valid @implements value:
12
+ * - one story path
13
+ * - at least one requirement ID
14
+ *
15
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
16
+ * @req REQ-IMPLEMENTS-PARSE
17
+ */
18
+ exports.MIN_IMPLEMENTS_TOKENS = 2;
19
+ /**
20
+ * Report a completely missing @implements value (no story path or req IDs).
21
+ *
22
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
23
+ * @req REQ-FORMAT-VALIDATION
24
+ */
25
+ function reportMissingImplementsValue(context, comment, options) {
26
+ const { storyExample, reqExample } = options;
27
+ context.report({
28
+ node: comment,
29
+ messageId: "invalidImplementsFormat",
30
+ data: {
31
+ details: `Missing story path and requirement IDs for @implements annotation. Expected a value like "${storyExample} ${reqExample}".`,
32
+ },
33
+ });
34
+ }
35
+ /**
36
+ * Report a value that has only a story path and no requirement IDs.
37
+ *
38
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
39
+ * @req REQ-FORMAT-VALIDATION
40
+ */
41
+ function reportMissingImplementsReqIds(context, comment, options) {
42
+ const { storyExample, reqExample } = options;
43
+ context.report({
44
+ node: comment,
45
+ messageId: "invalidImplementsFormat",
46
+ data: {
47
+ details: `Missing requirement IDs for @implements annotation. Expected a value like "${storyExample} ${reqExample}".`,
48
+ },
49
+ });
50
+ }
51
+ /**
52
+ * Report an invalid story path inside @implements.
53
+ *
54
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
55
+ * @req REQ-FORMAT-VALIDATION
56
+ */
57
+ function reportInvalidImplementsStoryPath(context, comment, storyPath, options) {
58
+ const { storyExample } = options;
59
+ context.report({
60
+ node: comment,
61
+ messageId: "invalidImplementsFormat",
62
+ data: {
63
+ details: `Invalid story path "${storyPath}" for @implements annotation. Expected a path like "${storyExample}".`,
64
+ },
65
+ });
66
+ }
67
+ /**
68
+ * Report an invalid requirement ID token inside @implements.
69
+ *
70
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
71
+ * @req REQ-FORMAT-VALIDATION
72
+ * @req REQ-MIXED-SUPPORT
73
+ */
74
+ function reportInvalidImplementsReqId(context, comment, reqId, options) {
75
+ context.report({
76
+ node: comment,
77
+ messageId: "invalidReqFormat",
78
+ data: {
79
+ details: (0, valid_annotation_utils_1.buildReqErrorMessage)("invalid", reqId, options),
80
+ },
81
+ });
82
+ }
83
+ /**
84
+ * Prepare and validate the token array for an @implements value.
85
+ *
86
+ * Returns { storyPath, reqIds } when tokens are present and structurally valid,
87
+ * or null when a missing-value condition has been reported.
88
+ */
89
+ function parseImplementsTokens(deps, context, comment, rest) {
90
+ const { MIN_IMPLEMENTS_TOKENS, reportMissingImplementsValue, reportMissingImplementsReqIds, } = deps;
91
+ const { rawValue, options } = rest;
92
+ const value = rawValue?.trim() ?? "";
93
+ if (!value) {
94
+ reportMissingImplementsValue(context, comment, options);
95
+ return null;
96
+ }
97
+ const tokens = value.split(/\s+/);
98
+ if (tokens.length < MIN_IMPLEMENTS_TOKENS) {
99
+ reportMissingImplementsReqIds(context, comment, options);
100
+ return null;
101
+ }
102
+ const [storyPath, ...reqIds] = tokens;
103
+ return { storyPath, reqIds };
104
+ }
105
+ /**
106
+ * Validate the parsed storyPath and reqIds against the provided patterns and
107
+ * delegate reporting of any invalid tokens.
108
+ */
109
+ function validateImplementsTokens(deps, context, comment, rest) {
110
+ const { reportInvalidImplementsStoryPath, reportInvalidImplementsReqId } = deps;
111
+ const { parsed, options } = rest;
112
+ const { storyPath, reqIds } = parsed;
113
+ if (!options.storyPattern.test(storyPath)) {
114
+ reportInvalidImplementsStoryPath(context, comment, storyPath, options);
115
+ return;
116
+ }
117
+ for (const reqId of reqIds) {
118
+ if (!options.reqPattern.test(reqId)) {
119
+ reportInvalidImplementsReqId(context, comment, reqId, options);
120
+ }
121
+ }
122
+ }
123
+ /**
124
+ * Validate an @implements annotation value.
125
+ *
126
+ * This helper encapsulates the logic previously in valid-annotation-format.ts:
127
+ * - trims the raw value
128
+ * - splits into tokens
129
+ * - enforces MIN_IMPLEMENTS_TOKENS
130
+ * - validates the story path using options.storyPattern
131
+ * - validates each requirement ID using options.reqPattern
132
+ * - delegates reporting to the provided helpers
133
+ *
134
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
135
+ * @req REQ-IMPLEMENTS-PARSE
136
+ * @req REQ-FORMAT-VALIDATION
137
+ * @req REQ-MIXED-SUPPORT
138
+ */
139
+ function validateImplementsAnnotationHelper(deps, context, comment, args) {
140
+ const { rawValue, options } = args;
141
+ const parsed = parseImplementsTokens(deps, context, comment, {
142
+ rawValue,
143
+ options,
144
+ });
145
+ if (!parsed) {
146
+ return;
147
+ }
148
+ validateImplementsTokens(deps, context, comment, { parsed, options });
149
+ }
@@ -5,12 +5,11 @@
5
5
  * @req REQ-PROJECT-BOUNDARY - Ensure resolved candidate paths remain within the project root
6
6
  * @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
7
7
  */
8
- export interface ReportInvalidPathArgs {
8
+ export interface _ReportInvalidPathArgs {
9
9
  storyPath: string;
10
10
  commentNode: any;
11
11
  context: any;
12
12
  }
13
- export type ReportInvalidPathFn = (args: ReportInvalidPathArgs) => void;
14
13
  export interface HandleBoundaryOptions {
15
14
  storyPath: string;
16
15
  commentNode: any;
@@ -21,7 +20,7 @@ export interface HandleBoundaryOptions {
21
20
  status: "exists" | "missing" | "fs-error" | null;
22
21
  matchedPath?: string | null;
23
22
  } | null;
24
- reportInvalidPath: ReportInvalidPathFn;
23
+ reportInvalidPath: (_args: _ReportInvalidPathArgs) => void;
25
24
  }
26
25
  export interface SecurityValidationOptions {
27
26
  storyPath: string;
@@ -29,7 +28,7 @@ export interface SecurityValidationOptions {
29
28
  context: any;
30
29
  cwd: string;
31
30
  allowAbsolute: boolean;
32
- reportInvalidPath: ReportInvalidPathFn;
31
+ reportInvalidPath: (_args: _ReportInvalidPathArgs) => void;
33
32
  }
34
33
  /**
35
34
  * Analyze candidate paths against the project boundary, returning whether any