@promptci/core 0.0.1 → 0.1.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.
- package/LICENSE +201 -201
- package/README.md +29 -30
- package/dist/agent-practices.d.ts +2 -0
- package/dist/agent-practices.d.ts.map +1 -1
- package/dist/agent-practices.js +91 -51
- package/dist/agent-practices.js.map +1 -1
- package/dist/ai-config.d.ts +139 -0
- package/dist/ai-config.d.ts.map +1 -0
- package/dist/ai-config.js +523 -0
- package/dist/ai-config.js.map +1 -0
- package/dist/baseline.d.ts +17 -1
- package/dist/baseline.d.ts.map +1 -1
- package/dist/baseline.js +5 -1
- package/dist/baseline.js.map +1 -1
- package/dist/buried-critical.d.ts +17 -0
- package/dist/buried-critical.d.ts.map +1 -0
- package/dist/buried-critical.js +112 -0
- package/dist/buried-critical.js.map +1 -0
- package/dist/canonical-owner.d.ts.map +1 -1
- package/dist/canonical-owner.js +18 -8
- package/dist/canonical-owner.js.map +1 -1
- package/dist/command-validity.d.ts.map +1 -1
- package/dist/command-validity.js +53 -74
- package/dist/command-validity.js.map +1 -1
- package/dist/conflicts.d.ts.map +1 -1
- package/dist/conflicts.js +1 -42
- package/dist/conflicts.js.map +1 -1
- package/dist/context-analysis.d.ts.map +1 -1
- package/dist/context-analysis.js +13 -2
- package/dist/context-analysis.js.map +1 -1
- package/dist/context-bloat.d.ts +19 -0
- package/dist/context-bloat.d.ts.map +1 -1
- package/dist/context-bloat.js +82 -9
- package/dist/context-bloat.js.map +1 -1
- package/dist/context-optimizer.d.ts.map +1 -1
- package/dist/context-optimizer.js +15 -54
- package/dist/context-optimizer.js.map +1 -1
- package/dist/copilot-instructions-detector.d.ts +24 -0
- package/dist/copilot-instructions-detector.d.ts.map +1 -0
- package/dist/copilot-instructions-detector.js +145 -0
- package/dist/copilot-instructions-detector.js.map +1 -0
- package/dist/cursor-rules-detector.d.ts +17 -0
- package/dist/cursor-rules-detector.d.ts.map +1 -0
- package/dist/cursor-rules-detector.js +110 -0
- package/dist/cursor-rules-detector.js.map +1 -0
- package/dist/custom-rules.d.ts +52 -0
- package/dist/custom-rules.d.ts.map +1 -0
- package/dist/custom-rules.js +298 -0
- package/dist/custom-rules.js.map +1 -0
- package/dist/dead-references.js +11 -9
- package/dist/dead-references.js.map +1 -1
- package/dist/detectors.d.ts +1 -3
- package/dist/detectors.d.ts.map +1 -1
- package/dist/detectors.js +46 -53
- package/dist/detectors.js.map +1 -1
- package/dist/duplicates.d.ts.map +1 -1
- package/dist/duplicates.js +10 -5
- package/dist/duplicates.js.map +1 -1
- package/dist/evidence.d.ts +28 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +38 -0
- package/dist/evidence.js.map +1 -0
- package/dist/fix-engine.d.ts +11 -0
- package/dist/fix-engine.d.ts.map +1 -1
- package/dist/fix-engine.js +88 -71
- package/dist/fix-engine.js.map +1 -1
- package/dist/frameworks/python.d.ts.map +1 -1
- package/dist/frameworks/python.js +5 -2
- package/dist/frameworks/python.js.map +1 -1
- package/dist/health-score.d.ts.map +1 -1
- package/dist/health-score.js +1 -0
- package/dist/health-score.js.map +1 -1
- package/dist/hooks-settings-detector.d.ts +17 -0
- package/dist/hooks-settings-detector.d.ts.map +1 -0
- package/dist/hooks-settings-detector.js +228 -0
- package/dist/hooks-settings-detector.js.map +1 -0
- package/dist/index.d.ts +22 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -6
- package/dist/index.js.map +1 -1
- package/dist/manifest-consistency.d.ts +6 -0
- package/dist/manifest-consistency.d.ts.map +1 -1
- package/dist/manifest-consistency.js +26 -5
- package/dist/manifest-consistency.js.map +1 -1
- package/dist/markdown-fences.d.ts +112 -0
- package/dist/markdown-fences.d.ts.map +1 -0
- package/dist/markdown-fences.js +174 -0
- package/dist/markdown-fences.js.map +1 -0
- package/dist/mcp-config-detector.d.ts +20 -0
- package/dist/mcp-config-detector.d.ts.map +1 -0
- package/dist/mcp-config-detector.js +237 -0
- package/dist/mcp-config-detector.js.map +1 -0
- package/dist/missing-context.d.ts.map +1 -1
- package/dist/missing-context.js +2 -52
- package/dist/missing-context.js.map +1 -1
- package/dist/model-budgets.d.ts +69 -0
- package/dist/model-budgets.d.ts.map +1 -0
- package/dist/model-budgets.js +110 -0
- package/dist/model-budgets.js.map +1 -0
- package/dist/negative-instructions.d.ts +26 -0
- package/dist/negative-instructions.d.ts.map +1 -0
- package/dist/negative-instructions.js +174 -0
- package/dist/negative-instructions.js.map +1 -0
- package/dist/path-containment.d.ts +12 -0
- package/dist/path-containment.d.ts.map +1 -0
- package/dist/path-containment.js +53 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/project-type.d.ts +12 -2
- package/dist/project-type.d.ts.map +1 -1
- package/dist/project-type.js +25 -12
- package/dist/project-type.js.map +1 -1
- package/dist/prompt-cache.d.ts.map +1 -1
- package/dist/prompt-cache.js +26 -19
- package/dist/prompt-cache.js.map +1 -1
- package/dist/promptci-gitignore.d.ts +34 -0
- package/dist/promptci-gitignore.d.ts.map +1 -0
- package/dist/promptci-gitignore.js +42 -0
- package/dist/promptci-gitignore.js.map +1 -0
- package/dist/repo-context.d.ts +27 -0
- package/dist/repo-context.d.ts.map +1 -1
- package/dist/repo-context.js +42 -11
- package/dist/repo-context.js.map +1 -1
- package/dist/report.d.ts +1 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +132 -61
- package/dist/report.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +7 -3
- package/dist/scan.js.map +1 -1
- package/dist/scanner.d.ts +13 -1
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +49 -37
- package/dist/scanner.js.map +1 -1
- package/dist/security-pack.d.ts +20 -0
- package/dist/security-pack.d.ts.map +1 -1
- package/dist/security-pack.js +27 -10
- package/dist/security-pack.js.map +1 -1
- package/dist/skills-detector.d.ts +19 -0
- package/dist/skills-detector.d.ts.map +1 -0
- package/dist/skills-detector.js +203 -0
- package/dist/skills-detector.js.map +1 -0
- package/dist/stale-instructions.d.ts +4 -1
- package/dist/stale-instructions.d.ts.map +1 -1
- package/dist/stale-instructions.js +51 -54
- package/dist/stale-instructions.js.map +1 -1
- package/dist/subagents-detector.d.ts +19 -0
- package/dist/subagents-detector.d.ts.map +1 -0
- package/dist/subagents-detector.js +155 -0
- package/dist/subagents-detector.js.map +1 -0
- package/dist/suppression.d.ts.map +1 -1
- package/dist/suppression.js +11 -45
- package/dist/suppression.js.map +1 -1
- package/dist/types.d.ts +28 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +25 -1
- package/dist/types.js.map +1 -1
- package/dist/vague-guidance.d.ts +15 -2
- package/dist/vague-guidance.d.ts.map +1 -1
- package/dist/vague-guidance.js +42 -40
- package/dist/vague-guidance.js.map +1 -1
- package/dist/windsurf-detector.d.ts +22 -0
- package/dist/windsurf-detector.d.ts.map +1 -0
- package/dist/windsurf-detector.js +87 -0
- package/dist/windsurf-detector.js.map +1 -0
- package/dist/within-section-dedup.d.ts +16 -0
- package/dist/within-section-dedup.d.ts.map +1 -0
- package/dist/within-section-dedup.js +153 -0
- package/dist/within-section-dedup.js.map +1 -0
- package/package.json +11 -8
- package/dist/cheap-model-routing.d.ts +0 -4
- package/dist/cheap-model-routing.d.ts.map +0 -1
- package/dist/cheap-model-routing.js +0 -43
- package/dist/cheap-model-routing.js.map +0 -1
- package/dist/cli-output-filter.d.ts +0 -16
- package/dist/cli-output-filter.d.ts.map +0 -1
- package/dist/cli-output-filter.js +0 -88
- package/dist/cli-output-filter.js.map +0 -1
- package/dist/context-cost.d.ts +0 -4
- package/dist/context-cost.d.ts.map +0 -1
- package/dist/context-cost.js +0 -70
- package/dist/context-cost.js.map +0 -1
- package/dist/context-recommender.d.ts +0 -3
- package/dist/context-recommender.d.ts.map +0 -1
- package/dist/context-recommender.js +0 -329
- package/dist/context-recommender.js.map +0 -1
- package/dist/llm.d.ts +0 -6
- package/dist/llm.d.ts.map +0 -1
- package/dist/llm.js +0 -67
- package/dist/llm.js.map +0 -1
- package/dist/mcp-detector.d.ts +0 -4
- package/dist/mcp-detector.d.ts.map +0 -1
- package/dist/mcp-detector.js +0 -131
- package/dist/mcp-detector.js.map +0 -1
- package/dist/native-tool-preference.d.ts +0 -20
- package/dist/native-tool-preference.d.ts.map +0 -1
- package/dist/native-tool-preference.js +0 -94
- package/dist/native-tool-preference.js.map +0 -1
- package/dist/output-verbosity.d.ts +0 -3
- package/dist/output-verbosity.d.ts.map +0 -1
- package/dist/output-verbosity.js +0 -83
- package/dist/output-verbosity.js.map +0 -1
- package/dist/product-boundary.d.ts +0 -11
- package/dist/product-boundary.d.ts.map +0 -1
- package/dist/product-boundary.js +0 -301
- package/dist/product-boundary.js.map +0 -1
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Core data types for PromptCI */
|
|
2
|
-
export type FileType = 'claude' | 'agents' | 'cursor' | 'copilot' | 'readme' | 'docs' | 'prompt' | 'unknown';
|
|
2
|
+
export type FileType = 'claude' | 'agents' | 'cursor' | 'windsurf' | 'copilot' | 'readme' | 'docs' | 'prompt' | 'skill' | 'agent' | 'unknown';
|
|
3
|
+
export declare function isOnDemandFileType(fileType: FileType): boolean;
|
|
3
4
|
export type IssueSeverity = 'info' | 'warning' | 'high' | 'critical';
|
|
4
|
-
export type IssueCategory = 'duplicate' | 'conflict' | 'context_bloat' | 'missing_context' | 'stale_instruction' | 'vague_guidance' | 'structure' | 'command_validity' | 'agent_practices' | 'security';
|
|
5
|
+
export type IssueCategory = 'duplicate' | 'conflict' | 'context_bloat' | 'missing_context' | 'stale_instruction' | 'vague_guidance' | 'structure' | 'command_validity' | 'agent_practices' | 'ai_config' | 'security' | 'custom';
|
|
5
6
|
export type InstructionSection = {
|
|
6
7
|
id: string;
|
|
7
8
|
filePath: string;
|
|
@@ -34,7 +35,15 @@ export type PromptCiIssue = {
|
|
|
34
35
|
}>;
|
|
35
36
|
evidence: string[];
|
|
36
37
|
recommendation: string;
|
|
38
|
+
/** Advisory, human-readable "here's how to fix it" text. Not machine-applied. */
|
|
37
39
|
fixRecipe?: string;
|
|
40
|
+
/**
|
|
41
|
+
* True only when `applyFixRecipe` implements a mechanical, reversible edit for
|
|
42
|
+
* this issue and it is safe to auto-apply. Gates `promptci fix` (see
|
|
43
|
+
* `isRepairable`). Detectors set this deliberately per recipe — it is never
|
|
44
|
+
* inferred from the presence of `fixRecipe`, which is usually advisory prose.
|
|
45
|
+
*/
|
|
46
|
+
autoApplySafe?: boolean;
|
|
38
47
|
confidence: number;
|
|
39
48
|
tags?: string[];
|
|
40
49
|
impact?: 'high' | 'medium' | 'low';
|
|
@@ -44,7 +53,8 @@ export type TopFix = {
|
|
|
44
53
|
reason: string;
|
|
45
54
|
suggestedAction: string;
|
|
46
55
|
};
|
|
47
|
-
export
|
|
56
|
+
export declare const PROJECT_TYPES: readonly ["auto", "typescript", "nextjs", "unity", "dotnet", "python", "go", "rust", "unknown"];
|
|
57
|
+
export type ProjectType = (typeof PROJECT_TYPES)[number];
|
|
48
58
|
export type ScanTrend = {
|
|
49
59
|
previousScanDate?: string;
|
|
50
60
|
scoreDelta: number;
|
|
@@ -83,6 +93,8 @@ export type ScanMetrics = {
|
|
|
83
93
|
path: string;
|
|
84
94
|
estimatedTokens: number;
|
|
85
95
|
}>;
|
|
96
|
+
onDemandFileCount?: number;
|
|
97
|
+
estimatedOnDemandTokens?: number;
|
|
86
98
|
};
|
|
87
99
|
/**
|
|
88
100
|
* R1: report.json intentionally omits InstructionFile's `content` and
|
|
@@ -124,5 +136,18 @@ export type ScanInput = {
|
|
|
124
136
|
baseline?: Baseline;
|
|
125
137
|
contextBudget?: number;
|
|
126
138
|
fileContextBudget?: number;
|
|
139
|
+
/**
|
|
140
|
+
* FEAT-012: a target-model preset name (e.g. `claude-opus-5`) that scales the
|
|
141
|
+
* context-bloat thresholds to that model's context window. An explicit
|
|
142
|
+
* `contextBudget`/`fileContextBudget` always overrides the preset. Surfaced
|
|
143
|
+
* as the `targetModel` config key and `--target-model` flag.
|
|
144
|
+
*/
|
|
145
|
+
targetModel?: string;
|
|
146
|
+
/**
|
|
147
|
+
* FEAT-004: force every vague-guidance finding to this severity, overriding
|
|
148
|
+
* the detector's high-signal/soft tiering. Surfaced as the
|
|
149
|
+
* `vagueGuidanceSeverity` config key.
|
|
150
|
+
*/
|
|
151
|
+
vagueGuidanceSeverity?: 'info' | 'warning';
|
|
127
152
|
};
|
|
128
153
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mCAAmC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
1
|
/** Core data types for PromptCI */
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* On-demand file types are loaded by an agent only when a skill/subagent is
|
|
4
|
+
* invoked, not on every turn. They do not count toward always-loaded context
|
|
5
|
+
* cost and are owned by the ai_config detectors, so the generic prose detectors
|
|
6
|
+
* skip them.
|
|
7
|
+
*/
|
|
8
|
+
const ON_DEMAND_FILE_TYPES = new Set(['skill', 'agent']);
|
|
9
|
+
export function isOnDemandFileType(fileType) {
|
|
10
|
+
return ON_DEMAND_FILE_TYPES.has(fileType);
|
|
11
|
+
}
|
|
12
|
+
// Single source of truth: consumers that need to *validate* a project type at
|
|
13
|
+
// runtime (the CLI's config loader, `promptci init`) import this array rather
|
|
14
|
+
// than keeping their own copy, which is how `python`/`go`/`rust` previously
|
|
15
|
+
// ended up writable by `init` but rejected by `loadConfig`.
|
|
16
|
+
export const PROJECT_TYPES = [
|
|
17
|
+
'auto',
|
|
18
|
+
'typescript',
|
|
19
|
+
'nextjs',
|
|
20
|
+
'unity',
|
|
21
|
+
'dotnet',
|
|
22
|
+
'python',
|
|
23
|
+
'go',
|
|
24
|
+
'rust',
|
|
25
|
+
'unknown',
|
|
26
|
+
];
|
|
3
27
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mCAAmC"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAmBnC;;;;;GAKG;AACH,MAAM,oBAAoB,GAA0B,IAAI,GAAG,CAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAE1F,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,OAAO,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAqED,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,4DAA4D;AAC5D,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,MAAM;IACN,YAAY;IACZ,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,IAAI;IACJ,MAAM;IACN,SAAS;CACD,CAAC"}
|
package/dist/vague-guidance.d.ts
CHANGED
|
@@ -5,8 +5,21 @@
|
|
|
5
5
|
* Emits at most one issue per section regardless of how many vague phrases
|
|
6
6
|
* appear in it.
|
|
7
7
|
*
|
|
8
|
-
* Heuristic detector
|
|
8
|
+
* Heuristic detector. Findings are tiered (FEAT-004): high-signal platitudes
|
|
9
|
+
* ("write clean code", "best practices", "SOLID principles", …) are
|
|
10
|
+
* warning 0.80; soft style/UX/paradigm phrases are info 0.75. A team can force
|
|
11
|
+
* one severity for every finding via the `severityOverride` option (wired to
|
|
12
|
+
* the `vagueGuidanceSeverity` config key).
|
|
9
13
|
*/
|
|
10
14
|
import type { InstructionFile, PromptCiIssue } from './types.js';
|
|
11
|
-
export
|
|
15
|
+
export type VagueGuidanceOptions = {
|
|
16
|
+
/**
|
|
17
|
+
* FEAT-004: force every vague-guidance finding to this severity, overriding
|
|
18
|
+
* the per-section high-signal/soft tiering. Wired to the `vagueGuidanceSeverity`
|
|
19
|
+
* config key so teams can enforce a stricter (all-warning) or looser
|
|
20
|
+
* (all-info) posture. Confidence still reflects the matched tier.
|
|
21
|
+
*/
|
|
22
|
+
severityOverride?: 'info' | 'warning';
|
|
23
|
+
};
|
|
24
|
+
export declare function detectVagueGuidance(files: InstructionFile[], options?: VagueGuidanceOptions): PromptCiIssue[];
|
|
12
25
|
//# sourceMappingURL=vague-guidance.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vague-guidance.d.ts","sourceRoot":"","sources":["../src/vague-guidance.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vague-guidance.d.ts","sourceRoot":"","sources":["../src/vague-guidance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAqC,aAAa,EAAE,MAAM,YAAY,CAAC;AAqLpG,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC,CAAC;AAEF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,EAAE,EACxB,OAAO,GAAE,oBAAyB,GACjC,aAAa,EAAE,CA8DjB"}
|
package/dist/vague-guidance.js
CHANGED
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
* Emits at most one issue per section regardless of how many vague phrases
|
|
6
6
|
* appear in it.
|
|
7
7
|
*
|
|
8
|
-
* Heuristic detector
|
|
8
|
+
* Heuristic detector. Findings are tiered (FEAT-004): high-signal platitudes
|
|
9
|
+
* ("write clean code", "best practices", "SOLID principles", …) are
|
|
10
|
+
* warning 0.80; soft style/UX/paradigm phrases are info 0.75. A team can force
|
|
11
|
+
* one severity for every finding via the `severityOverride` option (wired to
|
|
12
|
+
* the `vagueGuidanceSeverity` config key).
|
|
9
13
|
*/
|
|
10
14
|
import * as crypto from 'node:crypto';
|
|
15
|
+
import { stripCodeBlocks } from './markdown-fences.js';
|
|
11
16
|
const VAGUE_PHRASES = [
|
|
12
17
|
'write clean code',
|
|
13
18
|
'write clean', // catches "write clean Go code", "write clean Python code", etc.
|
|
@@ -105,6 +110,31 @@ function expandHyphenVariants(phrases) {
|
|
|
105
110
|
return [...expanded];
|
|
106
111
|
}
|
|
107
112
|
const ALL_VAGUE_PHRASES = expandHyphenVariants(VAGUE_PHRASES);
|
|
113
|
+
/**
|
|
114
|
+
* FEAT-004: High-signal platitudes. These are the phrases that most reliably
|
|
115
|
+
* mark instruction rot — broad quality/effort exhortations that give an agent
|
|
116
|
+
* no constraint at all. A section containing any of them is emitted at
|
|
117
|
+
* `warning` (0.80); sections whose only matches are softer style/UX/paradigm
|
|
118
|
+
* phrases stay `info` (0.75). Listed in the post-hyphen-expansion form so the
|
|
119
|
+
* no-hyphen twins ("production ready" ↔ "production-ready") both qualify.
|
|
120
|
+
*/
|
|
121
|
+
const HIGH_SIGNAL_PHRASES = new Set([
|
|
122
|
+
'write clean code',
|
|
123
|
+
'write clean',
|
|
124
|
+
'use best practices',
|
|
125
|
+
'best practices',
|
|
126
|
+
'follow best practices',
|
|
127
|
+
'make it production ready',
|
|
128
|
+
'production ready',
|
|
129
|
+
'production-ready',
|
|
130
|
+
'production quality',
|
|
131
|
+
'production-quality',
|
|
132
|
+
'be careful',
|
|
133
|
+
'solid principles',
|
|
134
|
+
'follow solid',
|
|
135
|
+
'clean architecture',
|
|
136
|
+
'make it maintainable',
|
|
137
|
+
]);
|
|
108
138
|
function escapeRegExp(text) {
|
|
109
139
|
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
110
140
|
}
|
|
@@ -115,41 +145,6 @@ function escapeRegExp(text) {
|
|
|
115
145
|
* reused across every section/file.
|
|
116
146
|
*/
|
|
117
147
|
const PHRASE_PATTERNS = new Map(ALL_VAGUE_PHRASES.map((phrase) => [phrase, new RegExp(`\\b${escapeRegExp(phrase)}\\b`)]));
|
|
118
|
-
/**
|
|
119
|
-
* V4: Strip fenced code blocks (``` and ~~~, matching either fence
|
|
120
|
-
* character/length per CommonMark — see scanner.ts) and inline code spans
|
|
121
|
-
* before matching. A fenced example that quotes a vague phrase is
|
|
122
|
-
* documentation about the phrase, not real guidance directed at the agent.
|
|
123
|
-
*/
|
|
124
|
-
function stripCodeBlocks(text) {
|
|
125
|
-
const lines = text.split('\n');
|
|
126
|
-
const kept = [];
|
|
127
|
-
let inBlock = false;
|
|
128
|
-
let fenceChar = null;
|
|
129
|
-
let fenceLen = 0;
|
|
130
|
-
for (const line of lines) {
|
|
131
|
-
if (!inBlock) {
|
|
132
|
-
const openMatch = /^ {0,3}([`~]{3,})/.exec(line);
|
|
133
|
-
if (openMatch) {
|
|
134
|
-
inBlock = true;
|
|
135
|
-
fenceChar = openMatch[1][0] ?? null;
|
|
136
|
-
fenceLen = openMatch[1].length;
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
kept.push(line);
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
const closeMatch = /^ {0,3}([`~]{3,})\s*$/.exec(line);
|
|
143
|
-
if (closeMatch && closeMatch[1][0] === fenceChar && closeMatch[1].length >= fenceLen) {
|
|
144
|
-
inBlock = false;
|
|
145
|
-
fenceChar = null;
|
|
146
|
-
fenceLen = 0;
|
|
147
|
-
}
|
|
148
|
-
// else: still inside the fence — drop the line
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return kept.join('\n').replace(/`[^`\n]+`/g, '');
|
|
152
|
-
}
|
|
153
148
|
/**
|
|
154
149
|
* V1: Find lines (or pairs of consecutive wrapped lines) containing `phraseRe`
|
|
155
150
|
* within the per-line length budget. Hard-wrapped guidance like:
|
|
@@ -181,7 +176,7 @@ function issueId(section) {
|
|
|
181
176
|
const hash = crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);
|
|
182
177
|
return `vague-${hash}`;
|
|
183
178
|
}
|
|
184
|
-
export function detectVagueGuidance(files) {
|
|
179
|
+
export function detectVagueGuidance(files, options = {}) {
|
|
185
180
|
const issues = [];
|
|
186
181
|
// One issue per section — collect all matching phrases and lines first
|
|
187
182
|
const seen = new Set();
|
|
@@ -190,7 +185,7 @@ export function detectVagueGuidance(files) {
|
|
|
190
185
|
const sectionKey = `${file.path}:${section.startLine}`;
|
|
191
186
|
if (seen.has(sectionKey))
|
|
192
187
|
continue;
|
|
193
|
-
const scanText = stripCodeBlocks(section.text);
|
|
188
|
+
const scanText = stripCodeBlocks(section.text, { stripInlineCode: true });
|
|
194
189
|
// V1: collapse newlines to spaces for this fast pre-filter only, so a
|
|
195
190
|
// phrase hard-wrapped across two lines still passes the gate. The actual
|
|
196
191
|
// evidence extraction below (findPhraseMatches) independently re-checks
|
|
@@ -214,9 +209,16 @@ export function detectVagueGuidance(files) {
|
|
|
214
209
|
continue;
|
|
215
210
|
seen.add(sectionKey);
|
|
216
211
|
const phraseList = matchedPhrases.map((p) => `"${p}"`).join(', ');
|
|
212
|
+
// FEAT-004: tier by the strongest phrase in the section. Any high-signal
|
|
213
|
+
// platitude escalates the whole finding to warning; a config override,
|
|
214
|
+
// when set, wins over the derived tier (but not the tier-based confidence).
|
|
215
|
+
const isHighSignal = matchedPhrases.some((p) => HIGH_SIGNAL_PHRASES.has(p));
|
|
216
|
+
const tierSeverity = isHighSignal ? 'warning' : 'info';
|
|
217
|
+
const severity = options.severityOverride ?? tierSeverity;
|
|
218
|
+
const confidence = isHighSignal ? 0.8 : 0.75;
|
|
217
219
|
issues.push({
|
|
218
220
|
id: issueId(section),
|
|
219
|
-
severity
|
|
221
|
+
severity,
|
|
220
222
|
category: 'vague_guidance',
|
|
221
223
|
title: 'Vague guidance detected',
|
|
222
224
|
summary: `This section contains guidance that may be too vague to be actionable: ${phraseList}.`,
|
|
@@ -226,7 +228,7 @@ export function detectVagueGuidance(files) {
|
|
|
226
228
|
recommendation: 'Replace vague guidance with concrete constraints — specific commands, forbidden ' +
|
|
227
229
|
'patterns, or measurable criteria. For example, replace "write clean code" with ' +
|
|
228
230
|
'"functions must be ≤ 50 lines; no unused variables".',
|
|
229
|
-
confidence
|
|
231
|
+
confidence,
|
|
230
232
|
});
|
|
231
233
|
}
|
|
232
234
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vague-guidance.js","sourceRoot":"","sources":["../src/vague-guidance.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"vague-guidance.js","sourceRoot":"","sources":["../src/vague-guidance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,aAAa,GAAG;IACpB,kBAAkB;IAClB,aAAa,EAAU,iEAAiE;IACxF,oBAAoB;IACpB,0BAA0B;IAC1B,kBAAkB,EAAQ,gDAAgD;IAC1E,kBAAkB;IAClB,YAAY;IACZ,oBAAoB;IACpB,sBAAsB;IACtB,kBAAkB;IAClB,gBAAgB;IAChB,oBAAoB;IACpB,gBAAgB,EAAY,oDAAoD;IAChF,oBAAoB,EAAQ,kCAAkC;IAC9D,oBAAoB,EAAQ,yBAAyB;IACrD,uBAAuB,EAAK,gBAAgB;IAC5C,gBAAgB,EAAY,yBAAyB;IACrD,4BAA4B;IAC5B,gBAAgB;IAChB,oBAAoB;IACpB,oBAAoB;IACpB,oBAAoB;IACpB,+EAA+E;IAC/E,kBAAkB,EAAU,uCAAuC;IACnE,cAAc,EAAc,mBAAmB;IAC/C,kBAAkB,EAAU,kCAAkC;IAC9D,sBAAsB,EAAM,yBAAyB;IACrD,oBAAoB,EAAQ,6BAA6B;IACzD,kBAAkB,EAAU,+BAA+B;IAC3D,+FAA+F;IAC/F,oBAAoB,EAAQ,2DAA2D;IACvF,mBAAmB,EAAS,0DAA0D;IACtF,eAAe,EAAa,wDAAwD;IACpF,mBAAmB,EAAS,qDAAqD;IACjF,mBAAmB,EAAS,yBAAyB;IACrD,2BAA2B,EAAE,gDAAgD;IAC7E,oFAAoF;IACpF,qFAAqF;IACrF,+EAA+E;IAC/E,mCAAmC,EAAG,6CAA6C;IACnF,iBAAiB,EAAqB,4CAA4C;IAClF,gFAAgF;IAChF,2BAA2B,EAAK,uCAAuC;IACvE,gBAAgB;IAChB,wBAAwB,EAAQ,2BAA2B;IAC3D,mBAAmB;IACnB,cAAc;IACd,aAAa;IACb,aAAa;IACb,uBAAuB,EAAS,8DAA8D;IAC9F,uBAAuB,EAAS,0BAA0B;IAC1D,uBAAuB;IACvB,sBAAsB;IACtB,qBAAqB;IACrB,qBAAqB;IACrB,eAAe;IACf,aAAa;IACb,YAAY;IACZ,sDAAsD;IACtD,YAAY,EAAgB,4BAA4B;IACxD,oBAAoB,EAAQ,+BAA+B;IAC3D,aAAa,EAAe,0BAA0B;IACtD,gFAAgF;IAChF,0BAA0B,EAAE,gDAAgD;IAC5E,4BAA4B;IAC5B,8BAA8B;IAC9B,cAAc,EAAc,4BAA4B;IACxD,gBAAgB;IAChB,kBAAkB;IAClB,kBAAkB;IAClB,sBAAsB;CACvB,CAAC;AAEF;;;;GAIG;AACH,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAEpC;;;;;;GAMG;AACH,SAAS,oBAAoB,CAAC,OAAiB;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrB,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAS;IAC1C,kBAAkB;IAClB,aAAa;IACb,oBAAoB;IACpB,gBAAgB;IAChB,uBAAuB;IACvB,0BAA0B;IAC1B,kBAAkB;IAClB,kBAAkB;IAClB,oBAAoB;IACpB,oBAAoB;IACpB,YAAY;IACZ,kBAAkB;IAClB,cAAc;IACd,oBAAoB;IACpB,sBAAsB;CACvB,CAAC,CAAC;AAEH,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,iBAAiB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CACzF,CAAC;AAGF;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,QAAgB,EAAE,QAAgB;IAC3D,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,CAAE,CAAC;QAChC,IAAI,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,uBAAuB,EAAE,CAAC;YAC9F,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACjC,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,EAAE,CAAC;YACnC,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,uBAAuB,EAAE,CAAC;gBACpF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,OAAO,CAAC,OAA2B;IAC1C,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,SAAS,QAAQ,CAAC;IAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9E,OAAO,SAAS,IAAI,EAAE,CAAC;AACzB,CAAC;AAYD,MAAM,UAAU,mBAAmB,CACjC,KAAwB,EACxB,UAAgC,EAAE;IAElC,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,uEAAuE;IACvE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACvD,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,SAAS;YAEnC,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1E,sEAAsE;YACtE,yEAAyE;YACzE,wEAAwE;YACxE,0EAA0E;YAC1E,6CAA6C;YAC7C,MAAM,kBAAkB,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YACtE,MAAM,cAAc,GAAa,EAAE,CAAC;YACpC,MAAM,YAAY,GAAa,EAAE,CAAC;YAElC,KAAK,MAAM,MAAM,IAAI,iBAAiB,EAAE,CAAC;gBACvC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;gBAC9C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC;oBAAE,SAAS;gBAEjD,KAAK,MAAM,IAAI,IAAI,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;oBACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC;wBAAE,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAClE,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC;wBAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;YAED,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAC1C,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAErB,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAElE,yEAAyE;YACzE,uEAAuE;YACvE,4EAA4E;YAC5E,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5E,MAAM,YAAY,GAAkB,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;YACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,gBAAgB,IAAI,YAAY,CAAC;YAC1D,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAE7C,MAAM,CAAC,IAAI,CAAC;gBACV,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC;gBACpB,QAAQ;gBACR,QAAQ,EAAE,gBAAgB;gBAC1B,KAAK,EAAE,yBAAyB;gBAChC,OAAO,EAAE,0EAA0E,UAAU,GAAG;gBAChG,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;gBACtB,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC5F,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;gBAC7D,cAAc,EACZ,kFAAkF;oBAClF,iFAAiF;oBACjF,sDAAsD;gBACxD,UAAU;aACX,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windsurf Rules Detector (issue #67)
|
|
3
|
+
*
|
|
4
|
+
* `.windsurfrules` is Windsurf's legacy, always-on, workspace-wide rules file:
|
|
5
|
+
* every rule in it is fed to the model on every request for the whole
|
|
6
|
+
* workspace. Windsurf also supports scoped `.windsurf/rules/*.md` files with
|
|
7
|
+
* glob / manual / model-decision activation.
|
|
8
|
+
*
|
|
9
|
+
* This detector flags one thing the generic prose detectors miss: a workspace
|
|
10
|
+
* `.windsurfrules` that clearly carries language- or framework-specific rules,
|
|
11
|
+
* which are cheaper as glob-scoped rules that load only for matching files. It
|
|
12
|
+
* emits a single cautious `info` suggesting the author clarify intended scope.
|
|
13
|
+
*
|
|
14
|
+
* The Windsurf-specific size/truncation check for `.windsurfrules` lives in
|
|
15
|
+
* `context-bloat.ts`, alongside the other file-type-specific size thresholds.
|
|
16
|
+
*
|
|
17
|
+
* Deterministic and offline.
|
|
18
|
+
*/
|
|
19
|
+
import type { RepoContext } from './repo-context.js';
|
|
20
|
+
import type { PromptCiIssue } from './types.js';
|
|
21
|
+
export declare function detectWindsurfRules(context: RepoContext): PromptCiIssue[];
|
|
22
|
+
//# sourceMappingURL=windsurf-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windsurf-detector.d.ts","sourceRoot":"","sources":["../src/windsurf-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAkChD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,EAAE,CAwCzE"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Windsurf Rules Detector (issue #67)
|
|
3
|
+
*
|
|
4
|
+
* `.windsurfrules` is Windsurf's legacy, always-on, workspace-wide rules file:
|
|
5
|
+
* every rule in it is fed to the model on every request for the whole
|
|
6
|
+
* workspace. Windsurf also supports scoped `.windsurf/rules/*.md` files with
|
|
7
|
+
* glob / manual / model-decision activation.
|
|
8
|
+
*
|
|
9
|
+
* This detector flags one thing the generic prose detectors miss: a workspace
|
|
10
|
+
* `.windsurfrules` that clearly carries language- or framework-specific rules,
|
|
11
|
+
* which are cheaper as glob-scoped rules that load only for matching files. It
|
|
12
|
+
* emits a single cautious `info` suggesting the author clarify intended scope.
|
|
13
|
+
*
|
|
14
|
+
* The Windsurf-specific size/truncation check for `.windsurfrules` lives in
|
|
15
|
+
* `context-bloat.ts`, alongside the other file-type-specific size thresholds.
|
|
16
|
+
*
|
|
17
|
+
* Deterministic and offline.
|
|
18
|
+
*/
|
|
19
|
+
import { shortHash } from './ai-config.js';
|
|
20
|
+
/**
|
|
21
|
+
* Language / framework signals that, when they appear as SECTION HEADINGS in an
|
|
22
|
+
* always-on workspace file, indicate rules scoped to only part of the codebase.
|
|
23
|
+
* Heading-only matching keeps this precise: a passing mention of "python" in
|
|
24
|
+
* prose does not count, but a `## Python` section does. Terms are chosen to be
|
|
25
|
+
* unambiguous in a heading (e.g. `golang`, not bare `go`).
|
|
26
|
+
*/
|
|
27
|
+
const SCOPED_TOPICS = [
|
|
28
|
+
{ label: 'TypeScript', re: /\btypescript\b/i },
|
|
29
|
+
{ label: 'JavaScript', re: /\bjavascript\b/i },
|
|
30
|
+
{ label: 'Python', re: /\bpython\b/i },
|
|
31
|
+
{ label: 'Rust', re: /\brust\b/i },
|
|
32
|
+
{ label: 'Go', re: /\bgolang\b/i },
|
|
33
|
+
{ label: 'Ruby', re: /\bruby\b|\brails\b/i },
|
|
34
|
+
{ label: 'Java', re: /\bjava\b(?!script)/i },
|
|
35
|
+
{ label: 'Kotlin', re: /\bkotlin\b/i },
|
|
36
|
+
{ label: 'Swift', re: /\bswift\b/i },
|
|
37
|
+
{ label: 'C#', re: /\bc#|\bcsharp\b|\bdotnet\b|\.net\b/i },
|
|
38
|
+
{ label: 'React', re: /\breact\b/i },
|
|
39
|
+
{ label: 'Vue', re: /\bvue\b/i },
|
|
40
|
+
{ label: 'Svelte', re: /\bsvelte\b/i },
|
|
41
|
+
{ label: 'Django', re: /\bdjango\b/i },
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Two or more distinct scoped topics in headings is a strong, low-noise signal
|
|
45
|
+
* that an always-on file mixes rule sets that would be better glob-scoped. One
|
|
46
|
+
* topic is left alone — a single `## Python` section in a Python repo is normal.
|
|
47
|
+
*/
|
|
48
|
+
const MIN_DISTINCT_TOPICS = 2;
|
|
49
|
+
export function detectWindsurfRules(context) {
|
|
50
|
+
const issues = [];
|
|
51
|
+
for (const file of context.files) {
|
|
52
|
+
if (file.fileType !== 'windsurf')
|
|
53
|
+
continue;
|
|
54
|
+
const topics = new Set();
|
|
55
|
+
for (const section of file.sections) {
|
|
56
|
+
const heading = section.heading;
|
|
57
|
+
if (!heading)
|
|
58
|
+
continue;
|
|
59
|
+
for (const topic of SCOPED_TOPICS) {
|
|
60
|
+
if (topic.re.test(heading))
|
|
61
|
+
topics.add(topic.label);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (topics.size >= MIN_DISTINCT_TOPICS) {
|
|
65
|
+
const found = [...topics].sort();
|
|
66
|
+
issues.push({
|
|
67
|
+
id: `ai-config-windsurf-scope-${shortHash(file.path)}`,
|
|
68
|
+
severity: 'info',
|
|
69
|
+
category: 'ai_config',
|
|
70
|
+
title: '.windsurfrules mixes language-specific rules in a workspace-wide file',
|
|
71
|
+
summary: `This \`.windsurfrules\` has headings for multiple languages/frameworks ` +
|
|
72
|
+
`(${found.join(', ')}), but it applies to the whole workspace on every ` +
|
|
73
|
+
`request. Language- or framework-specific rules are cheaper as glob-scoped ` +
|
|
74
|
+
`rules that load only for matching files.`,
|
|
75
|
+
filePaths: [file.path],
|
|
76
|
+
locations: [{ filePath: file.path, startLine: 1 }],
|
|
77
|
+
evidence: [`Scoped topics found in headings: ${found.join(', ')}`],
|
|
78
|
+
recommendation: 'Clarify the intended scope: keep genuinely workspace-wide conventions in ' +
|
|
79
|
+
'`.windsurfrules`, and move language- or framework-specific rules into ' +
|
|
80
|
+
'glob-scoped `.windsurf/rules/*.md` files, each with a glob trigger.',
|
|
81
|
+
confidence: 0.6,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return issues;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=windsurf-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windsurf-detector.js","sourceRoot":"","sources":["../src/windsurf-detector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Within-section semantic dedup detector (FEAT-013).
|
|
3
|
+
*
|
|
4
|
+
* The cross-file `detectDuplicates` detector works at section granularity and
|
|
5
|
+
* misses a single section that restates the same point several ways. This
|
|
6
|
+
* detector looks *inside* a section: for sections with more than five
|
|
7
|
+
* paragraphs, it computes pairwise Jaccard similarity on the paragraphs and
|
|
8
|
+
* flags any pair that is ≥ 0.65 similar, naming the paragraph numbers.
|
|
9
|
+
*
|
|
10
|
+
* Info severity only — near-duplicate prose within one section is worth a
|
|
11
|
+
* gentle nudge, not a hard failure. Deterministic: no clock, network, or
|
|
12
|
+
* randomness.
|
|
13
|
+
*/
|
|
14
|
+
import type { InstructionFile, PromptCiIssue } from './types.js';
|
|
15
|
+
export declare function detectWithinSectionDedup(files: InstructionFile[]): PromptCiIssue[];
|
|
16
|
+
//# sourceMappingURL=within-section-dedup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"within-section-dedup.d.ts","sourceRoot":"","sources":["../src/within-section-dedup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAsB,aAAa,EAAE,MAAM,YAAY,CAAC;AAkGrF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CA2DlF"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Within-section semantic dedup detector (FEAT-013).
|
|
3
|
+
*
|
|
4
|
+
* The cross-file `detectDuplicates` detector works at section granularity and
|
|
5
|
+
* misses a single section that restates the same point several ways. This
|
|
6
|
+
* detector looks *inside* a section: for sections with more than five
|
|
7
|
+
* paragraphs, it computes pairwise Jaccard similarity on the paragraphs and
|
|
8
|
+
* flags any pair that is ≥ 0.65 similar, naming the paragraph numbers.
|
|
9
|
+
*
|
|
10
|
+
* Info severity only — near-duplicate prose within one section is worth a
|
|
11
|
+
* gentle nudge, not a hard failure. Deterministic: no clock, network, or
|
|
12
|
+
* randomness.
|
|
13
|
+
*/
|
|
14
|
+
import * as crypto from 'node:crypto';
|
|
15
|
+
import { snippet } from './evidence.js';
|
|
16
|
+
/** Persistent, always-loaded instruction files this detector applies to. */
|
|
17
|
+
const TARGET_FILE_TYPES = new Set([
|
|
18
|
+
'claude',
|
|
19
|
+
'agents',
|
|
20
|
+
'cursor',
|
|
21
|
+
'windsurf',
|
|
22
|
+
'copilot',
|
|
23
|
+
'prompt',
|
|
24
|
+
]);
|
|
25
|
+
const MIN_PARAGRAPHS = 6; // "> 5 paragraphs"
|
|
26
|
+
const PARAGRAPH_JACCARD_THRESHOLD = 0.65;
|
|
27
|
+
const MIN_PARAGRAPH_CHARS = 40; // normalised; shorter paragraphs are too noisy
|
|
28
|
+
const MIN_PARAGRAPH_TOKENS = 8;
|
|
29
|
+
/** Normalise paragraph text for comparison: lowercase, strip markdown, collapse ws. */
|
|
30
|
+
function normalize(text) {
|
|
31
|
+
return text
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/^#{1,6}\s+/gm, '')
|
|
34
|
+
.replace(/^[\s]*[-*+]\s+/gm, '')
|
|
35
|
+
.replace(/^[\s]*\d+[.)]\s+/gm, '')
|
|
36
|
+
.replace(/\s+/g, ' ')
|
|
37
|
+
.trim();
|
|
38
|
+
}
|
|
39
|
+
function tokenize(text) {
|
|
40
|
+
return new Set(text.match(/\b\w+\b/g) ?? []);
|
|
41
|
+
}
|
|
42
|
+
function jaccard(a, b) {
|
|
43
|
+
if (a.size === 0 || b.size === 0)
|
|
44
|
+
return 0;
|
|
45
|
+
let intersection = 0;
|
|
46
|
+
for (const t of a)
|
|
47
|
+
if (b.has(t))
|
|
48
|
+
intersection++;
|
|
49
|
+
return intersection / (a.size + b.size - intersection);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Split a section into paragraphs (blank-line separated), tracking each
|
|
53
|
+
* paragraph's 1-based number and its start line within the file. Code-fence
|
|
54
|
+
* paragraphs are skipped entirely — a repeated code block is the concern of the
|
|
55
|
+
* duplicate-section detector, not prose dedup.
|
|
56
|
+
*/
|
|
57
|
+
function splitParagraphs(section) {
|
|
58
|
+
const lines = section.text.split('\n');
|
|
59
|
+
const paragraphs = [];
|
|
60
|
+
let current = [];
|
|
61
|
+
let currentStart = section.startLine;
|
|
62
|
+
let paragraphNumber = 0;
|
|
63
|
+
const flush = () => {
|
|
64
|
+
if (current.length === 0)
|
|
65
|
+
return;
|
|
66
|
+
paragraphNumber++;
|
|
67
|
+
const text = current.join('\n');
|
|
68
|
+
if (!text.includes('```') && !text.includes('~~~')) {
|
|
69
|
+
const norm = normalize(text);
|
|
70
|
+
const tokens = tokenize(norm);
|
|
71
|
+
if (norm.length >= MIN_PARAGRAPH_CHARS && tokens.size >= MIN_PARAGRAPH_TOKENS) {
|
|
72
|
+
paragraphs.push({ number: paragraphNumber, text, startLine: currentStart, tokens });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
current = [];
|
|
76
|
+
};
|
|
77
|
+
for (let i = 0; i < lines.length; i++) {
|
|
78
|
+
const line = lines[i] ?? '';
|
|
79
|
+
if (line.trim() === '') {
|
|
80
|
+
flush();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
if (current.length === 0)
|
|
84
|
+
currentStart = section.startLine + i;
|
|
85
|
+
current.push(line);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
flush();
|
|
89
|
+
return paragraphs;
|
|
90
|
+
}
|
|
91
|
+
function issueId(filePath, startLine) {
|
|
92
|
+
const hash = crypto
|
|
93
|
+
.createHash('sha1')
|
|
94
|
+
.update(`within-section-dup:${filePath}:${startLine}`)
|
|
95
|
+
.digest('hex')
|
|
96
|
+
.slice(0, 12);
|
|
97
|
+
return `within-section-dup-${hash}`;
|
|
98
|
+
}
|
|
99
|
+
export function detectWithinSectionDedup(files) {
|
|
100
|
+
const issues = [];
|
|
101
|
+
for (const file of files) {
|
|
102
|
+
if (!TARGET_FILE_TYPES.has(file.fileType))
|
|
103
|
+
continue;
|
|
104
|
+
for (const section of file.sections) {
|
|
105
|
+
const paragraphs = splitParagraphs(section);
|
|
106
|
+
// "> 5 paragraphs" is measured against the qualifying (comparable)
|
|
107
|
+
// paragraphs — trivial one-liners never counted toward the section total.
|
|
108
|
+
if (paragraphs.length < MIN_PARAGRAPHS)
|
|
109
|
+
continue;
|
|
110
|
+
// Find the single strongest similar pair (and how many pairs cross the bar).
|
|
111
|
+
let best = null;
|
|
112
|
+
let pairCount = 0;
|
|
113
|
+
for (let i = 0; i < paragraphs.length; i++) {
|
|
114
|
+
for (let j = i + 1; j < paragraphs.length; j++) {
|
|
115
|
+
const sim = jaccard(paragraphs[i].tokens, paragraphs[j].tokens);
|
|
116
|
+
if (sim >= PARAGRAPH_JACCARD_THRESHOLD) {
|
|
117
|
+
pairCount++;
|
|
118
|
+
if (!best || sim > best.sim)
|
|
119
|
+
best = { a: paragraphs[i], b: paragraphs[j], sim };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (!best)
|
|
124
|
+
continue;
|
|
125
|
+
const heading = section.heading ?? '(untitled section)';
|
|
126
|
+
const pct = Math.round(best.sim * 100);
|
|
127
|
+
const morePairs = pairCount > 1 ? ` (${pairCount} similar pairs in total)` : '';
|
|
128
|
+
issues.push({
|
|
129
|
+
id: issueId(file.path, section.startLine),
|
|
130
|
+
severity: 'info',
|
|
131
|
+
category: 'duplicate',
|
|
132
|
+
title: `Possible repetition within section: ${heading}`,
|
|
133
|
+
summary: `Within "${heading}", paragraphs ${best.a.number} and ${best.b.number} are about ` +
|
|
134
|
+
`${pct}% similar${morePairs}. The section may be restating the same point several ` +
|
|
135
|
+
`ways — consider consolidating.`,
|
|
136
|
+
filePaths: [file.path],
|
|
137
|
+
locations: [
|
|
138
|
+
{ filePath: file.path, startLine: best.a.startLine, endLine: best.a.startLine },
|
|
139
|
+
{ filePath: file.path, startLine: best.b.startLine, endLine: best.b.startLine },
|
|
140
|
+
],
|
|
141
|
+
evidence: [
|
|
142
|
+
`Paragraph ${best.a.number}: "${snippet(best.a.text, 120)}"`,
|
|
143
|
+
`Paragraph ${best.b.number}: "${snippet(best.b.text, 120)}"`,
|
|
144
|
+
],
|
|
145
|
+
recommendation: 'Merge the near-duplicate paragraphs into one clear statement. If both are ' +
|
|
146
|
+
'intentional (e.g. a summary and a detailed version), make the distinction explicit.',
|
|
147
|
+
confidence: 0.5,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return issues;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=within-section-dedup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"within-section-dedup.js","sourceRoot":"","sources":["../src/within-section-dedup.ts"],"names":[],"mappings":"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"}
|