@promptci/core 0.0.1
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/LICENSE +201 -0
- package/README.md +30 -0
- package/dist/agent-practices.d.ts +22 -0
- package/dist/agent-practices.d.ts.map +1 -0
- package/dist/agent-practices.js +582 -0
- package/dist/agent-practices.js.map +1 -0
- package/dist/baseline.d.ts +40 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +172 -0
- package/dist/baseline.js.map +1 -0
- package/dist/canonical-owner.d.ts +3 -0
- package/dist/canonical-owner.d.ts.map +1 -0
- package/dist/canonical-owner.js +131 -0
- package/dist/canonical-owner.js.map +1 -0
- package/dist/cheap-model-routing.d.ts +4 -0
- package/dist/cheap-model-routing.d.ts.map +1 -0
- package/dist/cheap-model-routing.js +43 -0
- package/dist/cheap-model-routing.js.map +1 -0
- package/dist/ci-alignment.d.ts +10 -0
- package/dist/ci-alignment.d.ts.map +1 -0
- package/dist/ci-alignment.js +343 -0
- package/dist/ci-alignment.js.map +1 -0
- package/dist/cli-output-filter.d.ts +16 -0
- package/dist/cli-output-filter.d.ts.map +1 -0
- package/dist/cli-output-filter.js +88 -0
- package/dist/cli-output-filter.js.map +1 -0
- package/dist/command-validity.d.ts +12 -0
- package/dist/command-validity.d.ts.map +1 -0
- package/dist/command-validity.js +543 -0
- package/dist/command-validity.js.map +1 -0
- package/dist/conflicts.d.ts +49 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +1065 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/context-analysis.d.ts +15 -0
- package/dist/context-analysis.d.ts.map +1 -0
- package/dist/context-analysis.js +24 -0
- package/dist/context-analysis.js.map +1 -0
- package/dist/context-bloat.d.ts +17 -0
- package/dist/context-bloat.d.ts.map +1 -0
- package/dist/context-bloat.js +148 -0
- package/dist/context-bloat.js.map +1 -0
- package/dist/context-cost.d.ts +4 -0
- package/dist/context-cost.d.ts.map +1 -0
- package/dist/context-cost.js +70 -0
- package/dist/context-cost.js.map +1 -0
- package/dist/context-optimizer.d.ts +14 -0
- package/dist/context-optimizer.d.ts.map +1 -0
- package/dist/context-optimizer.js +219 -0
- package/dist/context-optimizer.js.map +1 -0
- package/dist/context-recommender.d.ts +3 -0
- package/dist/context-recommender.d.ts.map +1 -0
- package/dist/context-recommender.js +329 -0
- package/dist/context-recommender.js.map +1 -0
- package/dist/dead-references.d.ts +33 -0
- package/dist/dead-references.d.ts.map +1 -0
- package/dist/dead-references.js +607 -0
- package/dist/dead-references.js.map +1 -0
- package/dist/detectors.d.ts +11 -0
- package/dist/detectors.d.ts.map +1 -0
- package/dist/detectors.js +160 -0
- package/dist/detectors.js.map +1 -0
- package/dist/duplicates.d.ts +36 -0
- package/dist/duplicates.d.ts.map +1 -0
- package/dist/duplicates.js +436 -0
- package/dist/duplicates.js.map +1 -0
- package/dist/fix-engine.d.ts +16 -0
- package/dist/fix-engine.d.ts.map +1 -0
- package/dist/fix-engine.js +179 -0
- package/dist/fix-engine.js.map +1 -0
- package/dist/framework-packs.d.ts +8 -0
- package/dist/framework-packs.d.ts.map +1 -0
- package/dist/framework-packs.js +22 -0
- package/dist/framework-packs.js.map +1 -0
- package/dist/frameworks/dotnet.d.ts +4 -0
- package/dist/frameworks/dotnet.d.ts.map +1 -0
- package/dist/frameworks/dotnet.js +77 -0
- package/dist/frameworks/dotnet.js.map +1 -0
- package/dist/frameworks/go.d.ts +4 -0
- package/dist/frameworks/go.d.ts.map +1 -0
- package/dist/frameworks/go.js +39 -0
- package/dist/frameworks/go.js.map +1 -0
- package/dist/frameworks/nextjs.d.ts +4 -0
- package/dist/frameworks/nextjs.d.ts.map +1 -0
- package/dist/frameworks/nextjs.js +66 -0
- package/dist/frameworks/nextjs.js.map +1 -0
- package/dist/frameworks/python.d.ts +4 -0
- package/dist/frameworks/python.d.ts.map +1 -0
- package/dist/frameworks/python.js +66 -0
- package/dist/frameworks/python.js.map +1 -0
- package/dist/frameworks/rust.d.ts +4 -0
- package/dist/frameworks/rust.d.ts.map +1 -0
- package/dist/frameworks/rust.js +72 -0
- package/dist/frameworks/rust.js.map +1 -0
- package/dist/frameworks/unity.d.ts +4 -0
- package/dist/frameworks/unity.d.ts.map +1 -0
- package/dist/frameworks/unity.js +74 -0
- package/dist/frameworks/unity.js.map +1 -0
- package/dist/health-score.d.ts +37 -0
- package/dist/health-score.d.ts.map +1 -0
- package/dist/health-score.js +164 -0
- package/dist/health-score.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.d.ts +6 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +67 -0
- package/dist/llm.js.map +1 -0
- package/dist/manifest-consistency.d.ts +37 -0
- package/dist/manifest-consistency.d.ts.map +1 -0
- package/dist/manifest-consistency.js +659 -0
- package/dist/manifest-consistency.js.map +1 -0
- package/dist/mcp-detector.d.ts +4 -0
- package/dist/mcp-detector.d.ts.map +1 -0
- package/dist/mcp-detector.js +131 -0
- package/dist/mcp-detector.js.map +1 -0
- package/dist/missing-context.d.ts +15 -0
- package/dist/missing-context.d.ts.map +1 -0
- package/dist/missing-context.js +517 -0
- package/dist/missing-context.js.map +1 -0
- package/dist/native-tool-preference.d.ts +20 -0
- package/dist/native-tool-preference.d.ts.map +1 -0
- package/dist/native-tool-preference.js +94 -0
- package/dist/native-tool-preference.js.map +1 -0
- package/dist/output-verbosity.d.ts +3 -0
- package/dist/output-verbosity.d.ts.map +1 -0
- package/dist/output-verbosity.js +83 -0
- package/dist/output-verbosity.js.map +1 -0
- package/dist/product-boundary.d.ts +11 -0
- package/dist/product-boundary.d.ts.map +1 -0
- package/dist/product-boundary.js +301 -0
- package/dist/product-boundary.js.map +1 -0
- package/dist/project-type.d.ts +15 -0
- package/dist/project-type.d.ts.map +1 -0
- package/dist/project-type.js +116 -0
- package/dist/project-type.js.map +1 -0
- package/dist/prompt-cache.d.ts +3 -0
- package/dist/prompt-cache.d.ts.map +1 -0
- package/dist/prompt-cache.js +151 -0
- package/dist/prompt-cache.js.map +1 -0
- package/dist/repo-context.d.ts +37 -0
- package/dist/repo-context.d.ts.map +1 -0
- package/dist/repo-context.js +221 -0
- package/dist/repo-context.js.map +1 -0
- package/dist/report.d.ts +87 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +558 -0
- package/dist/report.js.map +1 -0
- package/dist/scan.d.ts +7 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +43 -0
- package/dist/scan.js.map +1 -0
- package/dist/scanner.d.ts +3 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +216 -0
- package/dist/scanner.js.map +1 -0
- package/dist/security-pack.d.ts +4 -0
- package/dist/security-pack.d.ts.map +1 -0
- package/dist/security-pack.js +403 -0
- package/dist/security-pack.js.map +1 -0
- package/dist/stale-instructions.d.ts +20 -0
- package/dist/stale-instructions.d.ts.map +1 -0
- package/dist/stale-instructions.js +269 -0
- package/dist/stale-instructions.js.map +1 -0
- package/dist/suppression.d.ts +64 -0
- package/dist/suppression.d.ts.map +1 -0
- package/dist/suppression.js +338 -0
- package/dist/suppression.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/vague-guidance.d.ts +12 -0
- package/dist/vague-guidance.d.ts.map +1 -0
- package/dist/vague-guidance.js +235 -0
- package/dist/vague-guidance.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dead-reference detector for PromptCI.
|
|
3
|
+
*
|
|
4
|
+
* Scans instruction files for references to other local files and flags
|
|
5
|
+
* ones that don't exist on disk. Catches:
|
|
6
|
+
* - Markdown links: [text](./path/to/file.md)
|
|
7
|
+
* - Reference-style links: [text][ref] with a `[ref]: path` definition
|
|
8
|
+
* - @-file refs: @AGENTS.md | @docs/guide.md
|
|
9
|
+
*
|
|
10
|
+
* External URLs (http/https), anchor-only links (#section), and mailto:
|
|
11
|
+
* are always skipped. Only paths with a recognisable file extension are
|
|
12
|
+
* checked to avoid false-positiving on badge shields, npm package names, etc.
|
|
13
|
+
*
|
|
14
|
+
* Findings are heuristic — wording is intentionally cautious.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'node:fs';
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
import * as crypto from 'node:crypto';
|
|
19
|
+
// ── Patterns ──────────────────────────────────────────────────────────────────
|
|
20
|
+
/** Matches markdown inline links: [label](target) — captures target (may include a title). */
|
|
21
|
+
const MD_LINK_RE = /\[(?:[^\]]*)\]\(([^)]+)\)/g;
|
|
22
|
+
const NESTED_MD_LINK_RE = /\[(?:[^\]]|\]\([^)]*\))*\]\(([^)]+)\)/g;
|
|
23
|
+
/**
|
|
24
|
+
* Matches markdown reference-style link definitions: `[ref]: path/to/file.md "Title"`
|
|
25
|
+
* These are easy to miss because the usage site `[text][ref]` never carries the path —
|
|
26
|
+
* only the definition line does.
|
|
27
|
+
*/
|
|
28
|
+
const LINK_REF_DEF_RE = /^[ \t]{0,3}\[[^\]]+\]:\s*(\S+)/gm;
|
|
29
|
+
/**
|
|
30
|
+
* Matches @-file references: @AGENTS.md, @./path/file.ts, @docs/guide.md
|
|
31
|
+
* Only captures paths that have a recognisable file extension.
|
|
32
|
+
*/
|
|
33
|
+
const AT_FILE_RE = /(?<!\w)@((?:\.{1,2}\/)?[\w./\\-]+\.(?:md|ts|js|mjs|cjs|tsx|jsx|json|yml|yaml|toml|txt|sh|py|cs|go|rs))/g;
|
|
34
|
+
/**
|
|
35
|
+
* Matches backtick-enclosed file paths: `scripts/setup-dev.sh`, `Assets/Scripts/Foo.cs`
|
|
36
|
+
* Only fires when the content looks like a file path:
|
|
37
|
+
* - No spaces (commands like `npm run build` are excluded)
|
|
38
|
+
* - Has a recognisable extension
|
|
39
|
+
* This catches prose references like "see `docs/guide.md`" that aren't markdown links.
|
|
40
|
+
* An optional trailing `:line` or `:line:col` suffix (e.g. `src/gone.ts:42`) is allowed
|
|
41
|
+
* after the extension but excluded from the captured path.
|
|
42
|
+
*/
|
|
43
|
+
const BACKTICK_PATH_RE = /`([^\s`\n]+\.(?:md|sh|py|ts|js|tsx|jsx|mjs|cjs|json|yml|yaml|toml|cs|go|rs|txt|lock|env|png|jpg|jpeg|gif|svg|webp|pdf))(?::\d+(?::\d+)?)?`/g;
|
|
44
|
+
/**
|
|
45
|
+
* BUG-004: Bullet-list bare file paths in Repository Layout / Project Structure sections.
|
|
46
|
+
* Catches lines like:
|
|
47
|
+
* - docs/runbook.md
|
|
48
|
+
* * src/middleware/auth.rs
|
|
49
|
+
* Requires at least one slash (directory component) to avoid false-positiving on
|
|
50
|
+
* single-word filenames.
|
|
51
|
+
*/
|
|
52
|
+
const BULLET_PATH_RE = /^[ \t]*[-*+]\s+((?:[\w.-]+\/)+[\w.-]+\.(?:md|ts|js|tsx|jsx|mjs|cjs|json|yml|yaml|toml|sh|py|cs|go|rs|txt|png|jpg|jpeg|gif|svg|webp|pdf))\b/gm;
|
|
53
|
+
/**
|
|
54
|
+
* BUG-004: ASCII/Unicode directory-tree listing paths.
|
|
55
|
+
* Catches Unicode tree drawings:
|
|
56
|
+
* ├── docs/runbook.md
|
|
57
|
+
* └── rate-limiting.md
|
|
58
|
+
* and their common ASCII-art equivalents:
|
|
59
|
+
* |-- docs/runbook.md
|
|
60
|
+
* `-- rate-limiting.md
|
|
61
|
+
* The Unicode connector chars are unambiguous, so a bare space run after them is enough.
|
|
62
|
+
* The ASCII fallback chars (`|`, `+`, backtick) are also ordinary punctuation elsewhere
|
|
63
|
+
* (table pipes, code spans), so we require at least one dash in the connector to avoid
|
|
64
|
+
* misreading a markdown table cell as a tree entry.
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* BUG-004: Headings that indicate a repository/file layout section.
|
|
68
|
+
* When a file contains a heading matching this pattern, we also scan for
|
|
69
|
+
* bare paths in bullet lists and directory tree listings.
|
|
70
|
+
*
|
|
71
|
+
* Matches both real markdown headings (`## Project Structure`) and bolded
|
|
72
|
+
* pseudo-headings on their own line (`**Project Structure**`), since authors
|
|
73
|
+
* commonly use bold text in lieu of a heading for this kind of section.
|
|
74
|
+
*/
|
|
75
|
+
const LAYOUT_KEYWORDS_SRC = 'repository\\s+layout|project\\s+structure|file\\s+structure|folder\\s+structure|' +
|
|
76
|
+
'directory\\s+(?:structure|layout|tree|overview)|project\\s+tree|repo\\s+(?:structure|layout|map)|' +
|
|
77
|
+
'file\\s+tree|files?\\s+overview|codebase\\s+(?:map|overview|structure|layout)|layout';
|
|
78
|
+
const LAYOUT_SECTION_HEADING_RE = new RegExp(`^(?:#{1,4}\\s+.*\\b(?:${LAYOUT_KEYWORDS_SRC})\\b|\\*\\*[^*\\n]*\\b(?:${LAYOUT_KEYWORDS_SRC})\\b[^*\\n]*\\*\\*\\s*)`, 'im');
|
|
79
|
+
/**
|
|
80
|
+
* BUG-D1: Creation-intent phrasing. A reference introduced by an instruction to
|
|
81
|
+
* CREATE a file ("Create a file named `.github/workflows/promptci.yml` in your
|
|
82
|
+
* repository") is a template for the reader's own repo, not a claim that the
|
|
83
|
+
* file exists here — so its absence must not be flagged as a broken reference.
|
|
84
|
+
* Requires a creation verb + a file-ish noun + a connector, keeping it specific
|
|
85
|
+
* enough to avoid suppressing ordinary "see `docs/guide.md`" references.
|
|
86
|
+
*
|
|
87
|
+
* The trailing group captures the introduced target path so ONLY that ref is
|
|
88
|
+
* suppressed — an unrelated broken ref that merely shares the line (e.g.
|
|
89
|
+
* "Create a file named `ci.yml`, like the one in `docs/other.md`") is still
|
|
90
|
+
* flagged.
|
|
91
|
+
*/
|
|
92
|
+
const CREATION_INTENT_RE = /\b(?:create|add|generate|scaffold|place|put|save|make)\s+(?:a\s+|an\s+|the\s+|your\s+|this\s+|new\s+)*(?:file|workflow|config(?:uration)?|script|action|manifest|template|module|component)\b[^.\n]*?\b(?:named|called|at|as|in|to|like)\s+[`'"(]*([^\s`'"()]+)/i;
|
|
93
|
+
/** Normalizes a ref for creation-target comparison (slashes, ./ prefix, case). */
|
|
94
|
+
function normalizeRefPath(ref) {
|
|
95
|
+
return stripFragment(ref).replace(/\\/g, '/').replace(/^\.\//, '').toLowerCase();
|
|
96
|
+
}
|
|
97
|
+
/** File extensions we consider worth checking. */
|
|
98
|
+
const CHECKABLE_EXTENSIONS = new Set([
|
|
99
|
+
'.md', '.ts', '.js', '.mjs', '.cjs', '.tsx', '.jsx',
|
|
100
|
+
'.json', '.yml', '.yaml', '.toml', '.txt', '.sh', '.py', '.cs', '.go', '.rs',
|
|
101
|
+
// BUG-006: image and document links (e.g. [./docs/architecture.png]) were previously
|
|
102
|
+
// ignored because their extensions weren't in this set.
|
|
103
|
+
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.pdf',
|
|
104
|
+
]);
|
|
105
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
106
|
+
function isExternalRef(ref) {
|
|
107
|
+
return /^https?:\/\/|^mailto:|^#|^data:/.test(ref.trimStart());
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Strips a markdown link title from a raw link target.
|
|
111
|
+
* `docs/missing.md "The Guide"` → `docs/missing.md`
|
|
112
|
+
* `docs/missing.md 'The Guide'` → `docs/missing.md`
|
|
113
|
+
* Titles are only recognised when quoted, per the CommonMark link title grammar.
|
|
114
|
+
*/
|
|
115
|
+
function stripLinkTitle(raw) {
|
|
116
|
+
const trimmed = raw.trim();
|
|
117
|
+
const angleMatch = /^<([^>]+)>(?:\s+(?:"[^"]*"|'[^']*'))?\s*$/.exec(trimmed);
|
|
118
|
+
if (angleMatch)
|
|
119
|
+
return angleMatch[1];
|
|
120
|
+
const titleMatch = /^(\S+)\s+(?:"[^"]*"|'[^']*')\s*$/.exec(trimmed);
|
|
121
|
+
return titleMatch ? titleMatch[1] : trimmed;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* BUG-009 / BUG-012: Placeholder / example filenames and template paths.
|
|
125
|
+
*
|
|
126
|
+
* Skipped cases:
|
|
127
|
+
* - Common placeholder stems: foo.ts, bar.go, example.py, sample.md
|
|
128
|
+
* - ComponentName-style templates: ComponentName.tsx, MyComponent.tsx
|
|
129
|
+
* - Angle-bracket templates: src/features/<domain>/slice.ts
|
|
130
|
+
* - Numeric-suffix patterns: file1.ts, file2.go
|
|
131
|
+
*/
|
|
132
|
+
const PLACEHOLDER_STEM_RE = /^(foo|bar|baz|qux|quux|example|sample|placeholder|stub|demo|test|your[-_]?file|my[-_]?file|filename|file[-_]?name)(\.\w+)?$/i;
|
|
133
|
+
/**
|
|
134
|
+
* BUG-C2: Generic bare filenames that commonly appear as naming-convention examples
|
|
135
|
+
* in instruction files (e.g. "name your files like `tokens.json`") rather than as
|
|
136
|
+
* references to specific existing files. Only suppressed when the reference has NO
|
|
137
|
+
* directory prefix — `src/tokens.json` is specific enough to check.
|
|
138
|
+
*/
|
|
139
|
+
const GENERIC_BARE_FILENAMES = new Set([
|
|
140
|
+
'tokens.json', 'config.json', 'settings.json', 'schema.json', 'manifest.json',
|
|
141
|
+
'types.ts', 'types.d.ts', 'index.ts', 'index.js', 'main.ts', 'main.go',
|
|
142
|
+
'app.ts', 'app.js', 'constants.ts', 'utils.ts', 'helpers.ts', 'globals.ts',
|
|
143
|
+
'routes.ts', 'store.ts', 'models.ts', 'services.ts',
|
|
144
|
+
// Universal project root files — always present in their respective ecosystems;
|
|
145
|
+
// flagging them as dead references is always a false positive in real projects.
|
|
146
|
+
'package.json', 'package-lock.json', 'pnpm-lock.yaml', 'yarn.lock',
|
|
147
|
+
'tsconfig.json', 'tsconfig.base.json',
|
|
148
|
+
'pyproject.toml', 'cargo.toml',
|
|
149
|
+
// Framework config roots
|
|
150
|
+
'next.config.js', 'next.config.ts', 'next.config.mjs',
|
|
151
|
+
'vite.config.ts', 'vite.config.js', 'vite.config.mjs',
|
|
152
|
+
'eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs',
|
|
153
|
+
'.eslintrc.json', '.eslintrc.js', '.eslintrc.yml',
|
|
154
|
+
'jest.config.ts', 'jest.config.js', 'jest.config.mjs',
|
|
155
|
+
'vitest.config.ts', 'vitest.config.mts',
|
|
156
|
+
]);
|
|
157
|
+
/**
|
|
158
|
+
* BUG-012: Component/entity name templates used in naming-convention examples.
|
|
159
|
+
* Matches PascalCase names that are clearly "fill in the blank" placeholders.
|
|
160
|
+
*/
|
|
161
|
+
// Allow multiple extension segments so ComponentName.test.tsx is also caught
|
|
162
|
+
const COMPONENT_TEMPLATE_RE = /^(ComponentName|MyComponent|YourComponent|WidgetName|FeatureName|ModuleName|ServiceName|ModelName|EntityName|ResourceName|DomainName|ControllerName|ScreenName|PageName)(?:\.\w+)*$/i;
|
|
163
|
+
function isPlaceholderRef(ref) {
|
|
164
|
+
// BUG-012: Angle-bracket template placeholders: src/features/<domain>/slice.ts
|
|
165
|
+
if (/<[A-Za-z][A-Za-z0-9_-]*>/.test(ref))
|
|
166
|
+
return true;
|
|
167
|
+
// BUG-009: Curly-brace template variables: public/locales/{locale}/{namespace}.json
|
|
168
|
+
// These are pattern descriptions, not real file paths.
|
|
169
|
+
if (/\{[A-Za-z][A-Za-z0-9_-]*\}/.test(ref))
|
|
170
|
+
return true;
|
|
171
|
+
// BUG-006 (eval): Ellipsis segments ("...") are glob placeholders, not real path components.
|
|
172
|
+
// e.g. "src/test/java/.../unit/" is a structural description, not a literal path.
|
|
173
|
+
if (ref.split('/').includes('...'))
|
|
174
|
+
return true;
|
|
175
|
+
const base = path.basename(stripFragment(ref));
|
|
176
|
+
// "Real" directory means a path segment beyond a bare `./` or `.\` prefix.
|
|
177
|
+
// `./sample.md` → dir = '.' → no real directory (still a bare ref)
|
|
178
|
+
// `scripts/test.sh` → dir = 'scripts' → real directory
|
|
179
|
+
const dir = path.dirname(stripFragment(ref));
|
|
180
|
+
const hasRealDirectory = dir !== '.' && dir !== '' && dir !== '.\\' && dir !== './';
|
|
181
|
+
// Common generic placeholder stems — only suppress when there is no real directory prefix.
|
|
182
|
+
// A path like `scripts/test.sh` is a real reference even though "test" is a generic stem;
|
|
183
|
+
// a bare `test.sh` or `./test.sh` is almost certainly a naming-convention example.
|
|
184
|
+
if (!hasRealDirectory && PLACEHOLDER_STEM_RE.test(base))
|
|
185
|
+
return true;
|
|
186
|
+
// BUG-012: PascalCase component/entity name templates
|
|
187
|
+
if (COMPONENT_TEMPLATE_RE.test(base))
|
|
188
|
+
return true;
|
|
189
|
+
const hasDirectoryComponent = hasRealDirectory;
|
|
190
|
+
if (!hasDirectoryComponent && GENERIC_BARE_FILENAMES.has(base.toLowerCase()))
|
|
191
|
+
return true;
|
|
192
|
+
// BUG-005 (eval): Filenames whose stem literally contains a naming-convention term
|
|
193
|
+
// (camelCase, PascalCase, kebab-case, snake_case) are almost certainly illustrative
|
|
194
|
+
// examples in a naming-convention section, not references to real project files.
|
|
195
|
+
// e.g. "useCamelCase.ts", "camelCase.types.ts", "my-kebab-case-component.tsx"
|
|
196
|
+
if (/camelCase|CamelCase|PascalCase|pascalCase|kebab[-_]?[Cc]ase|snake_?[Cc]ase/.test(base))
|
|
197
|
+
return true;
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* BUG-008: Commonly gitignored credential and environment config files, plus
|
|
202
|
+
* known tool output directories whose contents are never committed.
|
|
203
|
+
*
|
|
204
|
+
* These files/paths are intentionally absent from the repo. Flagging them as
|
|
205
|
+
* dead references is a false positive; instruction files legitimately reference
|
|
206
|
+
* them as "where to put your credentials / local config / tool output".
|
|
207
|
+
*
|
|
208
|
+
* Matched against the full ref path (not just basename) so that
|
|
209
|
+
* `config/secrets.py` and `secrets.py` are both caught.
|
|
210
|
+
*/
|
|
211
|
+
const GITIGNORED_CREDENTIAL_PATTERNS = [
|
|
212
|
+
// PromptCI output directory — config and reports are user-created, never committed
|
|
213
|
+
/(?:^|[/\\])\.promptci[/\\]/,
|
|
214
|
+
// Python secrets / credentials
|
|
215
|
+
/(?:^|[\\/])secrets?\.\w+$/i,
|
|
216
|
+
/(?:^|[\\/])credentials?\.\w+$/i,
|
|
217
|
+
// .NET environment-specific appsettings (Development / Production are always gitignored)
|
|
218
|
+
/appsettings\.(?:Development|Production|Staging|Local)\.\w+$/i,
|
|
219
|
+
// .env files of any form
|
|
220
|
+
/(?:^|[\\/])\.env(?:\.\w+)?$/i,
|
|
221
|
+
// Generic local-override configs
|
|
222
|
+
/(?:^|[\\/])local\.(?:json|yaml|yml|toml|ini|conf|config)$/i,
|
|
223
|
+
/\.local\.\w+$/i,
|
|
224
|
+
// Named private-key files without a cert/key extension (e.g. ~/.ssh/id_rsa)
|
|
225
|
+
/(?:^|[\\/])(?:private(?:key)?|id_rsa|id_ed25519)(?:\.\w+)?$/i,
|
|
226
|
+
// Certificate/key file EXTENSIONS, regardless of stem — `certs/server.pem`,
|
|
227
|
+
// `config/client.key`, and a bare `.pem` must all be treated the same way.
|
|
228
|
+
/\.(?:pem|key|pfx|p12)$/i,
|
|
229
|
+
];
|
|
230
|
+
function isGitignored(ref) {
|
|
231
|
+
const normalised = ref.replace(/\\/g, '/');
|
|
232
|
+
return GITIGNORED_CREDENTIAL_PATTERNS.some((re) => re.test(normalised));
|
|
233
|
+
}
|
|
234
|
+
function hasCheckableExtension(ref) {
|
|
235
|
+
const ext = path.extname(ref.split('#')[0] ?? '').toLowerCase();
|
|
236
|
+
return CHECKABLE_EXTENSIONS.has(ext);
|
|
237
|
+
}
|
|
238
|
+
/** Strip query strings and anchor fragments from a path reference. */
|
|
239
|
+
function stripFragment(ref) {
|
|
240
|
+
return ref.split('#')[0].split('?')[0].trim();
|
|
241
|
+
}
|
|
242
|
+
function fileExists(p) {
|
|
243
|
+
try {
|
|
244
|
+
return fs.existsSync(p);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* BUG-C1: Source-code file extensions. When a path has one of these extensions
|
|
252
|
+
* and its parent directory exists (but the file itself doesn't), the reference
|
|
253
|
+
* is likely architecture documentation — reduce confidence accordingly.
|
|
254
|
+
*/
|
|
255
|
+
const SOURCE_CODE_EXTENSIONS = new Set([
|
|
256
|
+
'.go', '.cs', '.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
257
|
+
'.py', '.rs', '.sh', '.java', '.kt', '.swift',
|
|
258
|
+
]);
|
|
259
|
+
/**
|
|
260
|
+
* BUG-002: Compiled/native source extensions that, when encountered as backtick
|
|
261
|
+
* inline spans (NOT markdown links or @-refs) in any instruction file type,
|
|
262
|
+
* should be skipped. These are almost always architecture documentation, not real
|
|
263
|
+
* file references — e.g. `internal/client/errors.go`, `src/components/Button.tsx`,
|
|
264
|
+
* `pkg/registry/`, `PlayerController.cs`.
|
|
265
|
+
*
|
|
266
|
+
* NOTE: .sh, .py, .js are intentionally excluded — shell/Python/JS scripts are often
|
|
267
|
+
* legitimately referenced by name in instruction files (e.g. `scripts/setup-dev.sh`).
|
|
268
|
+
*/
|
|
269
|
+
const BACKTICK_SKIP_SOURCE_EXTENSIONS = new Set([
|
|
270
|
+
'.go', '.rs', '.cs', '.java', '.kt', '.swift',
|
|
271
|
+
'.ts', '.tsx', // TypeScript source — not config/test scripts
|
|
272
|
+
]);
|
|
273
|
+
/**
|
|
274
|
+
* Backtick source paths are usually architecture descriptions, but explicit
|
|
275
|
+
* legacy/old/deprecated/archive examples are often stale file references that
|
|
276
|
+
* should be checked.
|
|
277
|
+
*/
|
|
278
|
+
const STALE_SOURCE_REF_RE = /(?:^|[/\\])(?:legacy|old|deprecated|archive|archived)(?:[/\\]|[-_.])/i;
|
|
279
|
+
/**
|
|
280
|
+
* File types for which backtick-enclosed compiled/native source paths should be
|
|
281
|
+
* skipped. All instruction file types are included because architecture
|
|
282
|
+
* descriptions appear in all of them.
|
|
283
|
+
*
|
|
284
|
+
* BUG-002: Expanded from readme-only to all instruction file types.
|
|
285
|
+
*
|
|
286
|
+
* NOTE: this covers every FileType value, which also means backtick-quoted bare
|
|
287
|
+
* *directory* paths (no extension, e.g. `pkg/registry/`) are always treated as
|
|
288
|
+
* architecture descriptions rather than checked — that is intentional per
|
|
289
|
+
* BUG-002, not an oversight.
|
|
290
|
+
*/
|
|
291
|
+
const SKIP_SOURCE_BACKTICK_FILE_TYPES = new Set([
|
|
292
|
+
'readme', 'claude', 'agents', 'cursor', 'copilot', 'docs', 'prompt', 'unknown',
|
|
293
|
+
]);
|
|
294
|
+
/** Minimum confidence to emit a dead-reference issue. Below this the signal is too noisy. */
|
|
295
|
+
const MIN_CONFIDENCE = 0.45;
|
|
296
|
+
function parentDirExists(p) {
|
|
297
|
+
try {
|
|
298
|
+
return fs.existsSync(path.dirname(p));
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function issueId(normRef) {
|
|
305
|
+
const key = `dead-ref:${normRef}`;
|
|
306
|
+
const hash = crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);
|
|
307
|
+
return `dead-ref-${hash}`;
|
|
308
|
+
}
|
|
309
|
+
// ── Line-number tracking ────────────────────────────────────────────────────
|
|
310
|
+
/** Offsets (into `content`) at which each line starts. `lineOffsets[i]` is line `i + 1`. */
|
|
311
|
+
function buildLineOffsets(content) {
|
|
312
|
+
const offsets = [0];
|
|
313
|
+
for (let i = 0; i < content.length; i++) {
|
|
314
|
+
if (content[i] === '\n')
|
|
315
|
+
offsets.push(i + 1);
|
|
316
|
+
}
|
|
317
|
+
return offsets;
|
|
318
|
+
}
|
|
319
|
+
/** 1-indexed line number containing the given character offset. */
|
|
320
|
+
function lineForOffset(lineOffsets, offset) {
|
|
321
|
+
let lo = 0;
|
|
322
|
+
let hi = lineOffsets.length - 1;
|
|
323
|
+
while (lo < hi) {
|
|
324
|
+
const mid = (lo + hi + 1) >> 1;
|
|
325
|
+
if (lineOffsets[mid] <= offset)
|
|
326
|
+
lo = mid;
|
|
327
|
+
else
|
|
328
|
+
hi = mid - 1;
|
|
329
|
+
}
|
|
330
|
+
return lo + 1;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* BUG-002 (eval): Extract implied documentation paths from fenced code blocks inside
|
|
334
|
+
* Repository Layout sections. Handles listings where a parent directory line
|
|
335
|
+
* ("docs/") is followed by indented child file entries (" runbook.md").
|
|
336
|
+
*
|
|
337
|
+
* Only extracts .md files — source code file references in layout blocks are
|
|
338
|
+
* intentionally skipped to avoid false positives in test/instruction-only repos
|
|
339
|
+
* where no source files are present on disk.
|
|
340
|
+
*
|
|
341
|
+
* Called only when the file is known to have a layout section heading.
|
|
342
|
+
*/
|
|
343
|
+
const CODE_BLOCK_LAYOUT_EXT_RE = /\.(?:md|png|jpg|jpeg|gif|svg|webp|pdf)$/i;
|
|
344
|
+
function parseLayoutEntry(rawLine) {
|
|
345
|
+
const withoutComment = rawLine.replace(/\s*#.*$/, '');
|
|
346
|
+
const verticalPrefix = withoutComment.match(/^[ \t│|]*/)?.[0] ?? '';
|
|
347
|
+
const indent = verticalPrefix.replace(/[│|]/g, ' ').length;
|
|
348
|
+
const trimmed = withoutComment.trim();
|
|
349
|
+
if (!trimmed)
|
|
350
|
+
return null;
|
|
351
|
+
const connectorMatch = /^(?:[│|]\s*)*(?:[├└][-─\s]+|[|+`][-─]+\s*)(.+)$/.exec(trimmed);
|
|
352
|
+
const entry = (connectorMatch ? connectorMatch[1] : trimmed)?.trim() ?? '';
|
|
353
|
+
if (!entry)
|
|
354
|
+
return null;
|
|
355
|
+
if (!connectorMatch && /\s/.test(entry))
|
|
356
|
+
return null;
|
|
357
|
+
return {
|
|
358
|
+
entry,
|
|
359
|
+
indent,
|
|
360
|
+
hasConnector: Boolean(connectorMatch),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function collectLayoutEntryPaths(lines, lineOffsets, startOffset) {
|
|
364
|
+
const results = [];
|
|
365
|
+
const stack = [];
|
|
366
|
+
let lineOffset = startOffset;
|
|
367
|
+
for (const rawLine of lines) {
|
|
368
|
+
const thisLineOffset = lineOffset;
|
|
369
|
+
lineOffset += rawLine.length + 1;
|
|
370
|
+
const parsed = parseLayoutEntry(rawLine);
|
|
371
|
+
if (!parsed)
|
|
372
|
+
continue;
|
|
373
|
+
const entry = parsed.entry;
|
|
374
|
+
const isDir = /\/$/.test(entry);
|
|
375
|
+
if (isDir) {
|
|
376
|
+
const parent = [...stack].reverse().find((item) => item.indent < parsed.indent || (item.indent === parsed.indent && item.plain));
|
|
377
|
+
const dirPath = `${parent?.path ?? ''}${entry}`;
|
|
378
|
+
while (stack.length > 0 && stack[stack.length - 1].indent >= parsed.indent) {
|
|
379
|
+
stack.pop();
|
|
380
|
+
}
|
|
381
|
+
stack.push({ indent: parsed.indent, path: dirPath, plain: !parsed.hasConnector });
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
if (!CODE_BLOCK_LAYOUT_EXT_RE.test(entry))
|
|
385
|
+
continue;
|
|
386
|
+
const parent = [...stack].reverse().find((item) => item.indent < parsed.indent || (item.indent === parsed.indent && item.plain));
|
|
387
|
+
const ref = /[/\\]/.test(entry) ? entry : `${parent?.path ?? ''}${entry}`;
|
|
388
|
+
results.push({
|
|
389
|
+
ref,
|
|
390
|
+
evidence: rawLine.trim(),
|
|
391
|
+
line: lineForOffset(lineOffsets, thisLineOffset),
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
return results;
|
|
395
|
+
}
|
|
396
|
+
function extractCodeBlockLayoutPaths(content, lineOffsets) {
|
|
397
|
+
const results = [];
|
|
398
|
+
const fenceRe = /^```[^\n]*\n([\s\S]*?)^```/gm;
|
|
399
|
+
let blockMatch;
|
|
400
|
+
while ((blockMatch = fenceRe.exec(content)) !== null) {
|
|
401
|
+
const blockText = blockMatch[1] ?? '';
|
|
402
|
+
const firstLineEnd = content.indexOf('\n', blockMatch.index);
|
|
403
|
+
let lineOffset = firstLineEnd === -1 ? blockMatch.index : firstLineEnd + 1;
|
|
404
|
+
const lines = blockText.split('\n');
|
|
405
|
+
results.push(...collectLayoutEntryPaths(lines, lineOffsets, lineOffset));
|
|
406
|
+
}
|
|
407
|
+
return results;
|
|
408
|
+
}
|
|
409
|
+
function extractLayoutTreePaths(content, lineOffsets) {
|
|
410
|
+
return collectLayoutEntryPaths(content.split('\n'), lineOffsets, 0);
|
|
411
|
+
}
|
|
412
|
+
function collectRefs(content, opts = {}) {
|
|
413
|
+
const found = [];
|
|
414
|
+
const lineOffsets = buildLineOffsets(content);
|
|
415
|
+
// Markdown links: [label](target) or [label](target "Title")
|
|
416
|
+
let m;
|
|
417
|
+
const linkRe = new RegExp(MD_LINK_RE.source, MD_LINK_RE.flags);
|
|
418
|
+
while ((m = linkRe.exec(content)) !== null) {
|
|
419
|
+
if (m.index >= 2 && content[m.index - 2] === '[' && content[m.index - 1] === '!')
|
|
420
|
+
continue;
|
|
421
|
+
const raw = m[1] ?? '';
|
|
422
|
+
const ref = stripFragment(stripLinkTitle(raw));
|
|
423
|
+
if (ref && !isExternalRef(ref) && hasCheckableExtension(ref)) {
|
|
424
|
+
found.push({ ref, evidence: `[...](${raw})`, line: lineForOffset(lineOffsets, m.index) });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
const nestedLinkRe = new RegExp(NESTED_MD_LINK_RE.source, NESTED_MD_LINK_RE.flags);
|
|
428
|
+
while ((m = nestedLinkRe.exec(content)) !== null) {
|
|
429
|
+
const labelPart = m[0].slice(0, m[0].lastIndexOf(']('));
|
|
430
|
+
if (!labelPart.includes(']('))
|
|
431
|
+
continue;
|
|
432
|
+
const raw = m[1] ?? '';
|
|
433
|
+
const ref = stripFragment(stripLinkTitle(raw));
|
|
434
|
+
if (ref && !isExternalRef(ref) && hasCheckableExtension(ref)) {
|
|
435
|
+
found.push({ ref, evidence: `[...](${raw})`, line: lineForOffset(lineOffsets, m.index) });
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// Reference-style link definitions: [ref]: docs/missing.md "Title"
|
|
439
|
+
const linkDefRe = new RegExp(LINK_REF_DEF_RE.source, LINK_REF_DEF_RE.flags);
|
|
440
|
+
while ((m = linkDefRe.exec(content)) !== null) {
|
|
441
|
+
const raw = m[1] ?? '';
|
|
442
|
+
const ref = stripFragment(raw);
|
|
443
|
+
if (ref && !isExternalRef(ref) && hasCheckableExtension(ref)) {
|
|
444
|
+
found.push({ ref, evidence: m[0].trim(), line: lineForOffset(lineOffsets, m.index) });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// @-file refs
|
|
448
|
+
const atRe = new RegExp(AT_FILE_RE.source, AT_FILE_RE.flags);
|
|
449
|
+
while ((m = atRe.exec(content)) !== null) {
|
|
450
|
+
const ref = m[1] ?? '';
|
|
451
|
+
if (ref && !isExternalRef(ref)) {
|
|
452
|
+
found.push({ ref, evidence: `@${ref}`, line: lineForOffset(lineOffsets, m.index) });
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// Backtick file paths: `scripts/setup-dev.sh`, `Assets/Scripts/Foo.cs`
|
|
456
|
+
// No spaces = file path not a command; must have a recognisable extension.
|
|
457
|
+
// Skip glob patterns (contain * or ?) — those are pattern documentation, not file refs.
|
|
458
|
+
// BUG-002: In all instruction file types (CLAUDE.md, AGENTS.md, .cursorrules, copilot, etc.)
|
|
459
|
+
// skip backtick-quoted compiled/native source files — they are almost always architecture
|
|
460
|
+
// descriptions (e.g. `internal/client/errors.go`), not references to committed files.
|
|
461
|
+
// Shell (.sh), Python (.py), and JS files are kept because they CAN be real script refs.
|
|
462
|
+
const btRe = new RegExp(BACKTICK_PATH_RE.source, BACKTICK_PATH_RE.flags);
|
|
463
|
+
while ((m = btRe.exec(content)) !== null) {
|
|
464
|
+
const ref = m[1] ?? '';
|
|
465
|
+
if (ref && !isExternalRef(ref) && !ref.includes('*') && !ref.includes('?')) {
|
|
466
|
+
if (opts.skipSourceBackticks) {
|
|
467
|
+
const ext = path.extname(ref).toLowerCase();
|
|
468
|
+
if (BACKTICK_SKIP_SOURCE_EXTENSIONS.has(ext) && !STALE_SOURCE_REF_RE.test(ref))
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
found.push({ ref, evidence: `\`${ref}\``, line: lineForOffset(lineOffsets, m.index) });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// BUG-004: Bare paths in Repository Layout / Project Structure sections.
|
|
475
|
+
// These appear as bullet list items or ASCII tree lines without backticks.
|
|
476
|
+
if (LAYOUT_SECTION_HEADING_RE.test(content)) {
|
|
477
|
+
const bulletRe = new RegExp(BULLET_PATH_RE.source, BULLET_PATH_RE.flags);
|
|
478
|
+
while ((m = bulletRe.exec(content)) !== null) {
|
|
479
|
+
const ref = m[1] ?? '';
|
|
480
|
+
if (ref && !isExternalRef(ref)) {
|
|
481
|
+
found.push({ ref, evidence: m[0].trim(), line: lineForOffset(lineOffsets, m.index) });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
found.push(...extractLayoutTreePaths(content, lineOffsets));
|
|
485
|
+
// BUG-002 (eval): Fenced code blocks in layout sections may list documentation
|
|
486
|
+
// files as indented children under a parent directory line ("docs/\n runbook.md").
|
|
487
|
+
for (const item of extractCodeBlockLayoutPaths(content, lineOffsets)) {
|
|
488
|
+
found.push(item);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
// BUG-D1: Drop only the reference the line instructs the reader to CREATE
|
|
492
|
+
// (a template for their repo), not every ref that happens to share the line.
|
|
493
|
+
const contentLines = content.split('\n');
|
|
494
|
+
return found.filter(({ ref, line }) => {
|
|
495
|
+
const lineText = contentLines[line - 1] ?? '';
|
|
496
|
+
const m = CREATION_INTENT_RE.exec(lineText);
|
|
497
|
+
if (!m)
|
|
498
|
+
return true;
|
|
499
|
+
const target = normalizeRefPath(m[1] ?? '');
|
|
500
|
+
if (!target)
|
|
501
|
+
return true;
|
|
502
|
+
const refNorm = normalizeRefPath(ref);
|
|
503
|
+
// Suppress the ref that IS the creation target (allow basename-level match so
|
|
504
|
+
// a `path/to/x.yml` ref matches a captured `x.yml` and vice-versa).
|
|
505
|
+
const isTarget = refNorm === target ||
|
|
506
|
+
refNorm.endsWith(`/${target}`) ||
|
|
507
|
+
target.endsWith(`/${refNorm}`);
|
|
508
|
+
return !isTarget;
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
// ── Detector ─────────────────────────────────────────────────────────────────
|
|
512
|
+
/**
|
|
513
|
+
* Detect broken file references across all instruction files.
|
|
514
|
+
*
|
|
515
|
+
* @param files Instruction files to scan.
|
|
516
|
+
* @param repoRoot Absolute path to repo root — used as a second resolution base.
|
|
517
|
+
*/
|
|
518
|
+
/**
|
|
519
|
+
* BUG-004 / BUG-005: Group all unresolved references by their normalised path so
|
|
520
|
+
* that the same dead reference appearing in multiple instruction files produces ONE
|
|
521
|
+
* consolidated issue (listing all referencing files) instead of N separate issues.
|
|
522
|
+
*
|
|
523
|
+
* Previously each (filePath, ref) pair produced its own issue — a single dead path
|
|
524
|
+
* referenced in both CLAUDE.md and AGENTS.md would fire twice, inflating issue counts
|
|
525
|
+
* and health-score penalties.
|
|
526
|
+
*/
|
|
527
|
+
export function detectDeadReferences(files, repoRoot) {
|
|
528
|
+
// normRef → { displayRef, filePaths, fileLines, evidenceLines, minConfidence }
|
|
529
|
+
const byRef = new Map();
|
|
530
|
+
for (const file of files) {
|
|
531
|
+
const fileDir = path.dirname(file.path);
|
|
532
|
+
const refs = collectRefs(file.content, {
|
|
533
|
+
skipSourceBackticks: SKIP_SOURCE_BACKTICK_FILE_TYPES.has(file.fileType),
|
|
534
|
+
});
|
|
535
|
+
for (const { ref, evidence, line } of refs) {
|
|
536
|
+
// BUG-009: Skip obvious placeholder/example filenames (foo.ts, bar.go, etc.)
|
|
537
|
+
if (isPlaceholderRef(ref))
|
|
538
|
+
continue;
|
|
539
|
+
// BUG-008: Skip intentionally gitignored credential/config files.
|
|
540
|
+
if (isGitignored(ref))
|
|
541
|
+
continue;
|
|
542
|
+
// Resolve relative to the instruction file first, then repo root.
|
|
543
|
+
const candidates = [
|
|
544
|
+
path.resolve(fileDir, ref),
|
|
545
|
+
path.resolve(repoRoot, ref),
|
|
546
|
+
];
|
|
547
|
+
if (candidates.some(fileExists))
|
|
548
|
+
continue;
|
|
549
|
+
// BUG-C1: Source-code paths whose parent directory exists → lower confidence
|
|
550
|
+
// (likely documentation references, not broken links).
|
|
551
|
+
const ext = path.extname(ref.split('#')[0] ?? '').toLowerCase();
|
|
552
|
+
const isSourceCodeRef = SOURCE_CODE_EXTENSIONS.has(ext);
|
|
553
|
+
const anyParentExists = candidates.some(parentDirExists);
|
|
554
|
+
const confidence = isSourceCodeRef && anyParentExists ? 0.5 : 0.85;
|
|
555
|
+
// Normalise for deduplication (case-insensitive, forward slashes)
|
|
556
|
+
const normRef = ref.toLowerCase().replace(/\\/g, '/');
|
|
557
|
+
const evidenceLine = `Reference: ${evidence}`;
|
|
558
|
+
if (!byRef.has(normRef)) {
|
|
559
|
+
byRef.set(normRef, {
|
|
560
|
+
displayRef: ref,
|
|
561
|
+
filePaths: new Set(),
|
|
562
|
+
fileLines: new Map(),
|
|
563
|
+
evidenceLines: [],
|
|
564
|
+
minConfidence: confidence,
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
const entry = byRef.get(normRef);
|
|
568
|
+
entry.filePaths.add(file.path);
|
|
569
|
+
const existingLine = entry.fileLines.get(file.path);
|
|
570
|
+
if (existingLine === undefined || line < existingLine) {
|
|
571
|
+
entry.fileLines.set(file.path, line);
|
|
572
|
+
}
|
|
573
|
+
if (!entry.evidenceLines.includes(evidenceLine)) {
|
|
574
|
+
entry.evidenceLines.push(evidenceLine);
|
|
575
|
+
}
|
|
576
|
+
entry.minConfidence = Math.min(entry.minConfidence, confidence);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
const issues = [];
|
|
580
|
+
const MIN_CONFIDENCE_THRESHOLD = MIN_CONFIDENCE;
|
|
581
|
+
for (const [normRef, { displayRef, filePaths, fileLines, evidenceLines, minConfidence }] of byRef) {
|
|
582
|
+
// BUG-002: Skip findings below the minimum confidence threshold.
|
|
583
|
+
// Low-confidence source-code path references are almost certainly architecture docs.
|
|
584
|
+
if (minConfidence < MIN_CONFIDENCE_THRESHOLD)
|
|
585
|
+
continue;
|
|
586
|
+
const allFilePaths = [...filePaths];
|
|
587
|
+
issues.push({
|
|
588
|
+
id: issueId(normRef),
|
|
589
|
+
severity: 'warning',
|
|
590
|
+
category: 'structure',
|
|
591
|
+
title: `Broken file reference: ${path.basename(stripFragment(displayRef))}`,
|
|
592
|
+
summary: `This instruction file references "${displayRef}" which does not appear to exist. ` +
|
|
593
|
+
`The file may have been renamed, moved, or deleted.`,
|
|
594
|
+
filePaths: allFilePaths,
|
|
595
|
+
locations: allFilePaths.map((fp) => {
|
|
596
|
+
const startLine = fileLines.get(fp);
|
|
597
|
+
return startLine !== undefined ? { filePath: fp, startLine } : { filePath: fp };
|
|
598
|
+
}),
|
|
599
|
+
evidence: evidenceLines,
|
|
600
|
+
recommendation: 'Verify the path is correct relative to the instruction file location, ' +
|
|
601
|
+
'or remove the reference if the file no longer exists.',
|
|
602
|
+
confidence: minConfidence,
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
return issues;
|
|
606
|
+
}
|
|
607
|
+
//# sourceMappingURL=dead-references.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-references.js","sourceRoot":"","sources":["../src/dead-references.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PromptCiIssue } from './types.js';
|
|
2
|
+
import type { RepoContext } from './repo-context.js';
|
|
3
|
+
export type DetectorGroup = 'scan' | 'context';
|
|
4
|
+
export type DetectorDefinition = {
|
|
5
|
+
id: string;
|
|
6
|
+
group: DetectorGroup;
|
|
7
|
+
run: (context: RepoContext) => PromptCiIssue[];
|
|
8
|
+
};
|
|
9
|
+
export declare const DETECTORS: DetectorDefinition[];
|
|
10
|
+
export declare function runDetectors(context: RepoContext, group: DetectorGroup): PromptCiIssue[];
|
|
11
|
+
//# sourceMappingURL=detectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detectors.d.ts","sourceRoot":"","sources":["../src/detectors.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,aAAa,CAAC;IACrB,GAAG,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,aAAa,EAAE,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,kBAAkB,EAmIzC,CAAC;AAEF,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,aAAa,GAAG,aAAa,EAAE,CAIxF"}
|