@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.
Files changed (179) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +30 -0
  3. package/dist/agent-practices.d.ts +22 -0
  4. package/dist/agent-practices.d.ts.map +1 -0
  5. package/dist/agent-practices.js +582 -0
  6. package/dist/agent-practices.js.map +1 -0
  7. package/dist/baseline.d.ts +40 -0
  8. package/dist/baseline.d.ts.map +1 -0
  9. package/dist/baseline.js +172 -0
  10. package/dist/baseline.js.map +1 -0
  11. package/dist/canonical-owner.d.ts +3 -0
  12. package/dist/canonical-owner.d.ts.map +1 -0
  13. package/dist/canonical-owner.js +131 -0
  14. package/dist/canonical-owner.js.map +1 -0
  15. package/dist/cheap-model-routing.d.ts +4 -0
  16. package/dist/cheap-model-routing.d.ts.map +1 -0
  17. package/dist/cheap-model-routing.js +43 -0
  18. package/dist/cheap-model-routing.js.map +1 -0
  19. package/dist/ci-alignment.d.ts +10 -0
  20. package/dist/ci-alignment.d.ts.map +1 -0
  21. package/dist/ci-alignment.js +343 -0
  22. package/dist/ci-alignment.js.map +1 -0
  23. package/dist/cli-output-filter.d.ts +16 -0
  24. package/dist/cli-output-filter.d.ts.map +1 -0
  25. package/dist/cli-output-filter.js +88 -0
  26. package/dist/cli-output-filter.js.map +1 -0
  27. package/dist/command-validity.d.ts +12 -0
  28. package/dist/command-validity.d.ts.map +1 -0
  29. package/dist/command-validity.js +543 -0
  30. package/dist/command-validity.js.map +1 -0
  31. package/dist/conflicts.d.ts +49 -0
  32. package/dist/conflicts.d.ts.map +1 -0
  33. package/dist/conflicts.js +1065 -0
  34. package/dist/conflicts.js.map +1 -0
  35. package/dist/context-analysis.d.ts +15 -0
  36. package/dist/context-analysis.d.ts.map +1 -0
  37. package/dist/context-analysis.js +24 -0
  38. package/dist/context-analysis.js.map +1 -0
  39. package/dist/context-bloat.d.ts +17 -0
  40. package/dist/context-bloat.d.ts.map +1 -0
  41. package/dist/context-bloat.js +148 -0
  42. package/dist/context-bloat.js.map +1 -0
  43. package/dist/context-cost.d.ts +4 -0
  44. package/dist/context-cost.d.ts.map +1 -0
  45. package/dist/context-cost.js +70 -0
  46. package/dist/context-cost.js.map +1 -0
  47. package/dist/context-optimizer.d.ts +14 -0
  48. package/dist/context-optimizer.d.ts.map +1 -0
  49. package/dist/context-optimizer.js +219 -0
  50. package/dist/context-optimizer.js.map +1 -0
  51. package/dist/context-recommender.d.ts +3 -0
  52. package/dist/context-recommender.d.ts.map +1 -0
  53. package/dist/context-recommender.js +329 -0
  54. package/dist/context-recommender.js.map +1 -0
  55. package/dist/dead-references.d.ts +33 -0
  56. package/dist/dead-references.d.ts.map +1 -0
  57. package/dist/dead-references.js +607 -0
  58. package/dist/dead-references.js.map +1 -0
  59. package/dist/detectors.d.ts +11 -0
  60. package/dist/detectors.d.ts.map +1 -0
  61. package/dist/detectors.js +160 -0
  62. package/dist/detectors.js.map +1 -0
  63. package/dist/duplicates.d.ts +36 -0
  64. package/dist/duplicates.d.ts.map +1 -0
  65. package/dist/duplicates.js +436 -0
  66. package/dist/duplicates.js.map +1 -0
  67. package/dist/fix-engine.d.ts +16 -0
  68. package/dist/fix-engine.d.ts.map +1 -0
  69. package/dist/fix-engine.js +179 -0
  70. package/dist/fix-engine.js.map +1 -0
  71. package/dist/framework-packs.d.ts +8 -0
  72. package/dist/framework-packs.d.ts.map +1 -0
  73. package/dist/framework-packs.js +22 -0
  74. package/dist/framework-packs.js.map +1 -0
  75. package/dist/frameworks/dotnet.d.ts +4 -0
  76. package/dist/frameworks/dotnet.d.ts.map +1 -0
  77. package/dist/frameworks/dotnet.js +77 -0
  78. package/dist/frameworks/dotnet.js.map +1 -0
  79. package/dist/frameworks/go.d.ts +4 -0
  80. package/dist/frameworks/go.d.ts.map +1 -0
  81. package/dist/frameworks/go.js +39 -0
  82. package/dist/frameworks/go.js.map +1 -0
  83. package/dist/frameworks/nextjs.d.ts +4 -0
  84. package/dist/frameworks/nextjs.d.ts.map +1 -0
  85. package/dist/frameworks/nextjs.js +66 -0
  86. package/dist/frameworks/nextjs.js.map +1 -0
  87. package/dist/frameworks/python.d.ts +4 -0
  88. package/dist/frameworks/python.d.ts.map +1 -0
  89. package/dist/frameworks/python.js +66 -0
  90. package/dist/frameworks/python.js.map +1 -0
  91. package/dist/frameworks/rust.d.ts +4 -0
  92. package/dist/frameworks/rust.d.ts.map +1 -0
  93. package/dist/frameworks/rust.js +72 -0
  94. package/dist/frameworks/rust.js.map +1 -0
  95. package/dist/frameworks/unity.d.ts +4 -0
  96. package/dist/frameworks/unity.d.ts.map +1 -0
  97. package/dist/frameworks/unity.js +74 -0
  98. package/dist/frameworks/unity.js.map +1 -0
  99. package/dist/health-score.d.ts +37 -0
  100. package/dist/health-score.d.ts.map +1 -0
  101. package/dist/health-score.js +164 -0
  102. package/dist/health-score.js.map +1 -0
  103. package/dist/index.d.ts +31 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +23 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/llm.d.ts +6 -0
  108. package/dist/llm.d.ts.map +1 -0
  109. package/dist/llm.js +67 -0
  110. package/dist/llm.js.map +1 -0
  111. package/dist/manifest-consistency.d.ts +37 -0
  112. package/dist/manifest-consistency.d.ts.map +1 -0
  113. package/dist/manifest-consistency.js +659 -0
  114. package/dist/manifest-consistency.js.map +1 -0
  115. package/dist/mcp-detector.d.ts +4 -0
  116. package/dist/mcp-detector.d.ts.map +1 -0
  117. package/dist/mcp-detector.js +131 -0
  118. package/dist/mcp-detector.js.map +1 -0
  119. package/dist/missing-context.d.ts +15 -0
  120. package/dist/missing-context.d.ts.map +1 -0
  121. package/dist/missing-context.js +517 -0
  122. package/dist/missing-context.js.map +1 -0
  123. package/dist/native-tool-preference.d.ts +20 -0
  124. package/dist/native-tool-preference.d.ts.map +1 -0
  125. package/dist/native-tool-preference.js +94 -0
  126. package/dist/native-tool-preference.js.map +1 -0
  127. package/dist/output-verbosity.d.ts +3 -0
  128. package/dist/output-verbosity.d.ts.map +1 -0
  129. package/dist/output-verbosity.js +83 -0
  130. package/dist/output-verbosity.js.map +1 -0
  131. package/dist/product-boundary.d.ts +11 -0
  132. package/dist/product-boundary.d.ts.map +1 -0
  133. package/dist/product-boundary.js +301 -0
  134. package/dist/product-boundary.js.map +1 -0
  135. package/dist/project-type.d.ts +15 -0
  136. package/dist/project-type.d.ts.map +1 -0
  137. package/dist/project-type.js +116 -0
  138. package/dist/project-type.js.map +1 -0
  139. package/dist/prompt-cache.d.ts +3 -0
  140. package/dist/prompt-cache.d.ts.map +1 -0
  141. package/dist/prompt-cache.js +151 -0
  142. package/dist/prompt-cache.js.map +1 -0
  143. package/dist/repo-context.d.ts +37 -0
  144. package/dist/repo-context.d.ts.map +1 -0
  145. package/dist/repo-context.js +221 -0
  146. package/dist/repo-context.js.map +1 -0
  147. package/dist/report.d.ts +87 -0
  148. package/dist/report.d.ts.map +1 -0
  149. package/dist/report.js +558 -0
  150. package/dist/report.js.map +1 -0
  151. package/dist/scan.d.ts +7 -0
  152. package/dist/scan.d.ts.map +1 -0
  153. package/dist/scan.js +43 -0
  154. package/dist/scan.js.map +1 -0
  155. package/dist/scanner.d.ts +3 -0
  156. package/dist/scanner.d.ts.map +1 -0
  157. package/dist/scanner.js +216 -0
  158. package/dist/scanner.js.map +1 -0
  159. package/dist/security-pack.d.ts +4 -0
  160. package/dist/security-pack.d.ts.map +1 -0
  161. package/dist/security-pack.js +403 -0
  162. package/dist/security-pack.js.map +1 -0
  163. package/dist/stale-instructions.d.ts +20 -0
  164. package/dist/stale-instructions.d.ts.map +1 -0
  165. package/dist/stale-instructions.js +269 -0
  166. package/dist/stale-instructions.js.map +1 -0
  167. package/dist/suppression.d.ts +64 -0
  168. package/dist/suppression.d.ts.map +1 -0
  169. package/dist/suppression.js +338 -0
  170. package/dist/suppression.js.map +1 -0
  171. package/dist/types.d.ts +128 -0
  172. package/dist/types.d.ts.map +1 -0
  173. package/dist/types.js +3 -0
  174. package/dist/types.js.map +1 -0
  175. package/dist/vague-guidance.d.ts +12 -0
  176. package/dist/vague-guidance.d.ts.map +1 -0
  177. package/dist/vague-guidance.js +235 -0
  178. package/dist/vague-guidance.js.map +1 -0
  179. package/package.json +54 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-tool-preference.js","sourceRoot":"","sources":["../src/native-tool-preference.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { InstructionFile, PromptCiIssue } from './types.js';
2
+ export declare function detectOutputVerbosity(files: InstructionFile[]): PromptCiIssue[];
3
+ //# sourceMappingURL=output-verbosity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-verbosity.d.ts","sourceRoot":"","sources":["../src/output-verbosity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiCjE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CA8D/E"}
@@ -0,0 +1,83 @@
1
+ import * as path from 'node:path';
2
+ import * as crypto from 'node:crypto';
3
+ /**
4
+ * B4: id was keyed by `${file.fileType}` — two files sharing a fileType (e.g.
5
+ * CLAUDE.md and .claude/notes.md, both 'claude') collided on the same id.
6
+ */
7
+ function verbosityEncouragedIssueId(filePath) {
8
+ const hash = crypto.createHash('sha1').update(`cost-verbosity-encouraged:${filePath}`).digest('hex').slice(0, 12);
9
+ return `cost-verbosity-encouraged-${hash}`;
10
+ }
11
+ const VERBOSITY_ENCOURAGED_PATTERNS = [
12
+ /explain\s+everything\s+in\s+detail/i,
13
+ /explain\s+in\s+detail/i,
14
+ /always\s+include\s+full\s+command\s+output/i,
15
+ /provide\s+exhaustive\s+summaries/i,
16
+ /print\s+the\s+entire\s+report/i,
17
+ /include\s+all\s+logs/i,
18
+ /\b(?:provide|include|write|give)\s+(?:a\s+)?detailed\s+explanation\b/i,
19
+ ];
20
+ const CONCISENESS_PATTERNS = [
21
+ /summarize\s+command/i,
22
+ /do\s+not\s+paste\s+(?:full\s+)?logs/i,
23
+ /keep\s+final\s+answers\s+concise/i,
24
+ /be\s+concise/i,
25
+ /brief\s+summary/i,
26
+ /only\s+key\s+failures/i,
27
+ /use\s+links\s+to\s+generated\s+artifacts/i,
28
+ ];
29
+ export function detectOutputVerbosity(files) {
30
+ const issues = [];
31
+ const targetFiles = files.filter((f) => ['claude', 'agents', 'cursor', 'copilot', 'prompt'].includes(f.fileType));
32
+ if (targetFiles.length === 0) {
33
+ return [];
34
+ }
35
+ const combinedContent = targetFiles.map((f) => f.content).join('\n');
36
+ // 1. Check for verbosity encouraged (e.g. demanding full logs, verbose explanations)
37
+ for (const file of targetFiles) {
38
+ const fileName = path.basename(file.path);
39
+ const lines = file.content.split(/\r?\n/);
40
+ for (const line of lines) {
41
+ const matchesVerbosity = VERBOSITY_ENCOURAGED_PATTERNS.some((pat) => pat.test(line));
42
+ if (matchesVerbosity) {
43
+ // Exclude conditional debugging rules (e.g., "include logs when failing")
44
+ const isConditional = /fail|error|debug|crash|when\s+failing/i.test(line);
45
+ if (!isConditional) {
46
+ issues.push({
47
+ id: verbosityEncouragedIssueId(file.path),
48
+ severity: 'warning',
49
+ category: 'agent_practices',
50
+ title: 'Verbose output behavior encouraged',
51
+ summary: `The instruction file "${fileName}" encourages the agent to produce overly verbose outputs or print excessive logs, increasing output-token costs.`,
52
+ filePaths: [file.path],
53
+ locations: [],
54
+ evidence: [`Matched verbose instruction line: "${line.trim()}"`],
55
+ recommendation: 'Encourage concise status updates and final answers to reduce output token cost. Summarize logs and command results instead of pasting them in full.',
56
+ fixRecipe: 'Replace verbosity demands with instructions like: "Be concise and summarize results, linking to files instead of printing them in full."',
57
+ confidence: 0.85,
58
+ });
59
+ break; // one issue per file is enough
60
+ }
61
+ }
62
+ }
63
+ }
64
+ // 2. Check for missing conciseness guidelines
65
+ const hasConcisenessGuidelines = CONCISENESS_PATTERNS.some((pat) => pat.test(combinedContent));
66
+ if (!hasConcisenessGuidelines) {
67
+ issues.push({
68
+ id: 'cost-missing-verbosity-discipline',
69
+ severity: 'info',
70
+ category: 'agent_practices',
71
+ title: 'Missing output verbosity discipline',
72
+ summary: 'No conciseness or verbosity discipline guidelines found in instruction files. Verbose agents write redundant code dumps and explanations, bloating output tokens.',
73
+ filePaths: targetFiles.map((f) => f.path),
74
+ locations: [],
75
+ evidence: ['No guidelines found encouraging conciseness, log summarization, or artifact linking.'],
76
+ recommendation: 'Add guidance to keep answers concise, summarize command output, and link to files instead of pasting large content.',
77
+ fixRecipe: 'Add a section: "Summarize command results. Do not paste full logs unless requested. Keep final answers concise."',
78
+ confidence: 0.8,
79
+ });
80
+ }
81
+ return issues;
82
+ }
83
+ //# sourceMappingURL=output-verbosity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-verbosity.js","sourceRoot":"","sources":["../src/output-verbosity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC;;;GAGG;AACH,SAAS,0BAA0B,CAAC,QAAgB;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,6BAA6B,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAClH,OAAO,6BAA6B,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,6BAA6B,GAAG;IACpC,qCAAqC;IACrC,wBAAwB;IACxB,6CAA6C;IAC7C,mCAAmC;IACnC,gCAAgC;IAChC,uBAAuB;IACvB,uEAAuE;CACxE,CAAC;AAEF,MAAM,oBAAoB,GAAG;IAC3B,sBAAsB;IACtB,sCAAsC;IACtC,mCAAmC;IACnC,eAAe;IACf,kBAAkB;IAClB,wBAAwB;IACxB,2CAA2C;CAC5C,CAAC;AAEF,MAAM,UAAU,qBAAqB,CAAC,KAAwB;IAC5D,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CACrC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CACzE,CAAC;IAEF,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAErE,qFAAqF;IACrF,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,gBAAgB,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACrF,IAAI,gBAAgB,EAAE,CAAC;gBACrB,0EAA0E;gBAC1E,MAAM,aAAa,GAAG,wCAAwC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC1E,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,MAAM,CAAC,IAAI,CAAC;wBACV,EAAE,EAAE,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC;wBACzC,QAAQ,EAAE,SAAS;wBACnB,QAAQ,EAAE,iBAAiB;wBAC3B,KAAK,EAAE,oCAAoC;wBAC3C,OAAO,EAAE,yBAAyB,QAAQ,kHAAkH;wBAC5J,SAAS,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;wBACtB,SAAS,EAAE,EAAE;wBACb,QAAQ,EAAE,CAAC,sCAAsC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC;wBAChE,cAAc,EAAE,qJAAqJ;wBACrK,SAAS,EAAE,0IAA0I;wBACrJ,UAAU,EAAE,IAAI;qBACjB,CAAC,CAAC;oBACH,MAAM,CAAC,+BAA+B;gBACxC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,8CAA8C;IAC9C,MAAM,wBAAwB,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;IAC/F,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,mCAAmC;YACvC,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,iBAAiB;YAC3B,KAAK,EAAE,qCAAqC;YAC5C,OAAO,EAAE,mKAAmK;YAC5K,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACzC,SAAS,EAAE,EAAE;YACb,QAAQ,EAAE,CAAC,sFAAsF,CAAC;YAClG,cAAc,EAAE,qHAAqH;YACrI,SAAS,EAAE,kHAAkH;YAC7H,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { RepoContext } from './repo-context.js';
2
+ import type { PromptCiIssue } from './types.js';
3
+ /**
4
+ * Detects stale product-boundary instructions that contradict filesystem
5
+ * evidence of a feature already existing in the repo.
6
+ *
7
+ * One finding is emitted per (capability × file) regardless of how many
8
+ * contradictory patterns match in that file, to keep the report actionable.
9
+ */
10
+ export declare function detectProductBoundary(context: RepoContext): PromptCiIssue[];
11
+ //# sourceMappingURL=product-boundary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-boundary.d.ts","sourceRoot":"","sources":["../src/product-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAmUhD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,GAAG,aAAa,EAAE,CAqC3E"}
@@ -0,0 +1,301 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as crypto from 'node:crypto';
4
+ /**
5
+ * Product-Boundary Drift Detector
6
+ *
7
+ * Detects when instruction files contain stale "X does not exist" or
8
+ * "do not build X" guidance that contradicts clear filesystem evidence of X
9
+ * already existing in the repo.
10
+ *
11
+ * Philosophy:
12
+ * - Require STRONG filesystem evidence before flagging (multiple corroborating
13
+ * signals where possible).
14
+ * - Matched text from the instruction file is shown in evidence — the raw
15
+ * regex source is never exposed to users.
16
+ * - One finding per (capability × file), regardless of how many patterns match.
17
+ * - Wording is cautious throughout ("appears to", "may be stale").
18
+ *
19
+ * Findings are heuristic — cautious wording is intentional.
20
+ */
21
+ function issueId(key) {
22
+ const hash = crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);
23
+ return `prod-bound-${hash}`;
24
+ }
25
+ // ── Filesystem helpers ────────────────────────────────────────────────────────
26
+ /** Returns true when at least one of the repo-relative paths exists on disk. */
27
+ function anyPathExists(repoRoot, relativePaths) {
28
+ return relativePaths.some((p) => fs.existsSync(path.join(repoRoot, p)));
29
+ }
30
+ const CAPABILITIES = [
31
+ // ── Web App ───────────────────────────────────────────────────────────────
32
+ {
33
+ name: 'Web App',
34
+ fsPaths: [
35
+ 'apps/web',
36
+ 'next.config.ts',
37
+ 'next.config.js',
38
+ 'next.config.mjs',
39
+ 'next.config.cjs',
40
+ ],
41
+ depPrefixes: ['next'],
42
+ contradictoryPatterns: [
43
+ // "do not add web app" / "don't add web app"
44
+ /\b(?:do\s+not|don'?t)\s+add\s+(?:a\s+)?web\s+app\b/i,
45
+ // "no web app exists yet" / "no web app yet"
46
+ /\bno\s+web\s+app\s+(?:exists?\s+)?yet\b/i,
47
+ // "no web app" as a standalone factual claim (not "no web app logic" etc.)
48
+ /\bno\s+web\s+app\b(?!\s+(?:logic|code|component|route|page|specific))/i,
49
+ // "web app pending" / "web app not yet built"
50
+ /\bweb\s+app\s+(?:pending|not\s+(?:yet\s+)?(?:built|implemented|created))\b/i,
51
+ ],
52
+ severity: 'high',
53
+ title: 'Possible stale boundary: instruction appears to deny a web app that exists',
54
+ evidenceLabel: 'web app (apps/web or next.config.*)',
55
+ recommendation: 'Update the instruction file to reflect that the web app is now present. ' +
56
+ 'Replace the stale non-goal statement with current guidance about the web app.',
57
+ },
58
+ // ── Supabase ──────────────────────────────────────────────────────────────
59
+ {
60
+ name: 'Supabase',
61
+ fsPaths: ['supabase/migrations', 'supabase'],
62
+ depPrefixes: ['@supabase/'],
63
+ contradictoryPatterns: [
64
+ /\bno\s+supabase\b/i,
65
+ /\bsupabase\s+(?:pending|not\s+(?:yet\s+)?(?:set\s+up|configured|added|integrated))\b/i,
66
+ /\bno\s+(?:supabase\s+)?(?:database|db)\s+yet\b/i,
67
+ ],
68
+ severity: 'high',
69
+ title: 'Possible stale boundary: instruction appears to deny Supabase that is configured',
70
+ evidenceLabel: 'Supabase (supabase/ directory or @supabase/* dependency)',
71
+ recommendation: 'Update the instruction file to reflect that Supabase is now configured. ' +
72
+ 'Replace the stale non-goal statement with current guidance about the database.',
73
+ },
74
+ // ── Database ──────────────────────────────────────────────────────────────
75
+ {
76
+ name: 'Database',
77
+ fsPaths: ['supabase/migrations', 'drizzle', 'prisma', 'prisma/schema.prisma'],
78
+ depPrefixes: ['drizzle-orm', 'prisma', '@prisma/'],
79
+ contradictoryPatterns: [
80
+ /\bno\s+database\b/i,
81
+ /\bdatabase\s+pending\b/i,
82
+ /\bno\s+db\b/i,
83
+ /\bwithout\s+a\s+database\b/i,
84
+ ],
85
+ severity: 'high',
86
+ title: 'Possible stale boundary: instruction appears to deny a database that exists',
87
+ evidenceLabel: 'database schema/migrations (supabase/migrations, drizzle, or prisma)',
88
+ recommendation: 'Update the instruction file to reflect the current database setup. ' +
89
+ 'Replace the stale non-goal statement with current guidance about data access.',
90
+ },
91
+ // ── Dashboard ─────────────────────────────────────────────────────────────
92
+ {
93
+ name: 'Dashboard',
94
+ fsPaths: [
95
+ 'apps/web/src/app/dashboard',
96
+ 'apps/web/src/app/(dashboard)',
97
+ 'apps/web/src/app/api/upload/route.ts',
98
+ 'src/app/dashboard',
99
+ 'src/app/(dashboard)',
100
+ 'app/dashboard',
101
+ 'app/(dashboard)',
102
+ 'src/pages/dashboard',
103
+ 'pages/dashboard',
104
+ ],
105
+ contradictoryPatterns: [
106
+ /\bdashboard\s+(?:pending|not\s+(?:yet\s+)?(?:built|implemented|created|started))\b/i,
107
+ /\bno\s+dashboard\b/i,
108
+ /\bdashboard\s+does\s+not\s+exist\b/i,
109
+ ],
110
+ severity: 'warning',
111
+ title: 'Possible stale boundary: instruction appears to deny a dashboard that exists',
112
+ evidenceLabel: 'dashboard routes (apps/web/src/app/dashboard or upload API route)',
113
+ recommendation: 'Update the instruction file to reflect that the dashboard is now implemented.',
114
+ },
115
+ // ── Upload API ────────────────────────────────────────────────────────────
116
+ {
117
+ name: 'Upload API',
118
+ fsPaths: [
119
+ 'apps/web/src/app/api/upload/route.ts',
120
+ 'apps/web/src/app/api/upload',
121
+ 'src/app/api/upload/route.ts',
122
+ 'src/app/api/upload',
123
+ 'app/api/upload/route.ts',
124
+ 'app/api/upload',
125
+ 'pages/api/upload.ts',
126
+ ],
127
+ contradictoryPatterns: [
128
+ /\bupload\s+not\s+(?:yet\s+)?implemented\b/i,
129
+ /\bno\s+upload\s+(?:route|api|endpoint)\b/i,
130
+ /\bupload\s+(?:api|route|endpoint)\s+(?:pending|does\s+not\s+exist)\b/i,
131
+ ],
132
+ severity: 'warning',
133
+ title: 'Possible stale boundary: instruction appears to deny an upload API that exists',
134
+ evidenceLabel: 'upload API route (apps/web/src/app/api/upload/route.ts)',
135
+ recommendation: 'Update the instruction file to reflect that the upload API is implemented. ' +
136
+ 'Document what data is uploaded and any relevant security guidance.',
137
+ },
138
+ // ── Playwright / E2E ──────────────────────────────────────────────────────
139
+ {
140
+ name: 'Playwright/E2E',
141
+ fsPaths: [
142
+ 'playwright.config.ts',
143
+ 'playwright.config.js',
144
+ 'playwright.config.mjs',
145
+ 'playwright.config.cjs',
146
+ ],
147
+ depPrefixes: ['@playwright/test'],
148
+ contradictoryPatterns: [
149
+ /\bno\s+(?:e2e|end.to.end)\s+tests?\b/i,
150
+ /\bno\s+playwright\b/i,
151
+ /\bplaywright\s+(?:pending|not\s+(?:yet\s+)?(?:set\s+up|configured|added))\b/i,
152
+ ],
153
+ severity: 'warning',
154
+ title: 'Possible stale boundary: instruction appears to deny E2E tests that are configured',
155
+ evidenceLabel: 'Playwright config (playwright.config.*)',
156
+ recommendation: 'Update the instruction file to include E2E testing guidance. ' +
157
+ 'Document how to run Playwright tests and any relevant setup steps.',
158
+ },
159
+ // ── GitHub OAuth ──────────────────────────────────────────────────────────
160
+ {
161
+ name: 'GitHub OAuth',
162
+ fsPaths: [
163
+ 'apps/web/src/app/auth',
164
+ 'apps/web/src/app/api/auth',
165
+ 'apps/web/src/app/(auth)',
166
+ 'src/app/auth',
167
+ 'src/app/api/auth',
168
+ 'src/app/(auth)',
169
+ 'app/auth',
170
+ 'app/api/auth',
171
+ 'app/(auth)',
172
+ 'pages/api/auth',
173
+ ],
174
+ contradictoryPatterns: [
175
+ // PB1: exclude scoped prose like "no auth required for public
176
+ // endpoints" — that's a SCOPING statement about a specific route, not
177
+ // a claim that auth doesn't exist anywhere in the project. Without
178
+ // this exclusion, any repo with auth routes AND a scoped "no auth
179
+ // required" note anywhere would always get flagged.
180
+ /\bno\s+(?:github\s+)?(?:oauth|auth(?:entication)?)\b(?!\s+(?:is\s+)?(?:required|needed|necessary)\b)/i,
181
+ /\bauth(?:entication)?\s+(?:pending|not\s+(?:yet\s+)?(?:built|implemented|added))\b/i,
182
+ /\bno\s+login\b(?!\s+(?:is\s+)?(?:required|needed|necessary)\b)/i,
183
+ ],
184
+ severity: 'warning',
185
+ title: 'Possible stale boundary: instruction appears to deny auth that is implemented',
186
+ evidenceLabel: 'auth routes (apps/web/src/app/auth or apps/web/src/app/api/auth)',
187
+ recommendation: 'Update the instruction file to reflect that authentication is now implemented.',
188
+ },
189
+ ];
190
+ // ── Capability presence check ─────────────────────────────────────────────────
191
+ /**
192
+ * Returns true when the repo shows strong evidence of the capability, either
193
+ * via filesystem paths or via package.json dependencies.
194
+ */
195
+ function capabilityIsPresent(cap, context) {
196
+ if (anyPathExists(context.repoRoot, cap.fsPaths))
197
+ return true;
198
+ if (cap.depPrefixes && cap.depPrefixes.length > 0) {
199
+ const allDeps = {
200
+ ...context.packageJson.dependencies,
201
+ ...context.packageJson.devDependencies,
202
+ };
203
+ return cap.depPrefixes.some((prefix) => Object.keys(allDeps).some((dep) => dep === prefix || dep.startsWith(prefix)));
204
+ }
205
+ return false;
206
+ }
207
+ // ── First-match evidence extraction ──────────────────────────────────────────
208
+ /**
209
+ * PB2: strips fenced code blocks (``` or ~~~, matching fence char/length per
210
+ * CommonMark) and inline `code spans` before contradiction matching. Without
211
+ * this, a fenced example that QUOTES a contradictory phrase (e.g.
212
+ * documenting the OLD wording of a non-goal statement as a "before" example)
213
+ * was read as a live claim and flagged — the same class of bug fixed for
214
+ * vague-guidance.ts, stale-instructions.ts, and conflicts.ts.
215
+ */
216
+ function stripCodeBlocks(text) {
217
+ const lines = text.split('\n');
218
+ const kept = [];
219
+ let inBlock = false;
220
+ let fenceChar = null;
221
+ let fenceLen = 0;
222
+ for (const line of lines) {
223
+ if (!inBlock) {
224
+ const openMatch = /^ {0,3}([`~]{3,})/.exec(line);
225
+ if (openMatch) {
226
+ inBlock = true;
227
+ fenceChar = openMatch[1][0] ?? null;
228
+ fenceLen = openMatch[1].length;
229
+ continue;
230
+ }
231
+ kept.push(line);
232
+ }
233
+ else {
234
+ const closeMatch = /^ {0,3}([`~]{3,})\s*$/.exec(line);
235
+ if (closeMatch && closeMatch[1][0] === fenceChar && closeMatch[1].length >= fenceLen) {
236
+ inBlock = false;
237
+ fenceChar = null;
238
+ fenceLen = 0;
239
+ }
240
+ // else: still inside the fence — drop the line
241
+ }
242
+ }
243
+ return kept.join('\n').replace(/`[^`\n]+`/g, '');
244
+ }
245
+ /**
246
+ * Finds the first contradictory phrase in `content` and returns the matched
247
+ * text (the actual instruction wording), or undefined if none match.
248
+ */
249
+ function findFirstContradiction(content, patterns) {
250
+ for (const pattern of patterns) {
251
+ // Use a fresh non-global copy to find the first match position
252
+ const re = new RegExp(pattern.source, pattern.flags.replace('g', ''));
253
+ const m = re.exec(content);
254
+ if (m)
255
+ return { pattern, matched: m[0] };
256
+ }
257
+ return undefined;
258
+ }
259
+ // ── Public detector ───────────────────────────────────────────────────────────
260
+ /**
261
+ * Detects stale product-boundary instructions that contradict filesystem
262
+ * evidence of a feature already existing in the repo.
263
+ *
264
+ * One finding is emitted per (capability × file) regardless of how many
265
+ * contradictory patterns match in that file, to keep the report actionable.
266
+ */
267
+ export function detectProductBoundary(context) {
268
+ const issues = [];
269
+ const { repoRoot, files } = context;
270
+ for (const cap of CAPABILITIES) {
271
+ if (!capabilityIsPresent(cap, context))
272
+ continue;
273
+ // Collect all existing evidence paths for the finding
274
+ const existingPaths = cap.fsPaths.filter((p) => fs.existsSync(path.join(repoRoot, p)));
275
+ const evidencePaths = existingPaths.length > 0 ? existingPaths : [cap.evidenceLabel];
276
+ for (const file of files) {
277
+ const contradiction = findFirstContradiction(stripCodeBlocks(file.content), cap.contradictoryPatterns);
278
+ if (!contradiction)
279
+ continue;
280
+ issues.push({
281
+ id: issueId(`${cap.name}:${file.path}`),
282
+ severity: cap.severity,
283
+ category: 'stale_instruction',
284
+ title: cap.title,
285
+ summary: `The instruction file appears to contain a stale product-boundary statement ` +
286
+ `that contradicts ${cap.evidenceLabel} found in the repository. ` +
287
+ `The phrase "${contradiction.matched}" may no longer reflect the current state of the project.`,
288
+ filePaths: [file.path],
289
+ locations: [{ filePath: file.path }],
290
+ evidence: [
291
+ `Contradictory phrase found: "${contradiction.matched}"`,
292
+ `Repository evidence: ${evidencePaths.join(', ')}`,
293
+ ],
294
+ recommendation: cap.recommendation,
295
+ confidence: 0.85,
296
+ });
297
+ }
298
+ }
299
+ return issues;
300
+ }
301
+ //# sourceMappingURL=product-boundary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"product-boundary.js","sourceRoot":"","sources":["../src/product-boundary.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { InstructionFile, ProjectType } from './types.js';
2
+ /**
3
+ * Detect the primary project type for a repo at repoRoot.
4
+ * Detection order: nextjs → typescript → unity → dotnet → unknown
5
+ */
6
+ export declare function detectProjectType(repoRoot: string): Promise<ProjectType>;
7
+ /**
8
+ * Content-based project type detection — used as a fallback when the file-system
9
+ * scan returns 'unknown' (e.g. repos that only contain instruction files, no source).
10
+ * Checks the combined content of all scanned instruction files for strong signals.
11
+ *
12
+ * Detection order: nextjs → unity → typescript → dotnet → python → go → rust → unknown
13
+ */
14
+ export declare function detectProjectTypeFromContent(files: InstructionFile[]): ProjectType;
15
+ //# sourceMappingURL=project-type.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,116 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import fg from 'fast-glob';
4
+ async function exists(p) {
5
+ try {
6
+ await fs.access(p);
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ async function dirExists(p) {
14
+ try {
15
+ const stat = await fs.stat(p);
16
+ return stat.isDirectory();
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ /**
23
+ * Detect the primary project type for a repo at repoRoot.
24
+ * Detection order: nextjs → typescript → unity → dotnet → unknown
25
+ */
26
+ export async function detectProjectType(repoRoot) {
27
+ const root = path.resolve(repoRoot);
28
+ // ── Next.js ──────────────────────────────────────────────────────────────
29
+ // next.config.* exists OR (app/ or pages/ alongside package.json)
30
+ const nextConfigGlob = await fg('next.config.*', {
31
+ cwd: root,
32
+ dot: false,
33
+ followSymbolicLinks: false,
34
+ deep: 1,
35
+ });
36
+ if (nextConfigGlob.length > 0)
37
+ return 'nextjs';
38
+ const hasPkgJson = await exists(path.join(root, 'package.json'));
39
+ if (hasPkgJson) {
40
+ const hasApp = await dirExists(path.join(root, 'app'));
41
+ const hasPages = await dirExists(path.join(root, 'pages'));
42
+ if (hasApp || hasPages)
43
+ return 'nextjs';
44
+ }
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
+ // ── Unity ────────────────────────────────────────────────────────────────
51
+ // Assets/ directory AND ProjectSettings/ProjectVersion.txt both exist
52
+ const hasAssets = await dirExists(path.join(root, 'Assets'));
53
+ const hasProjectVersion = await exists(path.join(root, 'ProjectSettings', 'ProjectVersion.txt'));
54
+ if (hasAssets && hasProjectVersion)
55
+ return 'unity';
56
+ // ── .NET ─────────────────────────────────────────────────────────────────
57
+ // any .sln or .csproj file at repo root level
58
+ const dotnetFiles = await fg(['*.sln', '*.csproj'], {
59
+ cwd: root,
60
+ dot: false,
61
+ followSymbolicLinks: false,
62
+ deep: 1,
63
+ });
64
+ if (dotnetFiles.length > 0)
65
+ return 'dotnet';
66
+ // ── Python ───────────────────────────────────────────────────────────────
67
+ const hasPyProject = await exists(path.join(root, 'pyproject.toml'));
68
+ const hasRequirements = await exists(path.join(root, 'requirements.txt'));
69
+ const hasPipfile = await exists(path.join(root, 'Pipfile'));
70
+ const hasConda = await exists(path.join(root, 'environment.yml'));
71
+ const hasSetupPy = await exists(path.join(root, 'setup.py'));
72
+ if (hasPyProject || hasRequirements || hasPipfile || hasConda || hasSetupPy)
73
+ return 'python';
74
+ // ── Go ───────────────────────────────────────────────────────────────────
75
+ const hasGoMod = await exists(path.join(root, 'go.mod'));
76
+ if (hasGoMod)
77
+ return 'go';
78
+ // ── Rust ─────────────────────────────────────────────────────────────────
79
+ const hasCargoToml = await exists(path.join(root, 'Cargo.toml'));
80
+ if (hasCargoToml)
81
+ return 'rust';
82
+ return 'unknown';
83
+ }
84
+ /**
85
+ * Content-based project type detection — used as a fallback when the file-system
86
+ * scan returns 'unknown' (e.g. repos that only contain instruction files, no source).
87
+ * Checks the combined content of all scanned instruction files for strong signals.
88
+ *
89
+ * Detection order: nextjs → unity → typescript → dotnet → python → go → rust → unknown
90
+ */
91
+ export function detectProjectTypeFromContent(files) {
92
+ const combined = files.map((f) => f.content).join('\n');
93
+ if (/\bnext\.?js\b|\bapp\s+router\b|\bpages?\s+router\b|\bnext\s+build\b|\bnext\s+dev\b/i.test(combined)) {
94
+ return 'nextjs';
95
+ }
96
+ 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
+ return 'unity';
98
+ }
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
+ if (/\bdotnet\b|\.sln\b|\.csproj\b|\bxunit\b|\bnunit\b|\bmstest\b/i.test(combined)) {
103
+ return 'dotnet';
104
+ }
105
+ if (/\bpython\b|\bpip\b|\bpoetry\b|\bvenv\b|\bpytest\b|\bpyproject\.toml\b/i.test(combined)) {
106
+ return 'python';
107
+ }
108
+ if (/\bgo\.mod\b|\bgo\s+(build|test|run|get|mod)\b|\bgolang\b/i.test(combined)) {
109
+ return 'go';
110
+ }
111
+ if (/\bcargo\.toml\b|\bcargo\s+(build|test|run|clippy|fmt)\b|\brustc\b/i.test(combined)) {
112
+ return 'rust';
113
+ }
114
+ return 'unknown';
115
+ }
116
+ //# sourceMappingURL=project-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-type.js","sourceRoot":"","sources":["../src/project-type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,MAAM,WAAW,CAAC;AAG3B,KAAK,UAAU,MAAM,CAAC,CAAS;IAC7B,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,CAAS;IAChC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IACtD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEpC,4EAA4E;IAC5E,kEAAkE;IAClE,MAAM,cAAc,GAAG,MAAM,EAAE,CAAC,eAAe,EAAE;QAC/C,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,KAAK;QACV,mBAAmB,EAAE,KAAK;QAC1B,IAAI,EAAE,CAAC;KACR,CAAC,CAAC;IACH,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE/C,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IACjE,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,IAAI,MAAM,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;IAC1C,CAAC;IAED,4EAA4E;IAC5E,wCAAwC;IACxC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC;IACnE,IAAI,UAAU,IAAI,WAAW;QAAE,OAAO,YAAY,CAAC;IAEnD,4EAA4E;IAC5E,sEAAsE;IACtE,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,iBAAiB,GAAG,MAAM,MAAM,CACpC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,EAAE,oBAAoB,CAAC,CACzD,CAAC;IACF,IAAI,SAAS,IAAI,iBAAiB;QAAE,OAAO,OAAO,CAAC;IAEnD,4EAA4E;IAC5E,8CAA8C;IAC9C,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE;QAClD,GAAG,EAAE,IAAI;QACT,GAAG,EAAE,KAAK;QACV,mBAAmB,EAAE,KAAK;QAC1B,IAAI,EAAE,CAAC;KACR,CAAC,CAAC;IACH,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAE5C,4EAA4E;IAC5E,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACrE,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAC1E,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7D,IAAI,YAAY,IAAI,eAAe,IAAI,UAAU,IAAI,QAAQ,IAAI,UAAU;QAAE,OAAO,QAAQ,CAAC;IAE7F,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzD,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE1B,4EAA4E;IAC5E,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;IACjE,IAAI,YAAY;QAAE,OAAO,MAAM,CAAC;IAEhC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAAC,KAAwB;IACnE,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAExD,IAAI,qFAAqF,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzG,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,8BAA8B,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,mFAAmF,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzL,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,iFAAiF,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACrG,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IAAI,+DAA+D,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnF,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,wEAAwE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5F,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,2DAA2D,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,oEAAoE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { InstructionFile, PromptCiIssue } from './types.js';
2
+ export declare function detectPromptCacheFriendliness(files: InstructionFile[]): PromptCiIssue[];
3
+ //# sourceMappingURL=prompt-cache.d.ts.map
@@ -0,0 +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"}