@lyse-labs/lyse 0.1.0-alpha.2 → 0.2.0-alpha.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 (140) hide show
  1. package/LICENSE +0 -5
  2. package/dist/cli.js +67 -11
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/__tests__/add-ci-gate.test.d.ts +1 -0
  5. package/dist/commands/__tests__/add-ci-gate.test.js +149 -0
  6. package/dist/commands/__tests__/add-ci-gate.test.js.map +1 -0
  7. package/dist/commands/add-ci-gate.d.ts +26 -0
  8. package/dist/commands/add-ci-gate.js +429 -0
  9. package/dist/commands/add-ci-gate.js.map +1 -0
  10. package/dist/commands/audit-pipeline.js +1 -1
  11. package/dist/commands/audit-pipeline.js.map +1 -1
  12. package/dist/commands/fix.d.ts +8 -8
  13. package/dist/commands/init.d.ts +3 -3
  14. package/dist/commands/init.js +1 -1
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/mcp-setup.js +1 -1
  17. package/dist/commands/mcp-setup.js.map +1 -1
  18. package/dist/config/schema.d.ts +14 -14
  19. package/dist/llm/__tests__/layer4-stage.test.d.ts +1 -0
  20. package/dist/llm/__tests__/layer4-stage.test.js +157 -0
  21. package/dist/llm/__tests__/layer4-stage.test.js.map +1 -0
  22. package/dist/llm/__tests__/validator.test.d.ts +1 -0
  23. package/dist/llm/__tests__/validator.test.js +156 -0
  24. package/dist/llm/__tests__/validator.test.js.map +1 -0
  25. package/dist/llm/connectors/__tests__/anthropic-adapter.test.d.ts +1 -0
  26. package/dist/llm/connectors/__tests__/anthropic-adapter.test.js +99 -0
  27. package/dist/llm/connectors/__tests__/anthropic-adapter.test.js.map +1 -0
  28. package/dist/llm/connectors/__tests__/cache.test.d.ts +1 -0
  29. package/dist/llm/connectors/__tests__/cache.test.js +50 -0
  30. package/dist/llm/connectors/__tests__/cache.test.js.map +1 -0
  31. package/dist/llm/connectors/__tests__/noop-adapter.test.d.ts +1 -0
  32. package/dist/llm/connectors/__tests__/noop-adapter.test.js +21 -0
  33. package/dist/llm/connectors/__tests__/noop-adapter.test.js.map +1 -0
  34. package/dist/llm/connectors/__tests__/openai-compatible-adapter.test.d.ts +1 -0
  35. package/dist/llm/connectors/__tests__/openai-compatible-adapter.test.js +82 -0
  36. package/dist/llm/connectors/__tests__/openai-compatible-adapter.test.js.map +1 -0
  37. package/dist/llm/connectors/__tests__/resolver.test.d.ts +1 -0
  38. package/dist/llm/connectors/__tests__/resolver.test.js +140 -0
  39. package/dist/llm/connectors/__tests__/resolver.test.js.map +1 -0
  40. package/dist/llm/connectors/anthropic-adapter.d.ts +12 -0
  41. package/dist/llm/connectors/anthropic-adapter.js +57 -0
  42. package/dist/llm/connectors/anthropic-adapter.js.map +1 -0
  43. package/dist/llm/connectors/cache.d.ts +15 -0
  44. package/dist/llm/connectors/cache.js +57 -0
  45. package/dist/llm/connectors/cache.js.map +1 -0
  46. package/dist/llm/connectors/noop-adapter.d.ts +4 -0
  47. package/dist/llm/connectors/noop-adapter.js +12 -0
  48. package/dist/llm/connectors/noop-adapter.js.map +1 -0
  49. package/dist/llm/connectors/openai-compatible-adapter.d.ts +13 -0
  50. package/dist/llm/connectors/openai-compatible-adapter.js +47 -0
  51. package/dist/llm/connectors/openai-compatible-adapter.js.map +1 -0
  52. package/dist/llm/connectors/resolver.d.ts +9 -0
  53. package/dist/llm/connectors/resolver.js +162 -0
  54. package/dist/llm/connectors/resolver.js.map +1 -0
  55. package/dist/llm/connectors/types.d.ts +25 -0
  56. package/dist/llm/connectors/types.js +13 -0
  57. package/dist/llm/connectors/types.js.map +1 -0
  58. package/dist/llm/layer4-stage.d.ts +8 -6
  59. package/dist/llm/layer4-stage.js +145 -9
  60. package/dist/llm/layer4-stage.js.map +1 -1
  61. package/dist/llm/rubric-stub.d.ts +8 -0
  62. package/dist/llm/rubric-stub.js +4 -0
  63. package/dist/llm/rubric-stub.js.map +1 -0
  64. package/dist/llm/validator.d.ts +18 -0
  65. package/dist/llm/validator.js +81 -0
  66. package/dist/llm/validator.js.map +1 -0
  67. package/dist/parsers/ai-tokens.d.ts +10 -0
  68. package/dist/parsers/ai-tokens.js +156 -0
  69. package/dist/parsers/ai-tokens.js.map +1 -0
  70. package/dist/reliability/catalogue/__tests__/sub-axes.test.js +13 -3
  71. package/dist/reliability/catalogue/__tests__/sub-axes.test.js.map +1 -1
  72. package/dist/reliability/catalogue/sub-axes.js +16 -0
  73. package/dist/reliability/catalogue/sub-axes.js.map +1 -1
  74. package/dist/reliability/types.d.ts +1 -1
  75. package/dist/rule-runner.js +1 -1
  76. package/dist/rule-runner.js.map +1 -1
  77. package/dist/rules/ai-governance-ai-content-live-region.d.ts +11 -0
  78. package/dist/rules/ai-governance-ai-content-live-region.js +304 -0
  79. package/dist/rules/ai-governance-ai-content-live-region.js.map +1 -0
  80. package/dist/rules/ai-governance-ai-loading-error-states.d.ts +9 -0
  81. package/dist/rules/ai-governance-ai-loading-error-states.js +214 -0
  82. package/dist/rules/ai-governance-ai-loading-error-states.js.map +1 -0
  83. package/dist/rules/ai-governance-ai-marker-anti-patterns.d.ts +15 -0
  84. package/dist/rules/ai-governance-ai-marker-anti-patterns.js +178 -0
  85. package/dist/rules/ai-governance-ai-marker-anti-patterns.js.map +1 -0
  86. package/dist/rules/ai-governance-ai-marker-component-present.d.ts +28 -0
  87. package/dist/rules/ai-governance-ai-marker-component-present.js +320 -0
  88. package/dist/rules/ai-governance-ai-marker-component-present.js.map +1 -0
  89. package/dist/rules/ai-governance-ai-token-requires-marker.d.ts +17 -0
  90. package/dist/rules/ai-governance-ai-token-requires-marker.js +206 -0
  91. package/dist/rules/ai-governance-ai-token-requires-marker.js.map +1 -0
  92. package/dist/rules/ai-governance-ai-tokens-reserved.d.ts +8 -0
  93. package/dist/rules/ai-governance-ai-tokens-reserved.js +85 -0
  94. package/dist/rules/ai-governance-ai-tokens-reserved.js.map +1 -0
  95. package/dist/rules/ai-governance-disclaimer-present.d.ts +18 -0
  96. package/dist/rules/ai-governance-disclaimer-present.js +210 -0
  97. package/dist/rules/ai-governance-disclaimer-present.js.map +1 -0
  98. package/dist/rules/ai-governance-explainability-affordance.d.ts +14 -0
  99. package/dist/rules/ai-governance-explainability-affordance.js +196 -0
  100. package/dist/rules/ai-governance-explainability-affordance.js.map +1 -0
  101. package/dist/rules/ai-governance-feedback-control-present.d.ts +19 -0
  102. package/dist/rules/ai-governance-feedback-control-present.js +223 -0
  103. package/dist/rules/ai-governance-feedback-control-present.js.map +1 -0
  104. package/dist/rules/ai-governance-human-control-affordances.d.ts +16 -0
  105. package/dist/rules/ai-governance-human-control-affordances.js +228 -0
  106. package/dist/rules/ai-governance-human-control-affordances.js.map +1 -0
  107. package/dist/rules/ai-governance-value-gate-doc-present.d.ts +18 -0
  108. package/dist/rules/ai-governance-value-gate-doc-present.js +206 -0
  109. package/dist/rules/ai-governance-value-gate-doc-present.js.map +1 -0
  110. package/dist/rules/ai-surface-agent-instruction-files.d.ts +33 -0
  111. package/dist/rules/ai-surface-agent-instruction-files.js +310 -0
  112. package/dist/rules/ai-surface-agent-instruction-files.js.map +1 -0
  113. package/dist/rules/ai-surface-llms-txt-structure.d.ts +18 -0
  114. package/dist/rules/ai-surface-llms-txt-structure.js +200 -0
  115. package/dist/rules/ai-surface-llms-txt-structure.js.map +1 -0
  116. package/dist/rules/ai-surface-mcp-config-present.d.ts +23 -0
  117. package/dist/rules/ai-surface-mcp-config-present.js +193 -0
  118. package/dist/rules/ai-surface-mcp-config-present.js.map +1 -0
  119. package/dist/rules/ai-surface-shadcn-registry-valid.d.ts +22 -0
  120. package/dist/rules/ai-surface-shadcn-registry-valid.js +247 -0
  121. package/dist/rules/ai-surface-shadcn-registry-valid.js.map +1 -0
  122. package/dist/rules/components-contracts-strictness.d.ts +48 -0
  123. package/dist/rules/components-contracts-strictness.js +372 -0
  124. package/dist/rules/components-contracts-strictness.js.map +1 -0
  125. package/dist/rules/registry.js +32 -0
  126. package/dist/rules/registry.js.map +1 -1
  127. package/dist/rules/tokens-dtcg-conformance.d.ts +55 -19
  128. package/dist/rules/tokens-dtcg-conformance.js +320 -82
  129. package/dist/rules/tokens-dtcg-conformance.js.map +1 -1
  130. package/dist/scorer.js +4 -3
  131. package/dist/scorer.js.map +1 -1
  132. package/dist/types.d.ts +2 -2
  133. package/dist/types.js.map +1 -1
  134. package/package.json +41 -16
  135. package/rules-manifest.json +431 -6
  136. package/schemas/v1/lyse-rules.json +1 -1
  137. package/dist/commands/ci-setup.d.ts +0 -9
  138. package/dist/commands/ci-setup.js +0 -42
  139. package/dist/commands/ci-setup.js.map +0 -1
  140. package/dist/commands/templates/lyse-workflow.yml.template +0 -30
@@ -0,0 +1,162 @@
1
+ import { DEFAULT_BUDGET_STATE_PATH, LLMBudget } from "../../reliability/llm-eval/budget.js";
2
+ import { DEFAULT_CACHE_DIR, ResponseCache } from "./cache.js";
3
+ import { NoopAdapter } from "./noop-adapter.js";
4
+ import { OpenAICompatibleAdapter } from "./openai-compatible-adapter.js";
5
+ import { AnthropicAdapter } from "./anthropic-adapter.js";
6
+ import { ConnectorNotImplementedError } from "./types.js";
7
+ class BudgetedCachedClient {
8
+ inner;
9
+ budget;
10
+ cache;
11
+ model;
12
+ constructor(inner, budget, cache, model) {
13
+ this.inner = inner;
14
+ this.budget = budget;
15
+ this.cache = cache;
16
+ this.model = model;
17
+ }
18
+ async complete(messages, opts) {
19
+ if (this.cache !== null) {
20
+ const cached = await this.cache.get(this.model, messages);
21
+ if (cached !== null)
22
+ return cached;
23
+ }
24
+ const estimate = opts?.estimateUsd ??
25
+ // Rough heuristic: total chars / 4 ≈ tokens, priced at conservative $0.015/1k input tokens.
26
+ // Upper-bound so the budget guard fires before a frontier call, not after.
27
+ (messages.reduce((sum, m) => sum + m.content.length, 0) / 4) * (0.015 / 1000);
28
+ if (!this.budget.canSpend(estimate)) {
29
+ return { text: "", usdSpent: 0, modelUsed: "none", llmQuality: "lower", cacheHit: false };
30
+ }
31
+ const result = await this.inner.complete(messages, opts);
32
+ if (result.usdSpent > 0) {
33
+ this.budget.record(result.usdSpent);
34
+ }
35
+ if (this.cache !== null) {
36
+ try {
37
+ await this.cache.set(this.model, messages, result);
38
+ }
39
+ catch {
40
+ // Non-fatal: cache write failed (e.g. disk full / permission error). Return the result anyway.
41
+ }
42
+ }
43
+ return result;
44
+ }
45
+ }
46
+ function buildCache(llm, opts, flags) {
47
+ if (flags?.noCache === true)
48
+ return null;
49
+ const maxAgeDays = llm?.cacheMaxAgeDays;
50
+ if (maxAgeDays === undefined || maxAgeDays <= 0)
51
+ return null;
52
+ return new ResponseCache({
53
+ cacheDir: opts.cacheDir ?? DEFAULT_CACHE_DIR,
54
+ maxAgeDays,
55
+ });
56
+ }
57
+ function buildBudget(llm, flags, opts) {
58
+ const dailyUsd = flags?.costCapUsd ?? llm?.costCapUsd ?? 50;
59
+ return new LLMBudget({
60
+ dailyUsd,
61
+ statePath: opts.budgetStatePath ?? DEFAULT_BUDGET_STATE_PATH,
62
+ });
63
+ }
64
+ export function resolveConnector(config, flags, opts = {}) {
65
+ const llm = config.llm;
66
+ if (flags?.staticOnly === true)
67
+ return new NoopAdapter();
68
+ if (llm?.staticOnly === true)
69
+ return new NoopAdapter();
70
+ if (!llm?.provider && !llm?.connector)
71
+ return new NoopAdapter();
72
+ const budget = buildBudget(llm, flags, opts);
73
+ const cache = buildCache(llm, opts, flags);
74
+ const provider = flags?.llmProvider ?? llm?.provider;
75
+ const connector = llm?.connector;
76
+ const model = flags?.llmModel ?? llm?.model;
77
+ if (connector === "mcp-host") {
78
+ const mcpStub = {
79
+ async complete(_messages, _opts) {
80
+ throw new ConnectorNotImplementedError("mcp-host");
81
+ },
82
+ };
83
+ return mcpStub;
84
+ }
85
+ if (connector === "ollama" ||
86
+ (provider === "openai-compatible" && (llm?.endpoint?.includes("localhost") ?? false))) {
87
+ const ollamaModel = model ?? "llama3.2";
88
+ const adapter = new OpenAICompatibleAdapter({
89
+ apiKey: "",
90
+ model: ollamaModel,
91
+ baseURL: llm?.endpoint ?? "http://localhost:11434/v1",
92
+ ...(opts.fetchFn !== undefined && { fetchFn: opts.fetchFn }),
93
+ });
94
+ return new BudgetedCachedClient(adapter, budget, cache, ollamaModel);
95
+ }
96
+ if (connector === "openrouter" ||
97
+ (provider === "openai-compatible" && (llm?.endpoint?.includes("openrouter.ai") ?? false))) {
98
+ const apiKey = process.env["OPENROUTER_API_KEY"] ?? "";
99
+ const orModel = model ?? "openai/gpt-4o-mini";
100
+ const adapter = new OpenAICompatibleAdapter({
101
+ apiKey,
102
+ model: orModel,
103
+ baseURL: llm?.endpoint ?? "https://openrouter.ai/api/v1",
104
+ ...(opts.fetchFn !== undefined && { fetchFn: opts.fetchFn }),
105
+ });
106
+ return new BudgetedCachedClient(adapter, budget, cache, orModel);
107
+ }
108
+ if (provider === "openai" || (connector === "direct-api-key" && provider === "openai")) {
109
+ const apiKey = process.env["OPENAI_API_KEY"] ?? "";
110
+ const oaiModel = model ?? "gpt-4o-mini";
111
+ const adapter = new OpenAICompatibleAdapter({
112
+ apiKey,
113
+ model: oaiModel,
114
+ baseURL: llm?.endpoint ?? "https://api.openai.com/v1",
115
+ ...(opts.fetchFn !== undefined && { fetchFn: opts.fetchFn }),
116
+ });
117
+ return new BudgetedCachedClient(adapter, budget, cache, oaiModel);
118
+ }
119
+ if (provider === "anthropic" || (connector === "direct-api-key" && provider === "anthropic")) {
120
+ const apiKey = process.env["ANTHROPIC_API_KEY"] ?? "";
121
+ const claudeModel = model ?? "claude-sonnet-4-6";
122
+ const adapter = new AnthropicAdapter({
123
+ apiKey,
124
+ model: claudeModel,
125
+ ...(opts.fetchFn !== undefined && { fetchFn: opts.fetchFn }),
126
+ });
127
+ return new BudgetedCachedClient(adapter, budget, cache, claudeModel);
128
+ }
129
+ if (provider === "auto") {
130
+ const anthropicKey = process.env["ANTHROPIC_API_KEY"];
131
+ if (anthropicKey) {
132
+ const claudeModel = model ?? "claude-sonnet-4-6";
133
+ const adapter = new AnthropicAdapter({
134
+ apiKey: anthropicKey,
135
+ model: claudeModel,
136
+ ...(opts.fetchFn !== undefined && { fetchFn: opts.fetchFn }),
137
+ });
138
+ return new BudgetedCachedClient(adapter, budget, cache, claudeModel);
139
+ }
140
+ const openaiKey = process.env["OPENAI_API_KEY"];
141
+ if (openaiKey) {
142
+ const oaiModel = model ?? "gpt-4o-mini";
143
+ const adapter = new OpenAICompatibleAdapter({
144
+ apiKey: openaiKey,
145
+ model: oaiModel,
146
+ baseURL: "https://api.openai.com/v1",
147
+ ...(opts.fetchFn !== undefined && { fetchFn: opts.fetchFn }),
148
+ });
149
+ return new BudgetedCachedClient(adapter, budget, cache, oaiModel);
150
+ }
151
+ return new NoopAdapter();
152
+ }
153
+ if (provider === "mcp") {
154
+ return {
155
+ async complete(_messages, _opts) {
156
+ throw new ConnectorNotImplementedError("mcp");
157
+ },
158
+ };
159
+ }
160
+ return new NoopAdapter();
161
+ }
162
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../src/llm/connectors/resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export interface ChatMessage {
2
+ role: "system" | "user" | "assistant";
3
+ content: string;
4
+ }
5
+ export interface CompleteOptions {
6
+ estimateUsd?: number;
7
+ }
8
+ export interface ConnectorResult {
9
+ text: string;
10
+ usdSpent: number;
11
+ modelUsed: string;
12
+ llmQuality: "higher" | "lower";
13
+ cacheHit: boolean;
14
+ }
15
+ export interface ConnectorClient {
16
+ complete(messages: ChatMessage[], opts?: CompleteOptions): Promise<ConnectorResult>;
17
+ }
18
+ export declare class ConnectorNotImplementedError extends Error {
19
+ name: string;
20
+ constructor(connector: string);
21
+ }
22
+ export declare class ConnectorBudgetExceededError extends Error {
23
+ name: string;
24
+ constructor(estimateUsd: number, dailyCap: number);
25
+ }
@@ -0,0 +1,13 @@
1
+ export class ConnectorNotImplementedError extends Error {
2
+ name = "ConnectorNotImplementedError";
3
+ constructor(connector) {
4
+ super(`Connector "${connector}" is not implemented in this version of Lyse.`);
5
+ }
6
+ }
7
+ export class ConnectorBudgetExceededError extends Error {
8
+ name = "ConnectorBudgetExceededError";
9
+ constructor(estimateUsd, dailyCap) {
10
+ super(`LLM call refused: estimated cost $${estimateUsd.toFixed(4)} would exceed daily cap $${dailyCap.toFixed(2)}.`);
11
+ }
12
+ }
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/llm/connectors/types.ts"],"names":[],"mappings":"AAqBA,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAC5C,IAAI,GAAG,8BAA8B,CAAC;IAC/C,YAAY,SAAiB;QAC3B,KAAK,CAAC,cAAc,SAAS,+CAA+C,CAAC,CAAC;IAChF,CAAC;CACF;AAED,MAAM,OAAO,4BAA6B,SAAQ,KAAK;IAC5C,IAAI,GAAG,8BAA8B,CAAC;IAC/C,YAAY,WAAmB,EAAE,QAAgB;QAC/C,KAAK,CACH,qCAAqC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,4BAA4B,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAC9G,CAAC;IACJ,CAAC;CACF"}
@@ -1,5 +1,7 @@
1
1
  import type { Finding, Layer4Meta, LyseConfig } from "../types.js";
2
2
  import type { AuditFlags } from "../commands/audit-flags.js";
3
+ import type { ConnectorClient } from "./connectors/types.js";
4
+ import type { RubricDimension } from "./rubric-stub.js";
3
5
  export interface Layer4StageInput {
4
6
  repoRoot: string;
5
7
  config: LyseConfig;
@@ -10,9 +12,9 @@ export interface Layer4StageResult {
10
12
  augmentedFindings: Finding[];
11
13
  meta: Layer4Meta;
12
14
  }
13
- /**
14
- * v0.1 stub — LLM Layer 4 was descoped in the Phase 1 reset (#106); the default
15
- * audit path is static-only. The prior implementation is in git history if a
16
- * future release revisits LLM augmentation.
17
- */
18
- export declare function runLayer4Stage(_input: Layer4StageInput): Promise<Layer4StageResult>;
15
+ export interface Layer4StageOptions {
16
+ connector?: ConnectorClient;
17
+ rubricDimensions?: RubricDimension[];
18
+ timeoutMs?: number;
19
+ }
20
+ export declare function runLayer4Stage(input: Layer4StageInput, opts?: Layer4StageOptions): Promise<Layer4StageResult>;
@@ -1,12 +1,148 @@
1
- /**
2
- * v0.1 stub LLM Layer 4 was descoped in the Phase 1 reset (#106); the default
3
- * audit path is static-only. The prior implementation is in git history if a
4
- * future release revisits LLM augmentation.
5
- */
6
- export async function runLayer4Stage(_input) {
7
- return {
8
- augmentedFindings: [],
9
- meta: { staticOnly: true },
1
+ import { resolveConnector } from "./connectors/resolver.js";
2
+ import { getStubRubricDimensions } from "./rubric-stub.js";
3
+ import { validateProposedFindings } from "./validator.js";
4
+ const VALID_SEVERITIES = new Set(["error", "warning", "info"]);
5
+ const VALID_AXES = new Set([
6
+ "tokens",
7
+ "a11y",
8
+ "components",
9
+ "stories",
10
+ "ai-surface",
11
+ "ai-governance",
12
+ ]);
13
+ const DEFAULT_TIMEOUT_MS = 60_000;
14
+ const MAX_LLM_FINDINGS = 100;
15
+ function extractJson(text) {
16
+ const jsonFenced = text.match(/```json\s*([\s\S]*?)```/);
17
+ if (jsonFenced && jsonFenced[1])
18
+ return JSON.parse(jsonFenced[1].trim());
19
+ const anyFenced = text.match(/```\s*([\s\S]*?)```/);
20
+ if (anyFenced && anyFenced[1])
21
+ return JSON.parse(anyFenced[1].trim());
22
+ return JSON.parse(text);
23
+ }
24
+ function isProposedFinding(v) {
25
+ if (typeof v !== "object" || v === null)
26
+ return false;
27
+ const o = v;
28
+ if (typeof o["ruleId"] !== "string" || o["ruleId"] === "")
29
+ return false;
30
+ if (typeof o["axis"] !== "string" || !VALID_AXES.has(o["axis"]))
31
+ return false;
32
+ if (typeof o["severity"] !== "string" || !VALID_SEVERITIES.has(o["severity"]))
33
+ return false;
34
+ if (typeof o["file"] !== "string" || o["file"] === "" || o["file"].includes("\0"))
35
+ return false;
36
+ if (typeof o["line"] !== "number" || !Number.isFinite(o["line"]) || o["line"] < 1)
37
+ return false;
38
+ if (typeof o["column"] !== "number" || !Number.isFinite(o["column"]) || o["column"] < 1)
39
+ return false;
40
+ if (typeof o["snippet"] !== "string")
41
+ return false;
42
+ if (typeof o["message"] !== "string" || o["message"] === "")
43
+ return false;
44
+ return true;
45
+ }
46
+ function dedupAgainstStatic(proposed, staticFindings) {
47
+ const seen = new Set();
48
+ for (const f of staticFindings) {
49
+ seen.add(`${f.ruleId}|${f.location.file}|${f.location.line}`);
50
+ }
51
+ return proposed.filter((p) => !seen.has(`${p.ruleId}|${p.file}|${p.line}`));
52
+ }
53
+ function buildPrompt(dimensions, staticFindings) {
54
+ const dimSection = dimensions
55
+ .map((d, i) => `${i + 1}. [${d.key}] ${d.prompt}`)
56
+ .join("\n");
57
+ const staticSummary = staticFindings.length === 0
58
+ ? "No static findings."
59
+ : staticFindings
60
+ .slice(0, 20)
61
+ .map((f) => `- ${f.ruleId} @ ${f.location.file}:${f.location.line} — ${f.message}`)
62
+ .join("\n");
63
+ return [
64
+ "You are a design-system governance auditor. Analyze the following dimensions and identify violations.",
65
+ "",
66
+ "## Dimensions to audit",
67
+ dimSection,
68
+ "",
69
+ "## Static findings context (already detected, do not repeat)",
70
+ staticSummary,
71
+ "",
72
+ "## Output format",
73
+ "Return ONLY valid JSON (no markdown, no explanation) matching:",
74
+ '{ "findings": [ { "ruleId": string, "axis": string, "severity": "error"|"warning"|"info", "file": string, "line": number, "column": number, "snippet": string, "message": string } ] }',
75
+ "",
76
+ "CRITICAL: Every finding MUST cite a real file path and an exact code snippet (at least 20 chars) from that file.",
77
+ 'If you have no findings, return: { "findings": [] }',
78
+ ].join("\n");
79
+ }
80
+ function withTimeout(p, ms) {
81
+ return Promise.race([
82
+ p,
83
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`LLM connector timeout after ${ms}ms`)), ms)),
84
+ ]);
85
+ }
86
+ export async function runLayer4Stage(input, opts = {}) {
87
+ const staticOnly = input.flags?.staticOnly === true || input.config.llm?.staticOnly === true;
88
+ if (staticOnly) {
89
+ return { augmentedFindings: [], meta: { staticOnly: true } };
90
+ }
91
+ const dimensions = opts.rubricDimensions ?? getStubRubricDimensions();
92
+ if (dimensions.length === 0) {
93
+ return { augmentedFindings: [], meta: {} };
94
+ }
95
+ const connector = opts.connector ?? resolveConnector(input.config, input.flags);
96
+ const prompt = buildPrompt(dimensions, input.staticFindings);
97
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
98
+ let connectorResult;
99
+ try {
100
+ connectorResult = await withTimeout(connector.complete([{ role: "user", content: prompt }]), timeoutMs);
101
+ }
102
+ catch (err) {
103
+ const message = err instanceof Error ? err.message : String(err);
104
+ return {
105
+ augmentedFindings: [],
106
+ meta: { error: { kind: "ConnectorError", message } },
107
+ };
108
+ }
109
+ if (connectorResult.text === "") {
110
+ return { augmentedFindings: [], meta: {} };
111
+ }
112
+ let parsed;
113
+ try {
114
+ const raw = extractJson(connectorResult.text);
115
+ if (typeof raw !== "object" ||
116
+ raw === null ||
117
+ !Array.isArray(raw["findings"])) {
118
+ throw new Error("Response missing 'findings' array");
119
+ }
120
+ parsed = raw;
121
+ }
122
+ catch (err) {
123
+ const message = err instanceof Error ? err.message : String(err);
124
+ return {
125
+ augmentedFindings: [],
126
+ meta: {
127
+ modelUsed: connectorResult.modelUsed,
128
+ usdSpent: connectorResult.usdSpent,
129
+ llmQuality: connectorResult.llmQuality,
130
+ cacheHit: connectorResult.cacheHit,
131
+ error: { kind: "ParseError", message },
132
+ },
133
+ };
134
+ }
135
+ const cappedRaw = parsed.findings.slice(0, MAX_LLM_FINDINGS);
136
+ const proposed = cappedRaw.filter(isProposedFinding);
137
+ const deduped = dedupAgainstStatic(proposed, input.staticFindings);
138
+ const { findings, droppedHallucinations } = await validateProposedFindings(deduped, input.repoRoot);
139
+ const meta = {
140
+ modelUsed: connectorResult.modelUsed,
141
+ usdSpent: connectorResult.usdSpent,
142
+ llmQuality: connectorResult.llmQuality,
143
+ cacheHit: connectorResult.cacheHit,
144
+ droppedHallucinations,
10
145
  };
146
+ return { augmentedFindings: findings, meta };
11
147
  }
12
148
  //# sourceMappingURL=layer4-stage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layer4-stage.js","sourceRoot":"","sources":["../../src/llm/layer4-stage.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAwB;IAC3D,OAAO;QACL,iBAAiB,EAAE,EAAE;QACrB,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;KAC3B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"layer4-stage.js","sourceRoot":"","sources":["../../src/llm/layer4-stage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { AxisName, RuleId } from "../types.js";
2
+ export interface RubricDimension {
3
+ key: string;
4
+ axis: AxisName;
5
+ ruleId: RuleId;
6
+ prompt: string;
7
+ }
8
+ export declare function getStubRubricDimensions(): RubricDimension[];
@@ -0,0 +1,4 @@
1
+ export function getStubRubricDimensions() {
2
+ return [];
3
+ }
4
+ //# sourceMappingURL=rubric-stub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rubric-stub.js","sourceRoot":"","sources":["../../src/llm/rubric-stub.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,uBAAuB;IACrC,OAAO,EAAE,CAAC;AACZ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { AxisName, Finding, Severity } from "../types.js";
2
+ import type { RuleId } from "../types.js";
3
+ export interface ProposedFinding {
4
+ ruleId: RuleId;
5
+ axis: AxisName;
6
+ severity: Severity;
7
+ file: string;
8
+ line: number;
9
+ column: number;
10
+ snippet: string;
11
+ message: string;
12
+ suggestion?: string;
13
+ }
14
+ export interface ValidationResult {
15
+ findings: Finding[];
16
+ droppedHallucinations: number;
17
+ }
18
+ export declare function validateProposedFindings(proposed: ProposedFinding[], repoRoot: string): Promise<ValidationResult>;
@@ -0,0 +1,81 @@
1
+ import { readFile, realpath } from "node:fs/promises";
2
+ import { join, sep } from "node:path";
3
+ import { ruleMap } from "../rules/registry.js";
4
+ const MIN_SNIPPET_LENGTH = 10;
5
+ const LINE_PROXIMITY_TOLERANCE = 5;
6
+ export async function validateProposedFindings(proposed, repoRoot) {
7
+ const findings = [];
8
+ let droppedHallucinations = 0;
9
+ let resolvedRoot;
10
+ try {
11
+ resolvedRoot = await realpath(repoRoot);
12
+ }
13
+ catch {
14
+ return { findings: [], droppedHallucinations: proposed.length };
15
+ }
16
+ for (const p of proposed) {
17
+ if (!ruleMap.has(p.ruleId)) {
18
+ droppedHallucinations++;
19
+ continue;
20
+ }
21
+ if (p.snippet.length < MIN_SNIPPET_LENGTH) {
22
+ droppedHallucinations++;
23
+ continue;
24
+ }
25
+ const absPath = join(resolvedRoot, p.file);
26
+ if (!absPath.startsWith(resolvedRoot + sep) && absPath !== resolvedRoot) {
27
+ droppedHallucinations++;
28
+ continue;
29
+ }
30
+ // Defense in depth against symlinks pointing outside the repo
31
+ let realAbs;
32
+ try {
33
+ realAbs = await realpath(absPath);
34
+ }
35
+ catch {
36
+ droppedHallucinations++;
37
+ continue;
38
+ }
39
+ if (!realAbs.startsWith(resolvedRoot + sep) && realAbs !== resolvedRoot) {
40
+ droppedHallucinations++;
41
+ continue;
42
+ }
43
+ let content;
44
+ try {
45
+ content = await readFile(realAbs, "utf8");
46
+ }
47
+ catch {
48
+ droppedHallucinations++;
49
+ continue;
50
+ }
51
+ const lines = content.split("\n");
52
+ const targetLine = Math.max(1, Math.min(p.line, lines.length));
53
+ const windowStart = Math.max(0, targetLine - 1 - LINE_PROXIMITY_TOLERANCE);
54
+ const windowEnd = Math.min(lines.length, targetLine - 1 + LINE_PROXIMITY_TOLERANCE + 1);
55
+ const windowContent = lines.slice(windowStart, windowEnd).join("\n");
56
+ if (!windowContent.includes(p.snippet)) {
57
+ droppedHallucinations++;
58
+ continue;
59
+ }
60
+ const finding = {
61
+ ruleId: p.ruleId,
62
+ axis: p.axis,
63
+ severity: p.severity,
64
+ location: { file: p.file, line: p.line, column: p.column },
65
+ message: p.message,
66
+ ...(p.suggestion !== undefined && { suggestion: p.suggestion }),
67
+ };
68
+ findings.push(finding);
69
+ }
70
+ findings.sort((a, b) => {
71
+ const fa = a.location.file;
72
+ const fb = b.location.file;
73
+ if (fa !== fb)
74
+ return fa < fb ? -1 : 1;
75
+ if (a.location.line !== b.location.line)
76
+ return a.location.line - b.location.line;
77
+ return a.ruleId < b.ruleId ? -1 : a.ruleId > b.ruleId ? 1 : 0;
78
+ });
79
+ return { findings, droppedHallucinations };
80
+ }
81
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/llm/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare function isReservedTokenName(rawName: string): boolean;
2
+ declare function collectJsonLeafPaths(node: unknown, prefix: string, out: Set<string>): void;
3
+ declare function extractCssCustomPropertyNames(source: string): string[];
4
+ export declare function detectReservedAiTokens(repoRoot: string): string[];
5
+ export declare const _internal: {
6
+ isReservedTokenName: typeof isReservedTokenName;
7
+ collectJsonLeafPaths: typeof collectJsonLeafPaths;
8
+ extractCssCustomPropertyNames: typeof extractCssCustomPropertyNames;
9
+ };
10
+ export {};