redlinegate 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 (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/commands/redline-init.md +54 -0
  4. package/commands/redline-verify.md +69 -0
  5. package/dist/bin/redline.js +605 -0
  6. package/dist/bin/redline.js.map +1 -0
  7. package/dist/commands/exempt.js +45 -0
  8. package/dist/commands/exempt.js.map +1 -0
  9. package/dist/commands/init.js +615 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/policy.js +25 -0
  12. package/dist/commands/policy.js.map +1 -0
  13. package/dist/commands/remove.js +427 -0
  14. package/dist/commands/remove.js.map +1 -0
  15. package/dist/commands/review.js +72 -0
  16. package/dist/commands/review.js.map +1 -0
  17. package/dist/commands/sync.js +42 -0
  18. package/dist/commands/sync.js.map +1 -0
  19. package/dist/commands/verify.js +478 -0
  20. package/dist/commands/verify.js.map +1 -0
  21. package/dist/config/redline-json.js +187 -0
  22. package/dist/config/redline-json.js.map +1 -0
  23. package/dist/core/errors.js +25 -0
  24. package/dist/core/errors.js.map +1 -0
  25. package/dist/core/git.js +152 -0
  26. package/dist/core/git.js.map +1 -0
  27. package/dist/core/log.js +22 -0
  28. package/dist/core/log.js.map +1 -0
  29. package/dist/core/severity.js +16 -0
  30. package/dist/core/severity.js.map +1 -0
  31. package/dist/core/version.js +15 -0
  32. package/dist/core/version.js.map +1 -0
  33. package/dist/detect/scan.js +79 -0
  34. package/dist/detect/scan.js.map +1 -0
  35. package/dist/detect/stack.js +71 -0
  36. package/dist/detect/stack.js.map +1 -0
  37. package/dist/enforce/ladder.js +148 -0
  38. package/dist/enforce/ladder.js.map +1 -0
  39. package/dist/exempt/parse.js +86 -0
  40. package/dist/exempt/parse.js.map +1 -0
  41. package/dist/metrics/options.js +257 -0
  42. package/dist/metrics/options.js.map +1 -0
  43. package/dist/metrics/run.js +72 -0
  44. package/dist/metrics/run.js.map +1 -0
  45. package/dist/platforms/azure/client.js +53 -0
  46. package/dist/platforms/azure/client.js.map +1 -0
  47. package/dist/platforms/azure/index.js +69 -0
  48. package/dist/platforms/azure/index.js.map +1 -0
  49. package/dist/platforms/azure/install.js +917 -0
  50. package/dist/platforms/azure/install.js.map +1 -0
  51. package/dist/platforms/azure/policy-types.js +70 -0
  52. package/dist/platforms/azure/policy-types.js.map +1 -0
  53. package/dist/platforms/azure/verify.js +309 -0
  54. package/dist/platforms/azure/verify.js.map +1 -0
  55. package/dist/platforms/detect.js +40 -0
  56. package/dist/platforms/detect.js.map +1 -0
  57. package/dist/platforms/github/client.js +49 -0
  58. package/dist/platforms/github/client.js.map +1 -0
  59. package/dist/platforms/github/index.js +54 -0
  60. package/dist/platforms/github/index.js.map +1 -0
  61. package/dist/platforms/github/install.js +558 -0
  62. package/dist/platforms/github/install.js.map +1 -0
  63. package/dist/platforms/github/push.js +102 -0
  64. package/dist/platforms/github/push.js.map +1 -0
  65. package/dist/platforms/github/remote.js +36 -0
  66. package/dist/platforms/github/remote.js.map +1 -0
  67. package/dist/platforms/github/verify.js +366 -0
  68. package/dist/platforms/github/verify.js.map +1 -0
  69. package/dist/platforms/http.js +58 -0
  70. package/dist/platforms/http.js.map +1 -0
  71. package/dist/platforms/pull-request-templates.js +157 -0
  72. package/dist/platforms/pull-request-templates.js.map +1 -0
  73. package/dist/platforms/remote.js +21 -0
  74. package/dist/platforms/remote.js.map +1 -0
  75. package/dist/platforms/resolve.js +69 -0
  76. package/dist/platforms/resolve.js.map +1 -0
  77. package/dist/platforms/shape.js +19 -0
  78. package/dist/platforms/shape.js.map +1 -0
  79. package/dist/platforms/types.js +24 -0
  80. package/dist/platforms/types.js.map +1 -0
  81. package/dist/policy/checks.js +91 -0
  82. package/dist/policy/checks.js.map +1 -0
  83. package/dist/policy/diff.js +50 -0
  84. package/dist/policy/diff.js.map +1 -0
  85. package/dist/registry/discover.js +74 -0
  86. package/dist/registry/discover.js.map +1 -0
  87. package/dist/registry/serialize.js +41 -0
  88. package/dist/registry/serialize.js.map +1 -0
  89. package/dist/registry/types.js +2 -0
  90. package/dist/registry/types.js.map +1 -0
  91. package/dist/remove/host.js +294 -0
  92. package/dist/remove/host.js.map +1 -0
  93. package/dist/render/commands.js +139 -0
  94. package/dist/render/commands.js.map +1 -0
  95. package/dist/render/manifest.js +82 -0
  96. package/dist/render/manifest.js.map +1 -0
  97. package/dist/render/markers.js +166 -0
  98. package/dist/render/markers.js.map +1 -0
  99. package/dist/render/profile.js +23 -0
  100. package/dist/render/profile.js.map +1 -0
  101. package/dist/render/standards.js +169 -0
  102. package/dist/render/standards.js.map +1 -0
  103. package/dist/render/vendors.js +199 -0
  104. package/dist/render/vendors.js.map +1 -0
  105. package/dist/review/engines/api.js +95 -0
  106. package/dist/review/engines/api.js.map +1 -0
  107. package/dist/review/engines/embedded.js +20 -0
  108. package/dist/review/engines/embedded.js.map +1 -0
  109. package/dist/review/engines/types.js +2 -0
  110. package/dist/review/engines/types.js.map +1 -0
  111. package/dist/review/glob.js +29 -0
  112. package/dist/review/glob.js.map +1 -0
  113. package/dist/review/prompt.js +45 -0
  114. package/dist/review/prompt.js.map +1 -0
  115. package/dist/review/rules.js +25 -0
  116. package/dist/review/rules.js.map +1 -0
  117. package/dist/review/schema.js +84 -0
  118. package/dist/review/schema.js.map +1 -0
  119. package/dist/review/scope.js +27 -0
  120. package/dist/review/scope.js.map +1 -0
  121. package/dist/sarif/map.js +58 -0
  122. package/dist/sarif/map.js.map +1 -0
  123. package/dist/sarif/parse.js +88 -0
  124. package/dist/sarif/parse.js.map +1 -0
  125. package/dist/sarif/types.js +2 -0
  126. package/dist/sarif/types.js.map +1 -0
  127. package/dist/sync/host.js +21 -0
  128. package/dist/sync/host.js.map +1 -0
  129. package/dist/sync/plan.js +47 -0
  130. package/dist/sync/plan.js.map +1 -0
  131. package/dist/sync/render.js +57 -0
  132. package/dist/sync/render.js.map +1 -0
  133. package/dist/sync/run.js +118 -0
  134. package/dist/sync/run.js.map +1 -0
  135. package/dist/verify/host.js +52 -0
  136. package/dist/verify/host.js.map +1 -0
  137. package/dist/verify/remote.js +146 -0
  138. package/dist/verify/remote.js.map +1 -0
  139. package/package.json +52 -0
  140. package/platforms/azure/gate-template.yml +105 -0
  141. package/rulesets/redline-org-ruleset.json +46 -0
  142. package/rulesets/redline-ruleset.json +49 -0
  143. package/scripts/assign-rule-ids.mjs +129 -0
  144. package/scripts/build-baseline.mjs +124 -0
  145. package/scripts/build-correlation.mjs +74 -0
  146. package/scripts/build-dashboard.mjs +584 -0
  147. package/scripts/build-digest.mjs +164 -0
  148. package/scripts/build-inbox.mjs +133 -0
  149. package/scripts/build-registry.mjs +42 -0
  150. package/scripts/build-roi.mjs +181 -0
  151. package/scripts/check-pins.mjs +88 -0
  152. package/scripts/collect-telemetry.mjs +300 -0
  153. package/scripts/lib/__tests__/baseline.test.mjs +132 -0
  154. package/scripts/lib/__tests__/correlate.test.mjs +131 -0
  155. package/scripts/lib/__tests__/dora.test.mjs +110 -0
  156. package/scripts/lib/__tests__/exemptions.test.mjs +85 -0
  157. package/scripts/lib/__tests__/metrics-scanner.test.mjs +65 -0
  158. package/scripts/lib/__tests__/sarif.test.mjs +125 -0
  159. package/scripts/lib/__tests__/spend.test.mjs +64 -0
  160. package/scripts/lib/baseline.mjs +156 -0
  161. package/scripts/lib/correlate.mjs +125 -0
  162. package/scripts/lib/dora.mjs +133 -0
  163. package/scripts/lib/exemptions.mjs +79 -0
  164. package/scripts/lib/metrics.mjs +172 -0
  165. package/scripts/lib/rules.mjs +107 -0
  166. package/scripts/lib/sarif.mjs +108 -0
  167. package/scripts/lib/spend.mjs +69 -0
  168. package/scripts/measure-context.mjs +101 -0
  169. package/scripts/render-self.mjs +18 -0
  170. package/scripts/score-seeds.mjs +294 -0
  171. package/scripts/validate.mjs +394 -0
  172. package/standards/core.md +124 -0
  173. package/standards/manifest.json +214 -0
  174. package/standards/stacks/csharp.md +29 -0
  175. package/standards/stacks/go.md +30 -0
  176. package/standards/stacks/java.md +30 -0
  177. package/standards/stacks/javascript.md +42 -0
  178. package/standards/stacks/kotlin.md +27 -0
  179. package/standards/stacks/microservices.md +37 -0
  180. package/standards/stacks/nodejs.md +34 -0
  181. package/standards/stacks/python.md +29 -0
  182. package/standards/stacks/react-native.md +29 -0
  183. package/standards/stacks/react.md +46 -0
  184. package/standards/stacks/swift.md +26 -0
  185. package/standards/stacks/terraform.md +27 -0
  186. package/templates/CODEOWNERS +29 -0
  187. package/templates/azure/pull_request_template.md +72 -0
  188. package/templates/github/pull_request_template.md +72 -0
  189. package/templates/redline.yml +35 -0
  190. package/templates/repo-context.md +64 -0
  191. package/workflows/dashboard.yml +119 -0
  192. package/workflows/inbox.yml +97 -0
  193. package/workflows/redline-collect.yml +61 -0
  194. package/workflows/redline-gate.yml +301 -0
  195. package/workflows/redline-sync.yml +79 -0
  196. package/workflows/seed-canary.yml +231 -0
  197. package/workflows/verify-onboarding.yml +115 -0
  198. package/workflows/weekly-digest.yml +78 -0
@@ -0,0 +1,95 @@
1
+ import { RedlineError } from "../../core/errors.js";
2
+ // The api engine: the CLI calls a configured endpoint itself.
3
+ //
4
+ // Two dialects, per the v3 design. OpenAI-compatible is the important one — it
5
+ // covers the fully local case at no cost, because Ollama, LM Studio and vLLM all
6
+ // expose it, and a review that has to send a diff to a third party is a review
7
+ // several of this organisation's markets cannot run at all.
8
+ //
9
+ // The endpoint is configured, never assumed, and the key comes from the
10
+ // environment. A model name baked into the tool is a model nobody can change when
11
+ // it is deprecated or when a market's regulator objects to it.
12
+ const DEFAULTS = {
13
+ openai: { baseUrl: 'http://localhost:11434/v1', apiKeyEnv: 'OPENAI_API_KEY' },
14
+ anthropic: { baseUrl: 'https://api.anthropic.com/v1', apiKeyEnv: 'ANTHROPIC_API_KEY' },
15
+ };
16
+ export function createApiEngine(config, deps = {}) {
17
+ const doFetch = deps.fetch ?? ((url, init) => fetch(url, init));
18
+ const env = deps.env ?? process.env;
19
+ const defaults = DEFAULTS[config.provider];
20
+ if (!defaults) {
21
+ throw new RedlineError('usage', `unknown review provider "${config.provider}" — use "openai" (any OpenAI-compatible endpoint, including a local one) or "anthropic"`);
22
+ }
23
+ const baseUrl = (config.baseUrl ?? defaults.baseUrl).replace(/\/+$/, '');
24
+ const keyEnv = config.apiKeyEnv ?? defaults.apiKeyEnv;
25
+ return {
26
+ name: `api:${config.provider}`,
27
+ async review(request) {
28
+ const key = env[keyEnv];
29
+ // A local endpoint legitimately needs no key. A remote one that silently
30
+ // sent an unauthenticated request would fail confusingly at the far end.
31
+ const local = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])/.test(baseUrl);
32
+ if (!key && !local) {
33
+ throw new RedlineError('permission', `no API key: set ${keyEnv}`, `or point --engine at a local endpoint, which needs none`);
34
+ }
35
+ const { url, init, extract } = config.provider === 'anthropic'
36
+ ? anthropic(baseUrl, config.model, key, request)
37
+ : openai(baseUrl, config.model, key, request);
38
+ let response;
39
+ try {
40
+ response = await doFetch(url, init);
41
+ }
42
+ catch (error) {
43
+ throw new RedlineError('host', `could not reach the review endpoint at ${baseUrl}: ${error instanceof Error ? error.message : String(error)}`);
44
+ }
45
+ if (!response.ok) {
46
+ throw new RedlineError('host', `the review endpoint returned ${response.status}`);
47
+ }
48
+ return { kind: 'output', raw: extract(await response.json()) };
49
+ },
50
+ };
51
+ }
52
+ function openai(baseUrl, model, key, request) {
53
+ return {
54
+ url: `${baseUrl}/chat/completions`,
55
+ init: {
56
+ method: 'POST',
57
+ headers: {
58
+ 'content-type': 'application/json',
59
+ ...(key ? { authorization: `Bearer ${key}` } : {}),
60
+ },
61
+ body: JSON.stringify({
62
+ model,
63
+ // Zero: a review is not a creative task, and two runs over the same diff
64
+ // disagreeing is how a team learns to disregard it.
65
+ temperature: 0,
66
+ messages: [{ role: 'user', content: request.prompt }],
67
+ }),
68
+ },
69
+ extract: (body) => body?.choices?.[0]?.message?.content ?? '',
70
+ };
71
+ }
72
+ function anthropic(baseUrl, model, key, request) {
73
+ return {
74
+ url: `${baseUrl}/messages`,
75
+ init: {
76
+ method: 'POST',
77
+ headers: {
78
+ 'content-type': 'application/json',
79
+ 'anthropic-version': '2023-06-01',
80
+ ...(key ? { 'x-api-key': key } : {}),
81
+ },
82
+ body: JSON.stringify({
83
+ model,
84
+ max_tokens: 4096,
85
+ temperature: 0,
86
+ messages: [{ role: 'user', content: request.prompt }],
87
+ }),
88
+ },
89
+ extract: (body) => (body?.content ?? [])
90
+ .filter((block) => block.type === 'text')
91
+ .map((block) => block.text ?? '')
92
+ .join(''),
93
+ };
94
+ }
95
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../cli/review/engines/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ // The embedded engine: the assistant already running this command applies the
2
+ // prompt itself.
3
+ //
4
+ // It returns the prompt rather than a review, and says so. That is not a stub —
5
+ // it is the whole design for the case where the CLI is being run BY an assistant
6
+ // (Claude Code, Copilot, Cursor), which already has a model and a context. Making
7
+ // the CLI call a second model from inside the first one's session would pay twice
8
+ // for a worse answer.
9
+ export const embedded = {
10
+ name: 'embedded',
11
+ async review(request) {
12
+ return {
13
+ kind: 'prompt',
14
+ prompt: request.prompt,
15
+ note: 'embedded engine: apply the prompt above and return the JSON it asks for. ' +
16
+ 'Run `redline review --engine api` to have the CLI call a model itself.',
17
+ };
18
+ },
19
+ };
20
+ //# sourceMappingURL=embedded.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedded.js","sourceRoot":"","sources":["../../../cli/review/engines/embedded.ts"],"names":[],"mappings":"AAEA,8EAA8E;AAC9E,iBAAiB;AACjB,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,kFAAkF;AAClF,kFAAkF;AAClF,sBAAsB;AACtB,MAAM,CAAC,MAAM,QAAQ,GAAiB;IACpC,IAAI,EAAE,UAAU;IAChB,KAAK,CAAC,MAAM,CAAC,OAAsB;QACjC,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EACF,2EAA2E;gBAC3E,wEAAwE;SAC3E,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../cli/review/engines/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ // The subset of glob syntax the standards actually use, matched without a
2
+ // dependency.
3
+ //
4
+ // `scripts/validate.mjs` already refuses a glob this cannot express: negation,
5
+ // brace expansion and commas are rejected at build time because Copilot's
6
+ // `applyTo` cannot express them either. So the grammar here is exactly `**`, `*`
7
+ // and `?`, and anything else in a manifest is a build failure rather than a
8
+ // silent mismatch at review time.
9
+ export function minimatch(path, glob) {
10
+ const pattern = glob
11
+ .split('')
12
+ .reduce((acc, _c, i, chars) => {
13
+ if (i < acc.i)
14
+ return acc;
15
+ const rest = chars.slice(i).join('');
16
+ if (rest.startsWith('**/'))
17
+ return { out: `${acc.out}(?:.*/)?`, i: i + 3 };
18
+ if (rest.startsWith('**'))
19
+ return { out: `${acc.out}.*`, i: i + 2 };
20
+ const c = chars[i];
21
+ if (c === '*')
22
+ return { out: `${acc.out}[^/]*`, i: i + 1 };
23
+ if (c === '?')
24
+ return { out: `${acc.out}[^/]`, i: i + 1 };
25
+ return { out: acc.out + c.replace(/[.+^${}()|[\]\\]/g, '\\$&'), i: i + 1 };
26
+ }, { out: '', i: 0 }).out;
27
+ return new RegExp(`^${pattern}$`).test(path);
28
+ }
29
+ //# sourceMappingURL=glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.js","sourceRoot":"","sources":["../../cli/review/glob.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,cAAc;AACd,EAAE;AACF,+EAA+E;AAC/E,0EAA0E;AAC1E,iFAAiF;AACjF,4EAA4E;AAC5E,kCAAkC;AAClC,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY;IAClD,MAAM,OAAO,GAAG,IAAI;SACjB,KAAK,CAAC,EAAE,CAAC;SACT,MAAM,CACL,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE;QACpB,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3E,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QACpE,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACpB,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3D,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1D,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7E,CAAC,EACD,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAClB,CAAC,GAAG,CAAC;IAER,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { SCHEMA_DESCRIPTION } from "./schema.js";
4
+ const read = (root, relPath) => readFileSync(join(root, relPath), 'utf8').trimEnd();
5
+ export function buildPrompt(opts) {
6
+ const { manifest, scope } = opts;
7
+ const sections = [
8
+ read(opts.root, manifest.core.source),
9
+ ...scope.stacks.map((id) => read(opts.root, manifest.stacks[id].source)),
10
+ ];
11
+ return [
12
+ 'You are reviewing a change against this organisation\'s engineering standard.',
13
+ '',
14
+ `Profile: ${scope.profile}. Applicable stacks: ${scope.stacks.join(', ') || 'core only'}.`,
15
+ 'These are the ONLY rules in scope. Do not apply a rule that is not written below,',
16
+ 'and do not cite a rule id that does not appear below — a finding you cannot point at',
17
+ 'a rule for is not a finding.',
18
+ '',
19
+ '--- RULES ---',
20
+ '',
21
+ ...sections,
22
+ '',
23
+ '--- THE CHANGE ---',
24
+ '',
25
+ '```diff',
26
+ opts.diff,
27
+ '```',
28
+ '',
29
+ '--- WHAT TO RETURN ---',
30
+ '',
31
+ 'Return JSON and nothing else, against this schema:',
32
+ '',
33
+ SCHEMA_DESCRIPTION,
34
+ '',
35
+ 'Rules for what you return:',
36
+ '- Only lines this change ADDED. An existing pattern the change merely touches is',
37
+ ' explicitly out of scope — see "What NOT to flag" in the rules above.',
38
+ '- One finding per problem. If the same rule is broken in several places, report the',
39
+ ' first and say "and N similar" in the problem.',
40
+ '- If you cannot describe the input that breaks it, it is not a finding. Return fewer.',
41
+ '- If nothing qualifies, return {"findings": []}. An empty review is a valid review and',
42
+ ' is a better answer than a manufactured one.',
43
+ ].join('\n');
44
+ }
45
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../cli/review/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAgBjD,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,OAAe,EAAU,EAAE,CACrD,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;AAEtD,MAAM,UAAU,WAAW,CAAC,IAAmB;IAC7C,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACjC,MAAM,QAAQ,GAAG;QACf,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QACrC,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAE,CAAC,MAAM,CAAC,CAAC;KAC1E,CAAC;IAEF,OAAO;QACL,+EAA+E;QAC/E,EAAE;QACF,YAAY,KAAK,CAAC,OAAO,wBAAwB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,WAAW,GAAG;QAC1F,mFAAmF;QACnF,sFAAsF;QACtF,8BAA8B;QAC9B,EAAE;QACF,eAAe;QACf,EAAE;QACF,GAAG,QAAQ;QACX,EAAE;QACF,oBAAoB;QACpB,EAAE;QACF,SAAS;QACT,IAAI,CAAC,IAAI;QACT,KAAK;QACL,EAAE;QACF,wBAAwB;QACxB,EAAE;QACF,oDAAoD;QACpD,EAAE;QACF,kBAAkB;QAClB,EAAE;QACF,4BAA4B;QAC5B,kFAAkF;QAClF,wEAAwE;QACxE,qFAAqF;QACrF,iDAAiD;QACjD,uFAAuF;QACvF,wFAAwF;QACxF,+CAA+C;KAChD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ // The rule ids in scope, parsed from the same markdown the prompt carries.
4
+ //
5
+ // Parsed from the source rather than taken from a list, so the set the model is
6
+ // validated against is exactly the set it was shown. Two lists would drift, and
7
+ // the drift would appear as a model "inventing" a rule that was in the prompt all
8
+ // along — which is the kind of bug that gets blamed on the model for a year.
9
+ const RULE_ID = /^-\s+`([a-z0-9][a-z0-9-]*\/[a-z0-9][a-z0-9-]*)`/gm;
10
+ export function loadRuleIds(root, manifest, stacks) {
11
+ const ids = new Set();
12
+ const sources = [manifest.core.source, ...stacks.map((id) => manifest.stacks[id].source)];
13
+ for (const source of sources) {
14
+ const body = readFileSync(join(root, source), 'utf8');
15
+ for (const match of body.matchAll(RULE_ID))
16
+ ids.add(match[1]);
17
+ }
18
+ // Reserved by the standard for a real problem no rule covers. It is in the
19
+ // core markdown as prose rather than as a rule bullet, so the parse above does
20
+ // not find it, and rejecting it would silence exactly the finding the standard
21
+ // asks for when the catalogue has a gap.
22
+ ids.add('core/uncatalogued');
23
+ return ids;
24
+ }
25
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../cli/review/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,2EAA2E;AAC3E,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,kFAAkF;AAClF,6EAA6E;AAC7E,MAAM,OAAO,GAAG,mDAAmD,CAAC;AAEpE,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAkB,EAAE,MAAgB;IAC5E,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3F,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QACtD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;IACjE,CAAC;IACD,2EAA2E;IAC3E,+EAA+E;IAC/E,+EAA+E;IAC/E,yCAAyC;IACzC,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC7B,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { isSeverity } from "../core/severity.js";
2
+ export const SCHEMA_DESCRIPTION = `{
3
+ "findings": [
4
+ {
5
+ "rule": "<stack>/<slug>, exactly as written in the rules above",
6
+ "severity": "BLOCKER" | "HIGH" | "SUGGESTION",
7
+ "file": "path/as/given/in/the/diff.ts",
8
+ "line": <line number in the new file>,
9
+ "problem": "one sentence: what breaks, and the input that breaks it",
10
+ "fix": "one sentence: the concrete change"
11
+ }
12
+ ]
13
+ }`;
14
+ /**
15
+ * Validate a model's JSON against the schema.
16
+ *
17
+ * `knownRules` is the applicable rule set. A finding citing a rule outside it is
18
+ * rejected — not corrected, not passed through. A model that cites a rule the
19
+ * prompt did not contain has not applied the standard, it has recalled one, and
20
+ * the id is what every measurement is keyed on.
21
+ */
22
+ export function parseReview(raw, knownRules) {
23
+ const findings = [];
24
+ const rejected = [];
25
+ let parsed;
26
+ try {
27
+ // A model will wrap JSON in a fence more often than not. Tolerating that is
28
+ // not the same as tolerating malformed output.
29
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(raw);
30
+ parsed = JSON.parse(fenced?.[1] ?? raw);
31
+ }
32
+ catch (error) {
33
+ return {
34
+ findings,
35
+ rejected: [{ raw, reason: `not valid JSON: ${error instanceof Error ? error.message : String(error)}` }],
36
+ };
37
+ }
38
+ const list = parsed?.findings;
39
+ if (!Array.isArray(list)) {
40
+ return { findings, rejected: [{ raw: parsed, reason: 'no "findings" array' }] };
41
+ }
42
+ for (const item of list) {
43
+ const f = item;
44
+ const reject = (reason) => {
45
+ rejected.push({ raw: item, reason });
46
+ };
47
+ if (typeof f['rule'] !== 'string' || !knownRules.has(f['rule'])) {
48
+ reject(`cites "${String(f['rule'])}", which is not one of the rules this review was given`);
49
+ continue;
50
+ }
51
+ if (!isSeverity(f['severity'])) {
52
+ reject(`severity "${String(f['severity'])}" is not BLOCKER, HIGH or SUGGESTION`);
53
+ continue;
54
+ }
55
+ if (typeof f['file'] !== 'string' || f['file'] === '') {
56
+ reject('no file');
57
+ continue;
58
+ }
59
+ const line = typeof f['line'] === 'number' ? f['line'] : Number.parseInt(String(f['line']), 10);
60
+ if (!Number.isInteger(line) || line < 1) {
61
+ reject(`line "${String(f['line'])}" is not a line number`);
62
+ continue;
63
+ }
64
+ if (typeof f['problem'] !== 'string' || f['problem'].trim() === '') {
65
+ reject('no problem statement — a finding nobody can act on is not a finding');
66
+ continue;
67
+ }
68
+ findings.push({
69
+ rule: f['rule'],
70
+ severity: f['severity'],
71
+ file: f['file'],
72
+ line,
73
+ problem: f['problem'].trim(),
74
+ fix: typeof f['fix'] === 'string' ? f['fix'].trim() : '',
75
+ });
76
+ }
77
+ return { findings, rejected };
78
+ }
79
+ /** The output contract. Rendered here and nowhere else. */
80
+ export function renderFinding(finding) {
81
+ const body = finding.fix ? `${finding.problem} ${finding.fix}` : finding.problem;
82
+ return `Redline/${finding.severity} [${finding.rule}]: ${body}`;
83
+ }
84
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../cli/review/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { minimatch } from "./glob.js";
2
+ import { resolveProfile } from "../render/profile.js";
3
+ export function resolveScope(manifest, profile, files) {
4
+ const resolved = resolveProfile(manifest, profile);
5
+ const matched = new Set();
6
+ const uncovered = [];
7
+ for (const file of files) {
8
+ let any = false;
9
+ for (const id of resolved.stacks) {
10
+ const globs = manifest.stacks[id]?.globs ?? [];
11
+ if (globs.some((glob) => minimatch(file, glob))) {
12
+ matched.add(id);
13
+ any = true;
14
+ }
15
+ }
16
+ if (!any)
17
+ uncovered.push(file);
18
+ }
19
+ // The profile's own order, not insertion order: a stack that extends another
20
+ // must come after it, and the manifest already encodes that ordering.
21
+ return {
22
+ profile: resolved.profile,
23
+ stacks: resolved.stacks.filter((id) => matched.has(id)),
24
+ uncovered,
25
+ };
26
+ }
27
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../cli/review/scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAqBtD,MAAM,UAAU,YAAY,CAAC,QAAkB,EAAE,OAAe,EAAE,KAAe;IAC/E,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,GAAG,GAAG,KAAK,CAAC;QAChB,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;YAC/C,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;gBAChD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,GAAG,GAAG,IAAI,CAAC;YACb,CAAC;QACH,CAAC;QACD,IAAI,CAAC,GAAG;YAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,6EAA6E;IAC7E,sEAAsE;IACtE,OAAO;QACL,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvD,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { SEVERITY_RANK, isSeverity } from "../core/severity.js";
2
+ // SARIF 2.1.0's own `level` vocabulary, plus the ones the common producers
3
+ // actually emit in `properties.security-severity` bands and vendor fields.
4
+ export const DEFAULT_SEVERITY_MAP = {
5
+ // SARIF levels
6
+ error: 'BLOCKER',
7
+ warning: 'HIGH',
8
+ note: 'SUGGESTION',
9
+ none: 'SUGGESTION',
10
+ // Common vendor words
11
+ critical: 'BLOCKER',
12
+ high: 'BLOCKER',
13
+ medium: 'HIGH',
14
+ moderate: 'HIGH',
15
+ low: 'SUGGESTION',
16
+ info: 'SUGGESTION',
17
+ informational: 'SUGGESTION',
18
+ };
19
+ export const FALLBACK_SEVERITY = 'SUGGESTION';
20
+ export function mapSeverity(native, map) {
21
+ const word = (native ?? '').trim();
22
+ const key = word.toLowerCase();
23
+ const mapped = map[key];
24
+ if (mapped)
25
+ return { severity: mapped, native: word, matched: true };
26
+ return { severity: FALLBACK_SEVERITY, native: word, matched: false };
27
+ }
28
+ /**
29
+ * A repository's override, merged over the defaults.
30
+ *
31
+ * Overrides are validated rather than trusted: `.redline.json` is a file a
32
+ * repository edits, and a typo like `"error": "CRITICAL"` must be refused at the
33
+ * boundary with a message naming it — not silently ignored, which would leave a
34
+ * repository believing it had raised a severity it had not.
35
+ */
36
+ export function resolveSeverityMap(override) {
37
+ const problems = [];
38
+ const map = { ...DEFAULT_SEVERITY_MAP };
39
+ if (override === undefined || override === null)
40
+ return { map, problems };
41
+ if (typeof override !== 'object' || Array.isArray(override)) {
42
+ problems.push('severityMap must be an object of {"<their word>": "BLOCKER|HIGH|SUGGESTION"}');
43
+ return { map, problems };
44
+ }
45
+ for (const [word, value] of Object.entries(override)) {
46
+ if (!isSeverity(value)) {
47
+ problems.push(`severityMap["${word}"] is ${JSON.stringify(value)} — it must be one of BLOCKER, HIGH or SUGGESTION`);
48
+ continue;
49
+ }
50
+ map[word.toLowerCase()] = value;
51
+ }
52
+ return { map, problems };
53
+ }
54
+ /** The most severe of a set. Never an average — an average severity is not a thing. */
55
+ export function mostSevere(severities) {
56
+ return severities.reduce((worst, s) => (worst === null || SEVERITY_RANK[s] > SEVERITY_RANK[worst] ? s : worst), null);
57
+ }
58
+ //# sourceMappingURL=map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.js","sourceRoot":"","sources":["../../cli/sarif/map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAiB,MAAM,qBAAqB,CAAC;AAe/E,2EAA2E;AAC3E,2EAA2E;AAC3E,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAC/C,eAAe;IACf,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,MAAM;IACf,IAAI,EAAE,YAAY;IAClB,IAAI,EAAE,YAAY;IAClB,sBAAsB;IACtB,QAAQ,EAAE,SAAS;IACnB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,MAAM;IAChB,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,YAAY;IAClB,aAAa,EAAE,YAAY;CAC5B,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAa,YAAY,CAAC;AAYxD,MAAM,UAAU,WAAW,CAAC,MAA0B,EAAE,GAAgB;IACtE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,MAAM;QAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACrE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAiB;IAClD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,GAAG,GAAgB,EAAE,GAAG,oBAAoB,EAAE,CAAC;IAErD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAC1E,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5D,QAAQ,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;QAC9F,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAmC,CAAC,EAAE,CAAC;QAChF,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,CACX,gBAAgB,IAAI,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,kDAAkD,CACrG,CAAC;YACF,SAAS;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,KAAK,CAAC;IAClC,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,UAAU,CAAC,UAAsB;IAC/C,OAAO,UAAU,CAAC,MAAM,CACtB,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EACrF,IAAI,CACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { mapSeverity } from "./map.js";
2
+ // CodeQL and several others carry a 0-10 CVSS-ish band here rather than a word.
3
+ // Read as a band, not a number: the point is which of three buckets it lands in,
4
+ // and inventing precision the producer did not have would be worse than the
5
+ // coarse answer.
6
+ function securitySeverityBand(properties) {
7
+ const raw = properties?.['security-severity'];
8
+ const value = typeof raw === 'string' ? Number.parseFloat(raw) : typeof raw === 'number' ? raw : NaN;
9
+ if (Number.isNaN(value))
10
+ return undefined;
11
+ if (value >= 9)
12
+ return 'critical';
13
+ if (value >= 7)
14
+ return 'high';
15
+ if (value >= 4)
16
+ return 'medium';
17
+ return 'low';
18
+ }
19
+ export function parseSarif(raw, map, where = 'sarif') {
20
+ const findings = [];
21
+ const problems = [];
22
+ let log;
23
+ try {
24
+ log = JSON.parse(raw);
25
+ }
26
+ catch (error) {
27
+ return {
28
+ findings,
29
+ problems: [`${where}: not valid JSON (${error instanceof Error ? error.message : String(error)})`],
30
+ };
31
+ }
32
+ if (typeof log !== 'object' || log === null || !Array.isArray(log.runs)) {
33
+ return { findings, problems: [`${where}: not a SARIF log — no "runs" array`] };
34
+ }
35
+ log.runs.forEach((run, runIndex) => {
36
+ const tool = run.tool?.driver?.name?.trim() || 'unknown';
37
+ const ruleTable = new Map();
38
+ for (const rule of run.tool?.driver?.rules ?? []) {
39
+ if (rule.id)
40
+ ruleTable.set(rule.id, rule);
41
+ }
42
+ const results = run.results;
43
+ if (!Array.isArray(results)) {
44
+ // A run with no results is a scanner that found nothing — a fact worth
45
+ // ingesting as silence, not a malformed file.
46
+ return;
47
+ }
48
+ results.forEach((result, i) => {
49
+ const ruleId = result.ruleId?.trim();
50
+ if (!ruleId) {
51
+ problems.push(`${where}: run ${runIndex} result ${i} has no ruleId — skipped`);
52
+ return;
53
+ }
54
+ const rule = ruleTable.get(ruleId);
55
+ // Precedence: the result's own level, then the rule's default, then the
56
+ // security-severity band. The result is most specific and the band is the
57
+ // least, which is the order a reader would expect.
58
+ const native = result.level ??
59
+ rule?.defaultConfiguration?.level ??
60
+ securitySeverityBand(result.properties) ??
61
+ securitySeverityBand(rule?.properties);
62
+ const mapped = mapSeverity(native, map);
63
+ if (!mapped.matched && native) {
64
+ problems.push(`${where}: ${tool} severity "${native}" is not in the severity map — ${ruleId} ingested as ${mapped.severity}`);
65
+ }
66
+ const location = result.locations?.[0]?.physicalLocation;
67
+ findings.push({
68
+ source: 'sarif',
69
+ tool,
70
+ ruleId,
71
+ severity: mapped.severity,
72
+ nativeSeverity: mapped.native,
73
+ message: result.message?.text?.trim() ?? '',
74
+ file: location?.artifactLocation?.uri ?? null,
75
+ line: location?.region?.startLine ?? null,
76
+ });
77
+ });
78
+ });
79
+ return { findings, problems };
80
+ }
81
+ /** Per-tool counts, for a view that must never merge two rule catalogues. */
82
+ export function byTool(findings) {
83
+ const counts = {};
84
+ for (const finding of findings)
85
+ counts[finding.tool] = (counts[finding.tool] ?? 0) + 1;
86
+ return counts;
87
+ }
88
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../cli/sarif/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../cli/sarif/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import { RedlineError } from "../core/errors.js";
2
+ import { createGitHubClient } from "../platforms/github/client.js";
3
+ import { readRemoteConfig, readRemoteFile } from "../platforms/github/remote.js";
4
+ import { openOrUpdatePullRequest, pushRemoteFiles } from "../platforms/github/push.js";
5
+ // The estate spans both hosts, so one sync run needs a client per host rather
6
+ // than the single resolved platform every other command works with — those all
7
+ // act on one repository, which has exactly one host.
8
+ export function createSyncHost(clients = {}) {
9
+ const github = clients.github ?? createGitHubClient();
10
+ const unsupported = (host) => {
11
+ throw new RedlineError('host', `sync does not support ${host} yet`, 'Azure DevOps discovery and sync are outstanding — see the roadmap Phase 0');
12
+ };
13
+ const forHost = (ref) => ref.host === 'github' ? github : unsupported(ref.host);
14
+ return {
15
+ readRemoteConfig: (ref) => readRemoteConfig(forHost(ref), ref),
16
+ readRemoteFile: (ref, path) => readRemoteFile(forHost(ref), ref, path),
17
+ pushFiles: (ref, push) => pushRemoteFiles(forHost(ref), ref, push),
18
+ openPullRequest: (ref, pr) => openOrUpdatePullRequest(forHost(ref), ref, pr),
19
+ };
20
+ }
21
+ //# sourceMappingURL=host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../../cli/sync/host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAqB,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAGvF,8EAA8E;AAC9E,+EAA+E;AAC/E,qDAAqD;AACrD,MAAM,UAAU,cAAc,CAAC,UAAmD,EAAE;IAClF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,kBAAkB,EAAE,CAAC;IAEtD,MAAM,WAAW,GAAG,CAAC,IAAU,EAAS,EAAE;QACxC,MAAM,IAAI,YAAY,CACpB,MAAM,EACN,yBAAyB,IAAI,MAAM,EACnC,2EAA2E,CAC5E,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,GAAY,EAA8B,EAAE,CAC3D,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAEzD,OAAO;QACL,gBAAgB,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;QAC9D,cAAc,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC;QACtE,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC;QAClE,eAAe,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,uBAAuB,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;KAC7E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,47 @@
1
+ // Compare two dotted versions numerically. A string compare would rank 0.0.10
2
+ // below 0.0.9 and quietly stop syncing an estate on the tenth patch.
3
+ function isBehind(recorded, current) {
4
+ const parse = (v) => v.split('.').map((p) => Number.parseInt(p, 10));
5
+ const a = parse(recorded);
6
+ const b = parse(current);
7
+ const length = Math.max(a.length, b.length);
8
+ for (let i = 0; i < length; i += 1) {
9
+ const left = a[i] ?? 0;
10
+ const right = b[i] ?? 0;
11
+ // A recorded version that is not a number at all — hand-edited, or written
12
+ // by a version of the CLI that did not exist yet — is treated as behind.
13
+ // Syncing it proposes a correction; skipping it would strand the repository.
14
+ if (Number.isNaN(left))
15
+ return true;
16
+ if (Number.isNaN(right))
17
+ return false;
18
+ if (left !== right)
19
+ return left < right;
20
+ }
21
+ return false;
22
+ }
23
+ // Which registered repositories need a pull request, and why each other one
24
+ // does not. Pure: no host, no filesystem. Every skip carries its reason so a
25
+ // dry run explains the whole estate rather than only the part it would touch.
26
+ export function planSync(registry, standardsVersion, opts = {}) {
27
+ const targets = [];
28
+ const skipped = [];
29
+ for (const entry of registry.entries) {
30
+ const name = `${entry.org}/${entry.repo}`;
31
+ if (opts.repo && name !== opts.repo) {
32
+ skipped.push({ entry, reason: `not ${opts.repo}` });
33
+ continue;
34
+ }
35
+ if (opts.force) {
36
+ targets.push({ entry, from: entry.standardsVersion });
37
+ continue;
38
+ }
39
+ if (isBehind(entry.standardsVersion, standardsVersion)) {
40
+ targets.push({ entry, from: entry.standardsVersion });
41
+ continue;
42
+ }
43
+ skipped.push({ entry, reason: `already at standards v${entry.standardsVersion}` });
44
+ }
45
+ return { standardsVersion, targets, skipped };
46
+ }
47
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../cli/sync/plan.ts"],"names":[],"mappings":"AA2BA,8EAA8E;AAC9E,qEAAqE;AACrE,SAAS,QAAQ,CAAC,QAAgB,EAAE,OAAe;IACjD,MAAM,KAAK,GAAG,CAAC,CAAS,EAAY,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACvF,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC1B,MAAM,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxB,2EAA2E;QAC3E,yEAAyE;QACzE,6EAA6E;QAC7E,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACpC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACtC,IAAI,IAAI,KAAK,KAAK;YAAE,OAAO,IAAI,GAAG,KAAK,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,MAAM,UAAU,QAAQ,CACtB,QAAkB,EAClB,gBAAwB,EACxB,OAAoB,EAAE;IAEtB,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,MAAM,OAAO,GAAe,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACpD,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;YACtD,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,CAAC,KAAK,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,EAAE,CAAC;YACvD,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;YACtD,SAAS;QACX,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,KAAK,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAChD,CAAC"}