@promptci/core 0.0.1 → 0.2.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 (253) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +29 -30
  3. package/dist/action-pinning.d.ts +37 -0
  4. package/dist/action-pinning.d.ts.map +1 -0
  5. package/dist/action-pinning.js +277 -0
  6. package/dist/action-pinning.js.map +1 -0
  7. package/dist/agent-practices.d.ts +3 -1
  8. package/dist/agent-practices.d.ts.map +1 -1
  9. package/dist/agent-practices.js +198 -94
  10. package/dist/agent-practices.js.map +1 -1
  11. package/dist/ai-config.d.ts +178 -0
  12. package/dist/ai-config.d.ts.map +1 -0
  13. package/dist/ai-config.js +721 -0
  14. package/dist/ai-config.js.map +1 -0
  15. package/dist/alias-files.d.ts +33 -0
  16. package/dist/alias-files.d.ts.map +1 -0
  17. package/dist/alias-files.js +60 -0
  18. package/dist/alias-files.js.map +1 -0
  19. package/dist/baseline.d.ts +17 -1
  20. package/dist/baseline.d.ts.map +1 -1
  21. package/dist/baseline.js +5 -1
  22. package/dist/baseline.js.map +1 -1
  23. package/dist/buried-critical.d.ts +17 -0
  24. package/dist/buried-critical.d.ts.map +1 -0
  25. package/dist/buried-critical.js +107 -0
  26. package/dist/buried-critical.js.map +1 -0
  27. package/dist/canonical-owner.d.ts.map +1 -1
  28. package/dist/canonical-owner.js +60 -30
  29. package/dist/canonical-owner.js.map +1 -1
  30. package/dist/ci-alignment.d.ts.map +1 -1
  31. package/dist/ci-alignment.js +3 -2
  32. package/dist/ci-alignment.js.map +1 -1
  33. package/dist/command-validity.d.ts +16 -0
  34. package/dist/command-validity.d.ts.map +1 -1
  35. package/dist/command-validity.js +387 -134
  36. package/dist/command-validity.js.map +1 -1
  37. package/dist/conflicts.d.ts.map +1 -1
  38. package/dist/conflicts.js +4 -44
  39. package/dist/conflicts.js.map +1 -1
  40. package/dist/context-analysis.d.ts.map +1 -1
  41. package/dist/context-analysis.js +13 -2
  42. package/dist/context-analysis.js.map +1 -1
  43. package/dist/context-bloat.d.ts +20 -1
  44. package/dist/context-bloat.d.ts.map +1 -1
  45. package/dist/context-bloat.js +99 -15
  46. package/dist/context-bloat.js.map +1 -1
  47. package/dist/context-optimizer.d.ts.map +1 -1
  48. package/dist/context-optimizer.js +18 -54
  49. package/dist/context-optimizer.js.map +1 -1
  50. package/dist/copilot-instructions-detector.d.ts +24 -0
  51. package/dist/copilot-instructions-detector.d.ts.map +1 -0
  52. package/dist/copilot-instructions-detector.js +145 -0
  53. package/dist/copilot-instructions-detector.js.map +1 -0
  54. package/dist/cursor-rules-detector.d.ts +17 -0
  55. package/dist/cursor-rules-detector.d.ts.map +1 -0
  56. package/dist/cursor-rules-detector.js +110 -0
  57. package/dist/cursor-rules-detector.js.map +1 -0
  58. package/dist/custom-rules.d.ts +52 -0
  59. package/dist/custom-rules.d.ts.map +1 -0
  60. package/dist/custom-rules.js +303 -0
  61. package/dist/custom-rules.js.map +1 -0
  62. package/dist/dead-references.d.ts +8 -2
  63. package/dist/dead-references.d.ts.map +1 -1
  64. package/dist/dead-references.js +315 -68
  65. package/dist/dead-references.js.map +1 -1
  66. package/dist/dependency-staleness.d.ts +35 -0
  67. package/dist/dependency-staleness.d.ts.map +1 -0
  68. package/dist/dependency-staleness.js +1001 -0
  69. package/dist/dependency-staleness.js.map +1 -0
  70. package/dist/detectors.d.ts +1 -3
  71. package/dist/detectors.d.ts.map +1 -1
  72. package/dist/detectors.js +74 -56
  73. package/dist/detectors.js.map +1 -1
  74. package/dist/duplicates.d.ts.map +1 -1
  75. package/dist/duplicates.js +15 -9
  76. package/dist/duplicates.js.map +1 -1
  77. package/dist/evidence.d.ts +39 -0
  78. package/dist/evidence.d.ts.map +1 -0
  79. package/dist/evidence.js +78 -0
  80. package/dist/evidence.js.map +1 -0
  81. package/dist/finding-id.d.ts +21 -0
  82. package/dist/finding-id.d.ts.map +1 -0
  83. package/dist/finding-id.js +31 -0
  84. package/dist/finding-id.js.map +1 -0
  85. package/dist/fix-engine.d.ts +11 -0
  86. package/dist/fix-engine.d.ts.map +1 -1
  87. package/dist/fix-engine.js +88 -71
  88. package/dist/fix-engine.js.map +1 -1
  89. package/dist/frameworks/dotnet.js +2 -2
  90. package/dist/frameworks/dotnet.js.map +1 -1
  91. package/dist/frameworks/python.d.ts.map +1 -1
  92. package/dist/frameworks/python.js +5 -2
  93. package/dist/frameworks/python.js.map +1 -1
  94. package/dist/frameworks/rust.d.ts.map +1 -1
  95. package/dist/frameworks/rust.js +2 -1
  96. package/dist/frameworks/rust.js.map +1 -1
  97. package/dist/git-symlinks.d.ts +30 -0
  98. package/dist/git-symlinks.d.ts.map +1 -0
  99. package/dist/git-symlinks.js +161 -0
  100. package/dist/git-symlinks.js.map +1 -0
  101. package/dist/gitignore.d.ts +22 -0
  102. package/dist/gitignore.d.ts.map +1 -0
  103. package/dist/gitignore.js +157 -0
  104. package/dist/gitignore.js.map +1 -0
  105. package/dist/health-score.d.ts.map +1 -1
  106. package/dist/health-score.js +35 -5
  107. package/dist/health-score.js.map +1 -1
  108. package/dist/hooks-settings-detector.d.ts +17 -0
  109. package/dist/hooks-settings-detector.d.ts.map +1 -0
  110. package/dist/hooks-settings-detector.js +228 -0
  111. package/dist/hooks-settings-detector.js.map +1 -0
  112. package/dist/index.d.ts +29 -8
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +21 -6
  115. package/dist/index.js.map +1 -1
  116. package/dist/makefile.d.ts +37 -0
  117. package/dist/makefile.d.ts.map +1 -0
  118. package/dist/makefile.js +156 -0
  119. package/dist/makefile.js.map +1 -0
  120. package/dist/manifest-consistency.d.ts +6 -0
  121. package/dist/manifest-consistency.d.ts.map +1 -1
  122. package/dist/manifest-consistency.js +75 -10
  123. package/dist/manifest-consistency.js.map +1 -1
  124. package/dist/markdown-fences.d.ts +112 -0
  125. package/dist/markdown-fences.d.ts.map +1 -0
  126. package/dist/markdown-fences.js +174 -0
  127. package/dist/markdown-fences.js.map +1 -0
  128. package/dist/mcp-config-detector.d.ts +20 -0
  129. package/dist/mcp-config-detector.d.ts.map +1 -0
  130. package/dist/mcp-config-detector.js +237 -0
  131. package/dist/mcp-config-detector.js.map +1 -0
  132. package/dist/missing-context.d.ts.map +1 -1
  133. package/dist/missing-context.js +2 -52
  134. package/dist/missing-context.js.map +1 -1
  135. package/dist/model-budgets.d.ts +69 -0
  136. package/dist/model-budgets.d.ts.map +1 -0
  137. package/dist/model-budgets.js +110 -0
  138. package/dist/model-budgets.js.map +1 -0
  139. package/dist/negative-instructions.d.ts +26 -0
  140. package/dist/negative-instructions.d.ts.map +1 -0
  141. package/dist/negative-instructions.js +169 -0
  142. package/dist/negative-instructions.js.map +1 -0
  143. package/dist/path-containment.d.ts +41 -0
  144. package/dist/path-containment.d.ts.map +1 -0
  145. package/dist/path-containment.js +123 -0
  146. package/dist/path-containment.js.map +1 -0
  147. package/dist/project-type.d.ts +12 -2
  148. package/dist/project-type.d.ts.map +1 -1
  149. package/dist/project-type.js +25 -12
  150. package/dist/project-type.js.map +1 -1
  151. package/dist/prompt-cache.d.ts.map +1 -1
  152. package/dist/prompt-cache.js +33 -24
  153. package/dist/prompt-cache.js.map +1 -1
  154. package/dist/promptci-gitignore.d.ts +34 -0
  155. package/dist/promptci-gitignore.d.ts.map +1 -0
  156. package/dist/promptci-gitignore.js +42 -0
  157. package/dist/promptci-gitignore.js.map +1 -0
  158. package/dist/repo-context.d.ts +80 -0
  159. package/dist/repo-context.d.ts.map +1 -1
  160. package/dist/repo-context.js +170 -16
  161. package/dist/repo-context.js.map +1 -1
  162. package/dist/report.d.ts +8 -0
  163. package/dist/report.d.ts.map +1 -1
  164. package/dist/report.js +163 -63
  165. package/dist/report.js.map +1 -1
  166. package/dist/scan.d.ts.map +1 -1
  167. package/dist/scan.js +47 -4
  168. package/dist/scan.js.map +1 -1
  169. package/dist/scanner.d.ts +61 -1
  170. package/dist/scanner.d.ts.map +1 -1
  171. package/dist/scanner.js +367 -70
  172. package/dist/scanner.js.map +1 -1
  173. package/dist/security-pack.d.ts +20 -0
  174. package/dist/security-pack.d.ts.map +1 -1
  175. package/dist/security-pack.js +33 -14
  176. package/dist/security-pack.js.map +1 -1
  177. package/dist/skill-supply-chain.d.ts +132 -0
  178. package/dist/skill-supply-chain.d.ts.map +1 -0
  179. package/dist/skill-supply-chain.js +2954 -0
  180. package/dist/skill-supply-chain.js.map +1 -0
  181. package/dist/skills-detector.d.ts +23 -0
  182. package/dist/skills-detector.d.ts.map +1 -0
  183. package/dist/skills-detector.js +279 -0
  184. package/dist/skills-detector.js.map +1 -0
  185. package/dist/stale-instructions.d.ts +4 -1
  186. package/dist/stale-instructions.d.ts.map +1 -1
  187. package/dist/stale-instructions.js +53 -55
  188. package/dist/stale-instructions.js.map +1 -1
  189. package/dist/subagents-detector.d.ts +19 -0
  190. package/dist/subagents-detector.d.ts.map +1 -0
  191. package/dist/subagents-detector.js +155 -0
  192. package/dist/subagents-detector.js.map +1 -0
  193. package/dist/suppression.d.ts +2 -0
  194. package/dist/suppression.d.ts.map +1 -1
  195. package/dist/suppression.js +16 -47
  196. package/dist/suppression.js.map +1 -1
  197. package/dist/types.d.ts +62 -4
  198. package/dist/types.d.ts.map +1 -1
  199. package/dist/types.js +47 -1
  200. package/dist/types.js.map +1 -1
  201. package/dist/vague-guidance.d.ts +15 -2
  202. package/dist/vague-guidance.d.ts.map +1 -1
  203. package/dist/vague-guidance.js +44 -41
  204. package/dist/vague-guidance.js.map +1 -1
  205. package/dist/windsurf-detector.d.ts +22 -0
  206. package/dist/windsurf-detector.d.ts.map +1 -0
  207. package/dist/windsurf-detector.js +96 -0
  208. package/dist/windsurf-detector.js.map +1 -0
  209. package/dist/within-section-dedup.d.ts +16 -0
  210. package/dist/within-section-dedup.d.ts.map +1 -0
  211. package/dist/within-section-dedup.js +148 -0
  212. package/dist/within-section-dedup.js.map +1 -0
  213. package/dist/workspace-manifests.d.ts +80 -0
  214. package/dist/workspace-manifests.d.ts.map +1 -0
  215. package/dist/workspace-manifests.js +333 -0
  216. package/dist/workspace-manifests.js.map +1 -0
  217. package/package.json +11 -8
  218. package/dist/cheap-model-routing.d.ts +0 -4
  219. package/dist/cheap-model-routing.d.ts.map +0 -1
  220. package/dist/cheap-model-routing.js +0 -43
  221. package/dist/cheap-model-routing.js.map +0 -1
  222. package/dist/cli-output-filter.d.ts +0 -16
  223. package/dist/cli-output-filter.d.ts.map +0 -1
  224. package/dist/cli-output-filter.js +0 -88
  225. package/dist/cli-output-filter.js.map +0 -1
  226. package/dist/context-cost.d.ts +0 -4
  227. package/dist/context-cost.d.ts.map +0 -1
  228. package/dist/context-cost.js +0 -70
  229. package/dist/context-cost.js.map +0 -1
  230. package/dist/context-recommender.d.ts +0 -3
  231. package/dist/context-recommender.d.ts.map +0 -1
  232. package/dist/context-recommender.js +0 -329
  233. package/dist/context-recommender.js.map +0 -1
  234. package/dist/llm.d.ts +0 -6
  235. package/dist/llm.d.ts.map +0 -1
  236. package/dist/llm.js +0 -67
  237. package/dist/llm.js.map +0 -1
  238. package/dist/mcp-detector.d.ts +0 -4
  239. package/dist/mcp-detector.d.ts.map +0 -1
  240. package/dist/mcp-detector.js +0 -131
  241. package/dist/mcp-detector.js.map +0 -1
  242. package/dist/native-tool-preference.d.ts +0 -20
  243. package/dist/native-tool-preference.d.ts.map +0 -1
  244. package/dist/native-tool-preference.js +0 -94
  245. package/dist/native-tool-preference.js.map +0 -1
  246. package/dist/output-verbosity.d.ts +0 -3
  247. package/dist/output-verbosity.d.ts.map +0 -1
  248. package/dist/output-verbosity.js +0 -83
  249. package/dist/output-verbosity.js.map +0 -1
  250. package/dist/product-boundary.d.ts +0 -11
  251. package/dist/product-boundary.d.ts.map +0 -1
  252. package/dist/product-boundary.js +0 -301
  253. package/dist/product-boundary.js.map +0 -1
@@ -0,0 +1,277 @@
1
+ import * as crypto from 'node:crypto';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * GitHub Actions pinning detector (detect-only).
5
+ *
6
+ * Flags `uses:` references in `.github/workflows/*.yml` and in composite
7
+ * actions (`action.yml` at the repo root or under `.github/actions/`) that
8
+ * point at a tag or branch instead of a full commit SHA. A tag is a mutable
9
+ * pointer: whoever can push to the action's repository can move it, and every
10
+ * workflow referencing it runs the new code on its next run. A SHA cannot be
11
+ * repointed.
12
+ *
13
+ * Deliberately DETECT-ONLY: there is no fix recipe and `autoApplySafe` is never
14
+ * set. Resolving a tag to its SHA needs a network call, and this package makes
15
+ * none from detector code — pinning is left to the user (or Dependabot /
16
+ * Renovate, which keep a pinned SHA and its version comment in step).
17
+ *
18
+ * Exempt: local actions (`./path`) and container actions (`docker://…`), which
19
+ * have no git ref to pin. These files are read from `workflows.sources`, never
20
+ * from `context.files`, so they are never treated as instruction files.
21
+ *
22
+ * Deterministic and offline: line-oriented parsing of the YAML text, no clock,
23
+ * no network.
24
+ */
25
+ /** A full git commit id: 40-hex SHA-1, or 64-hex for SHA-256 repositories. */
26
+ const FULL_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
27
+ /** `owner/repo` or `owner/repo/sub/path` — the only action shape shown verbatim. */
28
+ const ACTION_RE = /^[\w.-]+\/[\w.-]+(?:\/[\w./-]+)?$/;
29
+ /** A plausible tag/branch name — the only version shape shown verbatim. */
30
+ const VERSION_RE = /^[\w./+-]+$/;
31
+ /**
32
+ * Owners whose actions GitHub itself maintains. An unpinned first-party ref is
33
+ * still a mutable pointer, but the account holding it is GitHub's own, so it
34
+ * is reported one severity lower than a third-party ref.
35
+ */
36
+ const FIRST_PARTY_OWNERS = new Set(['actions', 'github']);
37
+ /** A `key: value` mapping line, optionally the first key of a list item. */
38
+ const KEY_LINE_RE = /^(\s*)(-\s+)?([\w.-]+)\s*:(?:\s+(.*))?$/;
39
+ /** A list item whose value is a flow mapping: `- { uses: x@v1, with: {...} }`. */
40
+ const FLOW_ITEM_RE = /^\s*-\s+(\{.*)$/;
41
+ /** A block scalar indicator (`|`, `>-`, `|2+`), optionally followed by a comment. */
42
+ const BLOCK_SCALAR_RE = /^[|>][+-]?\d*\s*(?:#.*)?$/;
43
+ /**
44
+ * Keys whose block-mapping values are free-form data, not steps. A `uses:` key
45
+ * nested under them (e.g. an input literally named `uses`) is not a step.
46
+ */
47
+ const DATA_MAPPING_KEYS = new Set(['with', 'env', 'secrets', 'inputs', 'outputs']);
48
+ /** Strip a trailing ` # comment` and surrounding quotes from a scalar value. */
49
+ function scalarValue(raw) {
50
+ let value = raw.trim();
51
+ const quote = value[0];
52
+ if (quote === '"' || quote === "'") {
53
+ const end = value.indexOf(quote, 1);
54
+ return end > 0 ? value.slice(1, end) : value.slice(1);
55
+ }
56
+ const comment = value.search(/(?:^|\s)#/);
57
+ if (comment >= 0)
58
+ value = value.slice(0, comment);
59
+ return value.trim();
60
+ }
61
+ /**
62
+ * The value of a top-level `uses` key in a single-line flow mapping, or
63
+ * undefined. Nested maps (`with: { uses: … }`) are depth > 1 and ignored.
64
+ */
65
+ function flowMappingUses(flow) {
66
+ const entries = [];
67
+ let depth = 0;
68
+ let quote = '';
69
+ let current = '';
70
+ for (const c of flow) {
71
+ if (quote) {
72
+ if (c === quote)
73
+ quote = '';
74
+ if (depth === 1)
75
+ current += c;
76
+ continue;
77
+ }
78
+ if (c === '"' || c === "'") {
79
+ quote = c;
80
+ if (depth === 1)
81
+ current += c;
82
+ continue;
83
+ }
84
+ if (c === '{' || c === '[') {
85
+ depth++;
86
+ if (depth > 1)
87
+ current += c;
88
+ continue;
89
+ }
90
+ if (c === '}' || c === ']') {
91
+ depth--;
92
+ if (depth === 0)
93
+ break;
94
+ current += c;
95
+ continue;
96
+ }
97
+ if (c === ',' && depth === 1) {
98
+ entries.push(current);
99
+ current = '';
100
+ continue;
101
+ }
102
+ if (depth >= 1)
103
+ current += c;
104
+ }
105
+ entries.push(current);
106
+ for (const entry of entries) {
107
+ const m = /^\s*uses\s*:\s*(.*)$/.exec(entry);
108
+ if (m)
109
+ return scalarValue(m[1]);
110
+ }
111
+ return undefined;
112
+ }
113
+ /**
114
+ * Every step/job-level `uses:` reference in a workflow or composite-action
115
+ * file, in line order. Skipped: comments, lines inside block scalars (a
116
+ * `run: |` script that prints `uses: foo@v1`), and `uses` keys nested under
117
+ * data mappings such as `with:`.
118
+ */
119
+ export function extractActionUses(content) {
120
+ const lines = content.split(/\r?\n/);
121
+ const uses = [];
122
+ // Lines indented deeper than this column belong to a block scalar or data
123
+ // mapping and are skipped; -1 = not inside one.
124
+ let skipDeeperThan = -1;
125
+ for (let i = 0; i < lines.length; i++) {
126
+ const line = lines[i];
127
+ if (line.trim() === '')
128
+ continue;
129
+ const indent = /^\s*/.exec(line)[0].length;
130
+ if (skipDeeperThan >= 0) {
131
+ if (indent > skipDeeperThan)
132
+ continue;
133
+ skipDeeperThan = -1;
134
+ }
135
+ if (/^\s*#/.test(line))
136
+ continue;
137
+ const flow = FLOW_ITEM_RE.exec(line);
138
+ if (flow) {
139
+ const ref = flowMappingUses(flow[1]);
140
+ if (ref)
141
+ uses.push({ ref, line: i + 1 });
142
+ continue;
143
+ }
144
+ const kv = KEY_LINE_RE.exec(line);
145
+ if (!kv)
146
+ continue;
147
+ const keyCol = kv[1].length + (kv[2]?.length ?? 0);
148
+ const key = kv[3];
149
+ const rawValue = kv[4] ?? '';
150
+ const value = rawValue.trim();
151
+ if (key === 'uses') {
152
+ const ref = scalarValue(rawValue);
153
+ if (ref)
154
+ uses.push({ ref, line: i + 1 });
155
+ continue;
156
+ }
157
+ if (BLOCK_SCALAR_RE.test(value)) {
158
+ skipDeeperThan = keyCol;
159
+ continue;
160
+ }
161
+ if ((value === '' || value.startsWith('#')) && DATA_MAPPING_KEYS.has(key)) {
162
+ skipDeeperThan = keyCol;
163
+ }
164
+ }
165
+ return uses;
166
+ }
167
+ /**
168
+ * Classify one `uses:` value. Returns the parts for a remote ref that should
169
+ * be pinned but is not, or `undefined` when it is pinned, local, a container,
170
+ * or an expression.
171
+ */
172
+ export function unpinnedUse(use) {
173
+ const { ref } = use;
174
+ if (ref.startsWith('./') || ref.startsWith('.\\'))
175
+ return undefined; // local action
176
+ if (ref.startsWith('docker://'))
177
+ return undefined; // container action
178
+ if (ref.includes('${{'))
179
+ return undefined; // expression — not statically resolvable
180
+ const at = ref.lastIndexOf('@');
181
+ if (at <= 0 || !ref.slice(0, at).includes('/'))
182
+ return undefined; // not a remote action ref
183
+ const action = ref.slice(0, at);
184
+ const version = ref.slice(at + 1);
185
+ if (FULL_SHA_RE.test(version))
186
+ return undefined;
187
+ const displayable = ACTION_RE.test(action) && VERSION_RE.test(version);
188
+ return {
189
+ ...use,
190
+ displayable,
191
+ action,
192
+ version,
193
+ owner: displayable ? action.split('/')[0].toLowerCase() : '',
194
+ };
195
+ }
196
+ function shortHash(value) {
197
+ return crypto.createHash('sha1').update(value).digest('hex').slice(0, 12);
198
+ }
199
+ /** Belt and braces for text that is shown inside Markdown code spans. */
200
+ function codeSafe(text) {
201
+ return text.replace(/`/g, "'");
202
+ }
203
+ function buildIssue(context, site) {
204
+ const { filePath, lines, use } = site;
205
+ const absPath = path.resolve(context.repoRoot, filePath);
206
+ const base = {
207
+ // One finding per (file, ref): pinning the ref in one workflow must not
208
+ // re-key the finding for the other files that still use it.
209
+ id: `action-pin-${shortHash(`action-pin:${filePath}:${use.ref}`)}`,
210
+ category: 'security',
211
+ // Absolute, like every other finding's paths; the JSON report relativizes.
212
+ filePaths: [absPath],
213
+ locations: lines.map((line) => ({ filePath: absPath, startLine: line, endLine: line })),
214
+ // The ref is certainly mutable; whether that matters for this repo is the
215
+ // heuristic part, so confidence stays below 1 and the score hit modest
216
+ // (and capped — see TAG_DEDUCTION_CAP in health-score.ts).
217
+ confidence: 0.8,
218
+ tags: ['supply-chain', 'github-actions'],
219
+ };
220
+ const why = "A tag or branch is a mutable pointer: whoever can push to the action's repository can move " +
221
+ "it, and the next run executes the new code with this repository's token and secrets.";
222
+ if (!use.displayable) {
223
+ // The raw value never reaches the title, evidence, or a shell command.
224
+ return {
225
+ ...base,
226
+ severity: 'warning',
227
+ title: 'GitHub Action reference is not pinned to a commit SHA',
228
+ summary: `${filePath} has a \`uses:\` reference (line ${lines.join(', ')}) that is not pinned to a full ` +
229
+ `commit SHA and does not look like a plain \`owner/repo@ref\` value. ${why}`,
230
+ // No line numbers (baseline fingerprints hash evidence); a hash of the
231
+ // raw value keeps two different refs in one file distinct.
232
+ evidence: [`${filePath}: unrecognised uses: value (sha1 ${shortHash(use.ref)})`],
233
+ recommendation: 'Review this `uses:` line by hand. Pin the action to the full 40-character commit SHA of a ' +
234
+ "release you trust (resolved from the action's own repository), keep the version in a trailing " +
235
+ 'comment, and let Dependabot or Renovate update both. PromptCI reports this but does not rewrite ' +
236
+ 'workflow files.',
237
+ };
238
+ }
239
+ const firstParty = FIRST_PARTY_OWNERS.has(use.owner);
240
+ const [repoOwner, repoName] = use.action.split('/');
241
+ const ref = codeSafe(use.ref);
242
+ return {
243
+ ...base,
244
+ severity: firstParty ? 'info' : 'warning',
245
+ title: `GitHub Action \`${ref}\` is not pinned to a commit SHA`,
246
+ summary: `${filePath} references \`${codeSafe(use.action)}\` at \`${codeSafe(use.version)}\`, which appears ` +
247
+ `to be a tag or branch rather than a full commit SHA. ${why} ` +
248
+ (firstParty
249
+ ? 'This action is maintained by GitHub, which lowers but does not remove the risk.'
250
+ : 'Consider pinning third-party actions to a reviewed commit.'),
251
+ evidence: [`${filePath}: uses: ${ref}`],
252
+ recommendation: 'Pin the action to the full commit SHA of the release you trust and keep the version in a ' +
253
+ `trailing comment, e.g. \`uses: ${codeSafe(use.action)}@<40-char-sha> # ${codeSafe(use.version)}\`. ` +
254
+ `Resolve the SHA from the action's own repository (\`gh api repos/${repoOwner}/${repoName}/commits/` +
255
+ `${use.version} --jq .sha\`), and let Dependabot or Renovate update the SHA and comment together. ` +
256
+ 'PromptCI reports this but does not rewrite workflow files.',
257
+ };
258
+ }
259
+ export function detectActionPinning(context) {
260
+ const sources = context.workflows.sources ?? [];
261
+ // Group occurrences by (file, ref): several jobs in one workflow using the
262
+ // same unpinned action are one fix, and one finding.
263
+ const sites = new Map();
264
+ for (const source of sources) {
265
+ for (const use of extractActionUses(source.content)) {
266
+ const unpinned = unpinnedUse(use);
267
+ if (!unpinned)
268
+ continue;
269
+ const key = `${source.filePath}\u0000${unpinned.ref}`;
270
+ const site = sites.get(key) ?? { filePath: source.filePath, lines: [], use: unpinned };
271
+ site.lines.push(unpinned.line);
272
+ sites.set(key, site);
273
+ }
274
+ }
275
+ return [...sites.keys()].sort().map((key) => buildIssue(context, sites.get(key)));
276
+ }
277
+ //# sourceMappingURL=action-pinning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-pinning.js","sourceRoot":"","sources":["../src/action-pinning.ts"],"names":[],"mappings":"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"}
@@ -10,13 +10,15 @@
10
10
  * Checks:
11
11
  * - Verification loop — "run lint/tests before saying done"
12
12
  * - Honesty policy — "report failures, don't fake success"
13
+ * - Ask-when-unsure — "say so and ask when uncertain, don't guess"
13
14
  * - Read-before-edit — "read a file before editing it"
14
15
  * - Scope control — "prefer focused diffs, don't rewrite unrelated code"
16
+ * - Code preservation — "preserve existing code, only change what's needed"
15
17
  * - Plan-first guidance — "outline the approach before coding"
16
18
  * - No contradictory "do not edit" header in human-maintained files
17
19
  *
18
20
  * All findings are info/warning severity — these are guidance gaps, not errors.
19
21
  */
20
22
  import type { InstructionFile, PromptCiIssue } from './types.js';
21
- export declare function detectAgentPractices(files: InstructionFile[]): PromptCiIssue[];
23
+ export declare function detectAgentPractices(files: InstructionFile[], aliases?: InstructionFile[]): PromptCiIssue[];
22
24
  //# sourceMappingURL=agent-practices.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-practices.d.ts","sourceRoot":"","sources":["../src/agent-practices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAsUjE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CA4T9E"}
1
+ {"version":3,"file":"agent-practices.d.ts","sourceRoot":"","sources":["../src/agent-practices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAscjE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,EAAE,EAAE,OAAO,GAAE,eAAe,EAAO,GAAG,aAAa,EAAE,CAwS/G"}