@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.
Files changed (205) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +29 -30
  3. package/dist/agent-practices.d.ts +2 -0
  4. package/dist/agent-practices.d.ts.map +1 -1
  5. package/dist/agent-practices.js +91 -51
  6. package/dist/agent-practices.js.map +1 -1
  7. package/dist/ai-config.d.ts +139 -0
  8. package/dist/ai-config.d.ts.map +1 -0
  9. package/dist/ai-config.js +523 -0
  10. package/dist/ai-config.js.map +1 -0
  11. package/dist/baseline.d.ts +17 -1
  12. package/dist/baseline.d.ts.map +1 -1
  13. package/dist/baseline.js +5 -1
  14. package/dist/baseline.js.map +1 -1
  15. package/dist/buried-critical.d.ts +17 -0
  16. package/dist/buried-critical.d.ts.map +1 -0
  17. package/dist/buried-critical.js +112 -0
  18. package/dist/buried-critical.js.map +1 -0
  19. package/dist/canonical-owner.d.ts.map +1 -1
  20. package/dist/canonical-owner.js +18 -8
  21. package/dist/canonical-owner.js.map +1 -1
  22. package/dist/command-validity.d.ts.map +1 -1
  23. package/dist/command-validity.js +53 -74
  24. package/dist/command-validity.js.map +1 -1
  25. package/dist/conflicts.d.ts.map +1 -1
  26. package/dist/conflicts.js +1 -42
  27. package/dist/conflicts.js.map +1 -1
  28. package/dist/context-analysis.d.ts.map +1 -1
  29. package/dist/context-analysis.js +13 -2
  30. package/dist/context-analysis.js.map +1 -1
  31. package/dist/context-bloat.d.ts +19 -0
  32. package/dist/context-bloat.d.ts.map +1 -1
  33. package/dist/context-bloat.js +82 -9
  34. package/dist/context-bloat.js.map +1 -1
  35. package/dist/context-optimizer.d.ts.map +1 -1
  36. package/dist/context-optimizer.js +15 -54
  37. package/dist/context-optimizer.js.map +1 -1
  38. package/dist/copilot-instructions-detector.d.ts +24 -0
  39. package/dist/copilot-instructions-detector.d.ts.map +1 -0
  40. package/dist/copilot-instructions-detector.js +145 -0
  41. package/dist/copilot-instructions-detector.js.map +1 -0
  42. package/dist/cursor-rules-detector.d.ts +17 -0
  43. package/dist/cursor-rules-detector.d.ts.map +1 -0
  44. package/dist/cursor-rules-detector.js +110 -0
  45. package/dist/cursor-rules-detector.js.map +1 -0
  46. package/dist/custom-rules.d.ts +52 -0
  47. package/dist/custom-rules.d.ts.map +1 -0
  48. package/dist/custom-rules.js +298 -0
  49. package/dist/custom-rules.js.map +1 -0
  50. package/dist/dead-references.js +11 -9
  51. package/dist/dead-references.js.map +1 -1
  52. package/dist/detectors.d.ts +1 -3
  53. package/dist/detectors.d.ts.map +1 -1
  54. package/dist/detectors.js +46 -53
  55. package/dist/detectors.js.map +1 -1
  56. package/dist/duplicates.d.ts.map +1 -1
  57. package/dist/duplicates.js +10 -5
  58. package/dist/duplicates.js.map +1 -1
  59. package/dist/evidence.d.ts +28 -0
  60. package/dist/evidence.d.ts.map +1 -0
  61. package/dist/evidence.js +38 -0
  62. package/dist/evidence.js.map +1 -0
  63. package/dist/fix-engine.d.ts +11 -0
  64. package/dist/fix-engine.d.ts.map +1 -1
  65. package/dist/fix-engine.js +88 -71
  66. package/dist/fix-engine.js.map +1 -1
  67. package/dist/frameworks/python.d.ts.map +1 -1
  68. package/dist/frameworks/python.js +5 -2
  69. package/dist/frameworks/python.js.map +1 -1
  70. package/dist/health-score.d.ts.map +1 -1
  71. package/dist/health-score.js +1 -0
  72. package/dist/health-score.js.map +1 -1
  73. package/dist/hooks-settings-detector.d.ts +17 -0
  74. package/dist/hooks-settings-detector.d.ts.map +1 -0
  75. package/dist/hooks-settings-detector.js +228 -0
  76. package/dist/hooks-settings-detector.js.map +1 -0
  77. package/dist/index.d.ts +22 -7
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +18 -6
  80. package/dist/index.js.map +1 -1
  81. package/dist/manifest-consistency.d.ts +6 -0
  82. package/dist/manifest-consistency.d.ts.map +1 -1
  83. package/dist/manifest-consistency.js +26 -5
  84. package/dist/manifest-consistency.js.map +1 -1
  85. package/dist/markdown-fences.d.ts +112 -0
  86. package/dist/markdown-fences.d.ts.map +1 -0
  87. package/dist/markdown-fences.js +174 -0
  88. package/dist/markdown-fences.js.map +1 -0
  89. package/dist/mcp-config-detector.d.ts +20 -0
  90. package/dist/mcp-config-detector.d.ts.map +1 -0
  91. package/dist/mcp-config-detector.js +237 -0
  92. package/dist/mcp-config-detector.js.map +1 -0
  93. package/dist/missing-context.d.ts.map +1 -1
  94. package/dist/missing-context.js +2 -52
  95. package/dist/missing-context.js.map +1 -1
  96. package/dist/model-budgets.d.ts +69 -0
  97. package/dist/model-budgets.d.ts.map +1 -0
  98. package/dist/model-budgets.js +110 -0
  99. package/dist/model-budgets.js.map +1 -0
  100. package/dist/negative-instructions.d.ts +26 -0
  101. package/dist/negative-instructions.d.ts.map +1 -0
  102. package/dist/negative-instructions.js +174 -0
  103. package/dist/negative-instructions.js.map +1 -0
  104. package/dist/path-containment.d.ts +12 -0
  105. package/dist/path-containment.d.ts.map +1 -0
  106. package/dist/path-containment.js +53 -0
  107. package/dist/path-containment.js.map +1 -0
  108. package/dist/project-type.d.ts +12 -2
  109. package/dist/project-type.d.ts.map +1 -1
  110. package/dist/project-type.js +25 -12
  111. package/dist/project-type.js.map +1 -1
  112. package/dist/prompt-cache.d.ts.map +1 -1
  113. package/dist/prompt-cache.js +26 -19
  114. package/dist/prompt-cache.js.map +1 -1
  115. package/dist/promptci-gitignore.d.ts +34 -0
  116. package/dist/promptci-gitignore.d.ts.map +1 -0
  117. package/dist/promptci-gitignore.js +42 -0
  118. package/dist/promptci-gitignore.js.map +1 -0
  119. package/dist/repo-context.d.ts +27 -0
  120. package/dist/repo-context.d.ts.map +1 -1
  121. package/dist/repo-context.js +42 -11
  122. package/dist/repo-context.js.map +1 -1
  123. package/dist/report.d.ts +1 -0
  124. package/dist/report.d.ts.map +1 -1
  125. package/dist/report.js +132 -61
  126. package/dist/report.js.map +1 -1
  127. package/dist/scan.d.ts.map +1 -1
  128. package/dist/scan.js +7 -3
  129. package/dist/scan.js.map +1 -1
  130. package/dist/scanner.d.ts +13 -1
  131. package/dist/scanner.d.ts.map +1 -1
  132. package/dist/scanner.js +49 -37
  133. package/dist/scanner.js.map +1 -1
  134. package/dist/security-pack.d.ts +20 -0
  135. package/dist/security-pack.d.ts.map +1 -1
  136. package/dist/security-pack.js +27 -10
  137. package/dist/security-pack.js.map +1 -1
  138. package/dist/skills-detector.d.ts +19 -0
  139. package/dist/skills-detector.d.ts.map +1 -0
  140. package/dist/skills-detector.js +203 -0
  141. package/dist/skills-detector.js.map +1 -0
  142. package/dist/stale-instructions.d.ts +4 -1
  143. package/dist/stale-instructions.d.ts.map +1 -1
  144. package/dist/stale-instructions.js +51 -54
  145. package/dist/stale-instructions.js.map +1 -1
  146. package/dist/subagents-detector.d.ts +19 -0
  147. package/dist/subagents-detector.d.ts.map +1 -0
  148. package/dist/subagents-detector.js +155 -0
  149. package/dist/subagents-detector.js.map +1 -0
  150. package/dist/suppression.d.ts.map +1 -1
  151. package/dist/suppression.js +11 -45
  152. package/dist/suppression.js.map +1 -1
  153. package/dist/types.d.ts +28 -3
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +25 -1
  156. package/dist/types.js.map +1 -1
  157. package/dist/vague-guidance.d.ts +15 -2
  158. package/dist/vague-guidance.d.ts.map +1 -1
  159. package/dist/vague-guidance.js +42 -40
  160. package/dist/vague-guidance.js.map +1 -1
  161. package/dist/windsurf-detector.d.ts +22 -0
  162. package/dist/windsurf-detector.d.ts.map +1 -0
  163. package/dist/windsurf-detector.js +87 -0
  164. package/dist/windsurf-detector.js.map +1 -0
  165. package/dist/within-section-dedup.d.ts +16 -0
  166. package/dist/within-section-dedup.d.ts.map +1 -0
  167. package/dist/within-section-dedup.js +153 -0
  168. package/dist/within-section-dedup.js.map +1 -0
  169. package/package.json +11 -8
  170. package/dist/cheap-model-routing.d.ts +0 -4
  171. package/dist/cheap-model-routing.d.ts.map +0 -1
  172. package/dist/cheap-model-routing.js +0 -43
  173. package/dist/cheap-model-routing.js.map +0 -1
  174. package/dist/cli-output-filter.d.ts +0 -16
  175. package/dist/cli-output-filter.d.ts.map +0 -1
  176. package/dist/cli-output-filter.js +0 -88
  177. package/dist/cli-output-filter.js.map +0 -1
  178. package/dist/context-cost.d.ts +0 -4
  179. package/dist/context-cost.d.ts.map +0 -1
  180. package/dist/context-cost.js +0 -70
  181. package/dist/context-cost.js.map +0 -1
  182. package/dist/context-recommender.d.ts +0 -3
  183. package/dist/context-recommender.d.ts.map +0 -1
  184. package/dist/context-recommender.js +0 -329
  185. package/dist/context-recommender.js.map +0 -1
  186. package/dist/llm.d.ts +0 -6
  187. package/dist/llm.d.ts.map +0 -1
  188. package/dist/llm.js +0 -67
  189. package/dist/llm.js.map +0 -1
  190. package/dist/mcp-detector.d.ts +0 -4
  191. package/dist/mcp-detector.d.ts.map +0 -1
  192. package/dist/mcp-detector.js +0 -131
  193. package/dist/mcp-detector.js.map +0 -1
  194. package/dist/native-tool-preference.d.ts +0 -20
  195. package/dist/native-tool-preference.d.ts.map +0 -1
  196. package/dist/native-tool-preference.js +0 -94
  197. package/dist/native-tool-preference.js.map +0 -1
  198. package/dist/output-verbosity.d.ts +0 -3
  199. package/dist/output-verbosity.d.ts.map +0 -1
  200. package/dist/output-verbosity.js +0 -83
  201. package/dist/output-verbosity.js.map +0 -1
  202. package/dist/product-boundary.d.ts +0 -11
  203. package/dist/product-boundary.d.ts.map +0 -1
  204. package/dist/product-boundary.js +0 -301
  205. package/dist/product-boundary.js.map +0 -1
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Negative-instruction overload detector (FEAT-010).
3
+ *
4
+ * Models follow positive directives ("use const") more reliably than
5
+ * prohibitions ("don't use var"). A file whose directive sentences are
6
+ * overwhelmingly phrased as prohibitions is harder to follow than the same
7
+ * guidance framed positively.
8
+ *
9
+ * Heuristic: split the file into candidate sentences, classify each as a
10
+ * positive directive, a negative directive, or neither, and — when there are
11
+ * enough directives to be meaningful — flag files where more than 40% of the
12
+ * directives are negative. Info severity only; the wording is deliberately
13
+ * cautious and the recommendation offers a positive-rewrite hint.
14
+ *
15
+ * Deterministic and rule-based: no clock, network, or randomness.
16
+ */
17
+ import * as crypto from 'node:crypto';
18
+ import * as path from 'node:path';
19
+ import { snippet } from './evidence.js';
20
+ /** Persistent, always-loaded instruction files this detector applies to. */
21
+ const TARGET_FILE_TYPES = new Set([
22
+ 'claude',
23
+ 'agents',
24
+ 'cursor',
25
+ 'windsurf',
26
+ 'copilot',
27
+ 'prompt',
28
+ ]);
29
+ /**
30
+ * A sentence is a NEGATIVE directive when it contains one of these prohibition
31
+ * markers. Negative markers take precedence over positive ones, so "always
32
+ * avoid mutation" counts as negative — its operative instruction is a
33
+ * prohibition.
34
+ */
35
+ const NEGATIVE_MARKERS = [
36
+ // Second-person imperative prohibitions only. Third-person "does not" /
37
+ // "doesn't" is descriptive narration ("this file does not repeat it"), not a
38
+ // directive, so it is deliberately excluded to avoid inflating the count.
39
+ /\bdo\s+not\b/i,
40
+ /\bdon['’]?t\b/i,
41
+ /\bnever\b/i,
42
+ /\bavoid\b/i,
43
+ /\brefrain\s+from\b/i,
44
+ /\bmust\s+not\b/i,
45
+ /\bmustn['’]?t\b/i,
46
+ /\bshould\s+not\b/i,
47
+ /\bshouldn['’]?t\b/i,
48
+ /\bcannot\b/i,
49
+ /\bcan['’]?t\b/i,
50
+ /\bwon['’]?t\b/i,
51
+ /\bno\s+longer\b/i,
52
+ ];
53
+ /**
54
+ * A sentence is a POSITIVE directive when it starts with an imperative verb or
55
+ * carries a positive modal ("must"/"should"/"always" not already caught as
56
+ * negative above). This intentionally under-counts rather than over-counts:
57
+ * only clearly directive sentences enter the denominator.
58
+ */
59
+ const POSITIVE_IMPERATIVE_START = /^(use|always|prefer|ensure|make\s+sure|keep|run|write|add|follow|call|set|return|check|verify|read|ask|include|place|put|store|document|test|update|create|define|import|export|name|group|split|choose|pick|apply|treat|surface|confirm|report|explain|favor|favour|default\s+to|stick\s+to|remember\s+to|be\s+sure\s+to)\b/i;
60
+ const POSITIVE_MODAL = /\b(must|should|always|shall)\b/i;
61
+ const MIN_DIRECTIVES = 8; // below this the ratio is too noisy to be meaningful
62
+ const NEGATIVE_RATIO_THRESHOLD = 0.4; // > 40% negative → finding
63
+ /** Strip a leading markdown list bullet / ordered-list marker / heading hash. */
64
+ function stripLeadingMarkers(line) {
65
+ return line
66
+ .replace(/^\s*#{1,6}\s+/, '')
67
+ .replace(/^\s*[-*+]\s+/, '')
68
+ .replace(/^\s*\d+[.)]\s+/, '')
69
+ .replace(/^\s*>\s+/, '')
70
+ .trim();
71
+ }
72
+ /**
73
+ * Strip leading markdown emphasis (`**bold**`, `*italic*`, `` `code` ``) so the
74
+ * operative verb is visible to the imperative test — otherwise "**Report** ..."
75
+ * hides the "Report" that makes the sentence a positive directive.
76
+ */
77
+ function stripLeadingEmphasis(text) {
78
+ return text.replace(/^[*_`]+/, '').trimStart();
79
+ }
80
+ /**
81
+ * Split file content into candidate directive sentences. Fenced code blocks are
82
+ * dropped (a bash comment is not an instruction sentence). Each non-blank line
83
+ * is stripped of list/heading markers, then split on sentence terminators so a
84
+ * line holding two clauses is counted as two.
85
+ */
86
+ export function splitSentences(content) {
87
+ const lines = content.split(/\r?\n/);
88
+ const sentences = [];
89
+ let inFence = false;
90
+ for (const raw of lines) {
91
+ const fenceToggle = /^\s*(```|~~~)/.test(raw);
92
+ if (fenceToggle) {
93
+ inFence = !inFence;
94
+ continue;
95
+ }
96
+ if (inFence)
97
+ continue;
98
+ const cleaned = stripLeadingMarkers(raw);
99
+ if (!cleaned)
100
+ continue;
101
+ for (const part of cleaned.split(/(?<=[.!?])\s+/)) {
102
+ const trimmed = part.trim();
103
+ // Require a few words so table cells / fragments don't count as sentences.
104
+ if (trimmed.split(/\s+/).length >= 3)
105
+ sentences.push(trimmed);
106
+ }
107
+ }
108
+ return sentences;
109
+ }
110
+ function classify(sentence) {
111
+ const lead = stripLeadingEmphasis(sentence);
112
+ // The LEADING imperative governs: a positively-framed instruction that also
113
+ // mentions a prohibition ("Keep diffs focused; do not refactor unrelated
114
+ // code") is a positive directive, not a negative one. Only sentences whose
115
+ // primary verb is not a positive imperative fall through to the prohibition
116
+ // markers.
117
+ if (POSITIVE_IMPERATIVE_START.test(lead))
118
+ return 'positive';
119
+ for (const re of NEGATIVE_MARKERS) {
120
+ if (re.test(sentence))
121
+ return 'negative';
122
+ }
123
+ if (POSITIVE_MODAL.test(sentence))
124
+ return 'positive';
125
+ return 'none';
126
+ }
127
+ function issueId(filePath) {
128
+ const hash = crypto.createHash('sha1').update(`negative-overload:${filePath}`).digest('hex').slice(0, 12);
129
+ return `negative-overload-${hash}`;
130
+ }
131
+ export function detectNegativeInstructionOverload(files) {
132
+ const issues = [];
133
+ for (const file of files) {
134
+ if (!TARGET_FILE_TYPES.has(file.fileType))
135
+ continue;
136
+ const sentences = splitSentences(file.content);
137
+ const negatives = [];
138
+ let positiveCount = 0;
139
+ for (const sentence of sentences) {
140
+ const kind = classify(sentence);
141
+ if (kind === 'negative')
142
+ negatives.push(sentence);
143
+ else if (kind === 'positive')
144
+ positiveCount++;
145
+ }
146
+ const total = negatives.length + positiveCount;
147
+ if (total < MIN_DIRECTIVES)
148
+ continue;
149
+ const ratio = negatives.length / total;
150
+ if (ratio <= NEGATIVE_RATIO_THRESHOLD)
151
+ continue;
152
+ const fileName = path.basename(file.path);
153
+ const pct = Math.round(ratio * 100);
154
+ const examples = negatives.slice(0, 3).map((s) => snippet(s, 100));
155
+ issues.push({
156
+ id: issueId(file.path),
157
+ severity: 'info',
158
+ category: 'agent_practices',
159
+ title: 'High proportion of negative instructions',
160
+ summary: `About ${pct}% of the directive sentences in "${fileName}" are phrased as prohibitions ` +
161
+ `(${negatives.length} of ${total}). Models tend to follow positive directives more ` +
162
+ `reliably than prohibitions, so heavily negative guidance can be harder to apply.`,
163
+ filePaths: [file.path],
164
+ locations: [],
165
+ evidence: examples.map((e) => `Negative directive: "${e}"`),
166
+ recommendation: 'Where practical, rephrase prohibitions as positive directives — e.g. instead of ' +
167
+ '"don\'t use var", write "always use const or let". Keep prohibitions for the cases ' +
168
+ 'where no positive form is possible.',
169
+ confidence: 0.5,
170
+ });
171
+ }
172
+ return issues;
173
+ }
174
+ //# sourceMappingURL=negative-instructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negative-instructions.js","sourceRoot":"","sources":["../src/negative-instructions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * True when `candidatePath` resolves to `root` itself or a descendant of it.
3
+ * Both arguments are resolved to absolute form first, so either may be relative.
4
+ */
5
+ export declare function isWithinRoot(root: string, candidatePath: string): boolean;
6
+ /**
7
+ * Resolve `candidatePath` — treated as relative to `root` unless it is itself
8
+ * absolute — and return the absolute path when it stays within `root`, or
9
+ * `null` when it escapes (via `..`, an absolute path, or a different drive).
10
+ */
11
+ export declare function resolveWithinRoot(root: string, candidatePath: string): string | null;
12
+ //# sourceMappingURL=path-containment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-containment.d.ts","sourceRoot":"","sources":["../src/path-containment.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAYzE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUpF"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The one path-containment check for the whole toolchain.
3
+ *
4
+ * Every place that resolves an externally-influenced path (a config value, an
5
+ * LLM-proposed edit target, a fix's file path) must confirm the result stays
6
+ * inside the repo/target root before touching the filesystem. This module is
7
+ * the single, tested implementation those checks share.
8
+ *
9
+ * The naive guard this replaces — `resolved.startsWith(root)` — is wrong two
10
+ * ways:
11
+ * - **Sibling prefix.** `/repo-evil` starts with `/repo`, so a sibling
12
+ * directory whose name merely shares a prefix reads as "contained".
13
+ * - **Separator / drive blind spots.** On Windows a raw string prefix ignores
14
+ * the path separator boundary and cross-drive absolutes.
15
+ *
16
+ * Comparing the *relative* path from the root sidesteps both: a contained path
17
+ * yields `''` (the root itself) or a forward walk with no leading `..`; any
18
+ * escape yields a leading `..` segment or an absolute path (a different drive).
19
+ */
20
+ import * as path from 'node:path';
21
+ /**
22
+ * True when `candidatePath` resolves to `root` itself or a descendant of it.
23
+ * Both arguments are resolved to absolute form first, so either may be relative.
24
+ */
25
+ export function isWithinRoot(root, candidatePath) {
26
+ const resolvedRoot = path.resolve(root);
27
+ const resolved = path.resolve(candidatePath);
28
+ const rel = path.relative(resolvedRoot, resolved);
29
+ // '' is the root itself. A genuine escape is exactly '..' or begins with
30
+ // '..<sep>' (a child literally named e.g. '..foo' is NOT an escape), or is
31
+ // absolute — which path.relative only returns when the target is on a
32
+ // different Windows drive and cannot be reached by walking up.
33
+ return (rel === '' ||
34
+ (rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel)));
35
+ }
36
+ /**
37
+ * Resolve `candidatePath` — treated as relative to `root` unless it is itself
38
+ * absolute — and return the absolute path when it stays within `root`, or
39
+ * `null` when it escapes (via `..`, an absolute path, or a different drive).
40
+ */
41
+ export function resolveWithinRoot(root, candidatePath) {
42
+ try {
43
+ const resolvedRoot = path.resolve(root);
44
+ const resolved = path.isAbsolute(candidatePath)
45
+ ? path.resolve(candidatePath)
46
+ : path.resolve(resolvedRoot, candidatePath);
47
+ return isWithinRoot(resolvedRoot, resolved) ? resolved : null;
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ //# sourceMappingURL=path-containment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-containment.js","sourceRoot":"","sources":["../src/path-containment.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,aAAqB;IAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IAClD,yEAAyE;IACzE,2EAA2E;IAC3E,sEAAsE;IACtE,+DAA+D;IAC/D,OAAO,CACL,GAAG,KAAK,EAAE;QACV,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAC5E,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,aAAqB;IACnE,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YAC7C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;YAC7B,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QAC9C,OAAO,YAAY,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -1,7 +1,12 @@
1
1
  import type { InstructionFile, ProjectType } from './types.js';
2
2
  /**
3
3
  * Detect the primary project type for a repo at repoRoot.
4
- * Detection order: nextjs → typescript → unity → dotnet → unknown
4
+ * Detection order: nextjs → unity → dotnet → typescript → python → go → rust → unknown
5
+ *
6
+ * Unity/.NET probes run before the package.json→typescript branch: Unity and .NET
7
+ * repos commonly carry a root package.json for JS tooling (husky, prettier), so the
8
+ * bare-package.json check must not shadow their more specific markers. This matches
9
+ * the unity-before-typescript ordering in detectProjectTypeFromContent.
5
10
  */
6
11
  export declare function detectProjectType(repoRoot: string): Promise<ProjectType>;
7
12
  /**
@@ -9,7 +14,12 @@ export declare function detectProjectType(repoRoot: string): Promise<ProjectType
9
14
  * scan returns 'unknown' (e.g. repos that only contain instruction files, no source).
10
15
  * Checks the combined content of all scanned instruction files for strong signals.
11
16
  *
12
- * Detection order: nextjs → unity → typescript → dotnet → python → go → rust → unknown
17
+ * Detection order: nextjs → unity → dotnet → typescript → python → go → rust → unknown
18
+ *
19
+ * .NET is checked before typescript for the same reason as in the file-system
20
+ * probe above: .NET repos commonly mention JS tooling (pnpm, npm run, jest) in
21
+ * their instructions, so the broader typescript signals must not shadow the
22
+ * more specific .NET ones.
13
23
  */
14
24
  export declare function detectProjectTypeFromContent(files: InstructionFile[]): ProjectType;
15
25
  //# sourceMappingURL=project-type.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-type.d.ts","sourceRoot":"","sources":["../src/project-type.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAoB/D;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA4D9E;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,WAAW,CA0BlF"}
1
+ {"version":3,"file":"project-type.d.ts","sourceRoot":"","sources":["../src/project-type.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAoB/D;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA+D9E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,WAAW,CA0BlF"}
@@ -21,7 +21,12 @@ async function dirExists(p) {
21
21
  }
22
22
  /**
23
23
  * Detect the primary project type for a repo at repoRoot.
24
- * Detection order: nextjs → typescript → unity → dotnet → unknown
24
+ * Detection order: nextjs → unity → dotnet → typescript → python → go → rust → unknown
25
+ *
26
+ * Unity/.NET probes run before the package.json→typescript branch: Unity and .NET
27
+ * repos commonly carry a root package.json for JS tooling (husky, prettier), so the
28
+ * bare-package.json check must not shadow their more specific markers. This matches
29
+ * the unity-before-typescript ordering in detectProjectTypeFromContent.
25
30
  */
26
31
  export async function detectProjectType(repoRoot) {
27
32
  const root = path.resolve(repoRoot);
@@ -42,19 +47,17 @@ export async function detectProjectType(repoRoot) {
42
47
  if (hasApp || hasPages)
43
48
  return 'nextjs';
44
49
  }
45
- // ── TypeScript ───────────────────────────────────────────────────────────
46
- // package.json or tsconfig.json present
47
- const hasTsConfig = await exists(path.join(root, 'tsconfig.json'));
48
- if (hasPkgJson || hasTsConfig)
49
- return 'typescript';
50
50
  // ── Unity ────────────────────────────────────────────────────────────────
51
- // Assets/ directory AND ProjectSettings/ProjectVersion.txt both exist
51
+ // Assets/ directory AND ProjectSettings/ProjectVersion.txt both exist.
52
+ // Checked before the package.json→typescript branch so a Unity repo with a
53
+ // root package.json is not misclassified as typescript.
52
54
  const hasAssets = await dirExists(path.join(root, 'Assets'));
53
55
  const hasProjectVersion = await exists(path.join(root, 'ProjectSettings', 'ProjectVersion.txt'));
54
56
  if (hasAssets && hasProjectVersion)
55
57
  return 'unity';
56
58
  // ── .NET ─────────────────────────────────────────────────────────────────
57
- // any .sln or .csproj file at repo root level
59
+ // any .sln or .csproj file at repo root level. Also checked before the
60
+ // typescript branch for the same reason (JS tooling drops a package.json).
58
61
  const dotnetFiles = await fg(['*.sln', '*.csproj'], {
59
62
  cwd: root,
60
63
  dot: false,
@@ -63,6 +66,11 @@ export async function detectProjectType(repoRoot) {
63
66
  });
64
67
  if (dotnetFiles.length > 0)
65
68
  return 'dotnet';
69
+ // ── TypeScript ───────────────────────────────────────────────────────────
70
+ // package.json or tsconfig.json present
71
+ const hasTsConfig = await exists(path.join(root, 'tsconfig.json'));
72
+ if (hasPkgJson || hasTsConfig)
73
+ return 'typescript';
66
74
  // ── Python ───────────────────────────────────────────────────────────────
67
75
  const hasPyProject = await exists(path.join(root, 'pyproject.toml'));
68
76
  const hasRequirements = await exists(path.join(root, 'requirements.txt'));
@@ -86,7 +94,12 @@ export async function detectProjectType(repoRoot) {
86
94
  * scan returns 'unknown' (e.g. repos that only contain instruction files, no source).
87
95
  * Checks the combined content of all scanned instruction files for strong signals.
88
96
  *
89
- * Detection order: nextjs → unity → typescript → dotnet → python → go → rust → unknown
97
+ * Detection order: nextjs → unity → dotnet → typescript → python → go → rust → unknown
98
+ *
99
+ * .NET is checked before typescript for the same reason as in the file-system
100
+ * probe above: .NET repos commonly mention JS tooling (pnpm, npm run, jest) in
101
+ * their instructions, so the broader typescript signals must not shadow the
102
+ * more specific .NET ones.
90
103
  */
91
104
  export function detectProjectTypeFromContent(files) {
92
105
  const combined = files.map((f) => f.content).join('\n');
@@ -96,12 +109,12 @@ export function detectProjectTypeFromContent(files) {
96
109
  if ((/\bunity\b/i.test(combined) && /\b(lts|editor|hub|engine)\b/i.test(combined)) || /\bmonobehaviour\b|\bunityengine\b|\bgameobject\b|\bprefab\b|\bscriptableobject\b/i.test(combined)) {
97
110
  return 'unity';
98
111
  }
99
- if (/\btypescript\b|\btsconfig\b|\btsc\s|\bpnpm\b|\bnpm\s+run\b|\bvitest\b|\bjest\b/i.test(combined)) {
100
- return 'typescript';
101
- }
102
112
  if (/\bdotnet\b|\.sln\b|\.csproj\b|\bxunit\b|\bnunit\b|\bmstest\b/i.test(combined)) {
103
113
  return 'dotnet';
104
114
  }
115
+ if (/\btypescript\b|\btsconfig\b|\btsc\s|\bpnpm\b|\bnpm\s+run\b|\bvitest\b|\bjest\b/i.test(combined)) {
116
+ return 'typescript';
117
+ }
105
118
  if (/\bpython\b|\bpip\b|\bpoetry\b|\bvenv\b|\bpytest\b|\bpyproject\.toml\b/i.test(combined)) {
106
119
  return 'python';
107
120
  }
@@ -1 +1 @@
1
- {"version":3,"file":"project-type.js","sourceRoot":"","sources":["../src/project-type.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project-type.js","sourceRoot":"","sources":["../src/project-type.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-cache.d.ts","sourceRoot":"","sources":["../src/prompt-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAqCjE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CAiIvF"}
1
+ {"version":3,"file":"prompt-cache.d.ts","sourceRoot":"","sources":["../src/prompt-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAmDjE,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CAiIvF"}
@@ -1,3 +1,4 @@
1
+ import { matchEvidence } from './evidence.js';
1
2
  import * as path from 'node:path';
2
3
  import * as crypto from 'node:crypto';
3
4
  /**
@@ -10,33 +11,39 @@ function promptCacheIssueId(kind, filePath) {
10
11
  return `prompt-cache-${kind}-${hash}`;
11
12
  }
12
13
  const DATE_PATTERNS = [
13
- /\b(?:last\s+updated|last\s+modified|updated\s+at|modified\s+at|date)\b\s*:\s*\b\d{4}-\d{2}-\d{2}\b/i,
14
- /\b\d{4}-\d{2}-\d{2}t\d{2}:\d{2}:\d{2}\b/i,
15
- /\b(?:january|february|march|april|may|june|july|august|september|october|november|december)\s+\d{1,2}(?:st|nd|rd|th)?,\s+\d{4}\b/i,
14
+ {
15
+ re: /\b(?:last\s+updated|last\s+modified|updated\s+at|modified\s+at|date)\b\s*:\s*\b\d{4}-\d{2}-\d{2}\b/i,
16
+ label: 'Dated "last updated"/"modified" line',
17
+ },
18
+ { re: /\b\d{4}-\d{2}-\d{2}t\d{2}:\d{2}:\d{2}\b/i, label: 'ISO 8601 timestamp' },
19
+ {
20
+ re: /\b(?:january|february|march|april|may|june|july|august|september|october|november|december)\s+\d{1,2}(?:st|nd|rd|th)?,\s+\d{4}\b/i,
21
+ label: 'Long-form calendar date',
22
+ },
16
23
  ];
17
24
  const SCAN_REPORT_PATTERNS = [
18
- /health\s+score\s*:\s*\d+/i,
19
- /files\s+scanned\s*:\s*\d+/i,
20
- /promptci\s+report/i,
21
- /\bgenerated\s+by\s+promptci\b/i,
22
- /latest\s+scan\s+results/i,
23
- /scan\s+history/i,
25
+ { re: /health\s+score\s*:\s*\d+/i, label: 'Health-score line from a scan report' },
26
+ { re: /files\s+scanned\s*:\s*\d+/i, label: 'File-count line from a scan report' },
27
+ { re: /promptci\s+report/i, label: 'PromptCI report heading' },
28
+ { re: /\bgenerated\s+by\s+promptci\b/i, label: 'PromptCI generated-by marker' },
29
+ { re: /latest\s+scan\s+results/i, label: 'Pasted "latest scan results" section' },
30
+ { re: /scan\s+history/i, label: 'Pasted "scan history" section' },
24
31
  ];
25
32
  const BRANCH_TASK_PATTERNS = [
26
- /current\s+branch\s*:\s*\S+/i,
27
- /git\s+branch\s*:\s*\S+/i,
28
- /current\s+task\s*:\s*\S+/i,
33
+ { re: /current\s+branch\s*:\s*\S+/i, label: 'Current-branch line' },
34
+ { re: /git\s+branch\s*:\s*\S+/i, label: 'Git-branch line' },
35
+ { re: /current\s+task\s*:\s*\S+/i, label: 'Current-task line' },
29
36
  ];
30
37
  const LOCAL_PATH_PATTERN = /(?:[a-zA-Z]:\\users\\[a-zA-Z0-9_-]+)|(?:\/users\/[a-zA-Z0-9_-]+\/)|(?:\/home\/(?!runner)[a-zA-Z0-9_-]+\/)/i;
31
38
  export function detectPromptCacheFriendliness(files) {
32
39
  const issues = [];
33
40
  // Focus only on persistent, always-loaded instruction files
34
- const targetFiles = files.filter((f) => ['claude', 'agents', 'cursor', 'copilot', 'prompt'].includes(f.fileType));
41
+ const targetFiles = files.filter((f) => ['claude', 'agents', 'cursor', 'windsurf', 'copilot', 'prompt'].includes(f.fileType));
35
42
  for (const file of targetFiles) {
36
43
  const fileName = path.basename(file.path);
37
44
  // 1. Timestamps and Dates
38
45
  for (const pat of DATE_PATTERNS) {
39
- if (pat.test(file.content)) {
46
+ if (pat.re.test(file.content)) {
40
47
  issues.push({
41
48
  id: promptCacheIssueId('volatile-timestamp', file.path),
42
49
  severity: 'warning',
@@ -45,7 +52,7 @@ export function detectPromptCacheFriendliness(files) {
45
52
  summary: `The instruction file "${fileName}" contains a volatile date or timestamp. Frequently changing dates in persistent instruction files invalidate prompt caches.`,
46
53
  filePaths: [file.path],
47
54
  locations: [],
48
- evidence: [`Matched pattern: ${pat.toString()}`],
55
+ evidence: [matchEvidence(file.content, pat.re, pat.label)],
49
56
  recommendation: 'Move volatile dates to a separate task-specific prompt or scratch file, or remove them from canonical instructions.',
50
57
  fixRecipe: 'Remove the "Last updated: <date>" or timestamp line from the file.',
51
58
  confidence: 0.85,
@@ -55,7 +62,7 @@ export function detectPromptCacheFriendliness(files) {
55
62
  }
56
63
  // 2. Pasted Scan Outputs/Reports
57
64
  for (const pat of SCAN_REPORT_PATTERNS) {
58
- if (pat.test(file.content)) {
65
+ if (pat.re.test(file.content)) {
59
66
  issues.push({
60
67
  id: promptCacheIssueId('pasted-report', file.path),
61
68
  severity: 'warning',
@@ -64,7 +71,7 @@ export function detectPromptCacheFriendliness(files) {
64
71
  summary: `The instruction file "${fileName}" contains pasted scan results or reports. Committing generated reports into canonical instruction files breaks prompt caching and bloats context.`,
65
72
  filePaths: [file.path],
66
73
  locations: [],
67
- evidence: [`Matched pattern: ${pat.toString()}`],
74
+ evidence: [matchEvidence(file.content, pat.re, pat.label)],
68
75
  recommendation: 'Link to the generated report file or read it on-demand instead of pasting its contents into canonical instructions.',
69
76
  fixRecipe: 'Delete the pasted report summary block and reference the report file path instead.',
70
77
  confidence: 0.9,
@@ -74,7 +81,7 @@ export function detectPromptCacheFriendliness(files) {
74
81
  }
75
82
  // 3. Current Branch or Task status
76
83
  for (const pat of BRANCH_TASK_PATTERNS) {
77
- if (pat.test(file.content)) {
84
+ if (pat.re.test(file.content)) {
78
85
  issues.push({
79
86
  id: promptCacheIssueId('volatile-branch-task', file.path),
80
87
  severity: 'warning',
@@ -83,7 +90,7 @@ export function detectPromptCacheFriendliness(files) {
83
90
  summary: `The instruction file "${fileName}" contains active git branch or task details. Frequently updated task status or branch names break prompt caching.`,
84
91
  filePaths: [file.path],
85
92
  locations: [],
86
- evidence: [`Matched pattern: ${pat.toString()}`],
93
+ evidence: [matchEvidence(file.content, pat.re, pat.label)],
87
94
  recommendation: 'Move branch-specific notes and active task details to git commit messages, PR descriptions, or a temporary scratch file.',
88
95
  fixRecipe: 'Remove active branch and task notes from the persistent instructions.',
89
96
  confidence: 0.85,
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-cache.js","sourceRoot":"","sources":["../src/prompt-cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prompt-cache.js","sourceRoot":"","sources":["../src/prompt-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The recommended `.gitignore` stanza for the `.promptci/` directory.
3
+ *
4
+ * BUG-7: the old recommendation everywhere — `init`, the security-pack
5
+ * detector, the docs, and this repo's own .gitignore — was a bare
6
+ * `.promptci/`, which ignores the entire directory including
7
+ * `.promptci/baseline.json`. The CI ratchet (`--baseline` / `--fail-on-new`)
8
+ * only works when the team's agreed baseline is committed and readable in CI,
9
+ * so the recommended pattern was defeating the feature the directory exists to
10
+ * serve. `config.json` is carved out for the same reason: a per-repo config
11
+ * that only lives on one developer's machine is not a per-repo config.
12
+ *
13
+ * `custom-rules.json` is carved out on the same principle: custom rules must be
14
+ * committed for a local scan and the GitHub Action to see the same rules — a
15
+ * gitignored rule file would make the two disagree.
16
+ *
17
+ * Two details of the pattern are load-bearing:
18
+ *
19
+ * - `.promptci/*` rather than `.promptci/`. Git does not descend into an
20
+ * ignored *directory*, so with a trailing-slash pattern the `!` negations
21
+ * below would never be evaluated. Matching the directory's *contents*
22
+ * leaves them reachable.
23
+ * - The leading globstar segment. A pattern containing a slash is anchored
24
+ * to the .gitignore's own directory, so an unprefixed `.promptci/*` would
25
+ * stop covering nested checkouts that the old unanchored `.promptci/` did
26
+ * cover — in a monorepo, every sub-package's generated reports would start
27
+ * showing up as untracked files.
28
+ */
29
+ export declare const PROMPTCI_GITIGNORE_LINES: readonly string[];
30
+ /** The same stanza as text, LF-terminated. Callers on CRLF files should re-join. */
31
+ export declare const PROMPTCI_GITIGNORE_BLOCK: string;
32
+ /** Renders the stanza with the given line ending, trailing terminator included. */
33
+ export declare function renderPromptCiGitignore(lineEnding: string): string;
34
+ //# sourceMappingURL=promptci-gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptci-gitignore.d.ts","sourceRoot":"","sources":["../src/promptci-gitignore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAMrD,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,EAAE,MAAmD,CAAC;AAE3F,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAElE"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The recommended `.gitignore` stanza for the `.promptci/` directory.
3
+ *
4
+ * BUG-7: the old recommendation everywhere — `init`, the security-pack
5
+ * detector, the docs, and this repo's own .gitignore — was a bare
6
+ * `.promptci/`, which ignores the entire directory including
7
+ * `.promptci/baseline.json`. The CI ratchet (`--baseline` / `--fail-on-new`)
8
+ * only works when the team's agreed baseline is committed and readable in CI,
9
+ * so the recommended pattern was defeating the feature the directory exists to
10
+ * serve. `config.json` is carved out for the same reason: a per-repo config
11
+ * that only lives on one developer's machine is not a per-repo config.
12
+ *
13
+ * `custom-rules.json` is carved out on the same principle: custom rules must be
14
+ * committed for a local scan and the GitHub Action to see the same rules — a
15
+ * gitignored rule file would make the two disagree.
16
+ *
17
+ * Two details of the pattern are load-bearing:
18
+ *
19
+ * - `.promptci/*` rather than `.promptci/`. Git does not descend into an
20
+ * ignored *directory*, so with a trailing-slash pattern the `!` negations
21
+ * below would never be evaluated. Matching the directory's *contents*
22
+ * leaves them reachable.
23
+ * - The leading globstar segment. A pattern containing a slash is anchored
24
+ * to the .gitignore's own directory, so an unprefixed `.promptci/*` would
25
+ * stop covering nested checkouts that the old unanchored `.promptci/` did
26
+ * cover — in a monorepo, every sub-package's generated reports would start
27
+ * showing up as untracked files.
28
+ */
29
+ export const PROMPTCI_GITIGNORE_LINES = [
30
+ '# PromptCI: ignore generated reports, keep the shared baseline, config, and custom rules',
31
+ '**/.promptci/*',
32
+ '!**/.promptci/baseline.json',
33
+ '!**/.promptci/config.json',
34
+ '!**/.promptci/custom-rules.json',
35
+ ];
36
+ /** The same stanza as text, LF-terminated. Callers on CRLF files should re-join. */
37
+ export const PROMPTCI_GITIGNORE_BLOCK = `${PROMPTCI_GITIGNORE_LINES.join('\n')}\n`;
38
+ /** Renders the stanza with the given line ending, trailing terminator included. */
39
+ export function renderPromptCiGitignore(lineEnding) {
40
+ return `${PROMPTCI_GITIGNORE_LINES.join(lineEnding)}${lineEnding}`;
41
+ }
42
+ //# sourceMappingURL=promptci-gitignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptci-gitignore.js","sourceRoot":"","sources":["../src/promptci-gitignore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAsB;IACzD,0FAA0F;IAC1F,gBAAgB;IAChB,6BAA6B;IAC7B,2BAA2B;IAC3B,iCAAiC;CAClC,CAAC;AAEF,oFAAoF;AACpF,MAAM,CAAC,MAAM,wBAAwB,GAAW,GAAG,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AAE3F,mFAAmF;AACnF,MAAM,UAAU,uBAAuB,CAAC,UAAkB;IACxD,OAAO,GAAG,wBAAwB,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,UAAU,EAAE,CAAC;AACrE,CAAC"}