@pretorian-worx/runclaudia-core 0.12.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +16 -0
  3. package/dist/adapters/nextjs.d.ts +27 -0
  4. package/dist/adapters/nextjs.js +457 -0
  5. package/dist/adapters/nextjs.js.map +1 -0
  6. package/dist/adapters/prisma.d.ts +22 -0
  7. package/dist/adapters/prisma.js +80 -0
  8. package/dist/adapters/prisma.js.map +1 -0
  9. package/dist/adapters/specs.d.ts +13 -0
  10. package/dist/adapters/specs.js +126 -0
  11. package/dist/adapters/specs.js.map +1 -0
  12. package/dist/adapters/terraform.d.ts +17 -0
  13. package/dist/adapters/terraform.js +82 -0
  14. package/dist/adapters/terraform.js.map +1 -0
  15. package/dist/diff.d.ts +13 -0
  16. package/dist/diff.js +187 -0
  17. package/dist/diff.js.map +1 -0
  18. package/dist/gating.d.ts +29 -0
  19. package/dist/gating.js +59 -0
  20. package/dist/gating.js.map +1 -0
  21. package/dist/index.d.ts +15 -0
  22. package/dist/index.js +12 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/llm.d.ts +24 -0
  25. package/dist/llm.js +135 -0
  26. package/dist/llm.js.map +1 -0
  27. package/dist/map.d.ts +9 -0
  28. package/dist/map.js +55 -0
  29. package/dist/map.js.map +1 -0
  30. package/dist/plan-format.d.ts +3 -0
  31. package/dist/plan-format.js +71 -0
  32. package/dist/plan-format.js.map +1 -0
  33. package/dist/plan.d.ts +21 -0
  34. package/dist/plan.js +44 -0
  35. package/dist/plan.js.map +1 -0
  36. package/dist/prompt.d.ts +40 -0
  37. package/dist/prompt.js +266 -0
  38. package/dist/prompt.js.map +1 -0
  39. package/dist/runner.d.ts +53 -0
  40. package/dist/runner.js +119 -0
  41. package/dist/runner.js.map +1 -0
  42. package/dist/select.d.ts +31 -0
  43. package/dist/select.js +108 -0
  44. package/dist/select.js.map +1 -0
  45. package/dist/types.d.ts +176 -0
  46. package/dist/types.js +17 -0
  47. package/dist/types.js.map +1 -0
  48. package/package.json +54 -0
@@ -0,0 +1,13 @@
1
+ import type { SpecEntry } from "../types.js";
2
+ export interface SpecDiscoveryOptions {
3
+ rootDir: string;
4
+ maxDepth?: number;
5
+ }
6
+ /**
7
+ * Walk known E2E test directories and parse each spec file. Returns the per-test
8
+ * coverage map plus a reverse index.
9
+ */
10
+ export declare function discoverSpecs(opts: SpecDiscoveryOptions): {
11
+ specs: SpecEntry[];
12
+ fileToSpecs: Record<string, string[]>;
13
+ };
@@ -0,0 +1,126 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { join, relative, sep } from "node:path";
3
+ // Conventional E2E/integration locations. We intentionally don't index unit
4
+ // tests (e.g. *.test.ts next to source files) — they don't carry route or
5
+ // endpoint coverage information that's useful here.
6
+ const SPEC_DIRS = [
7
+ "e2e",
8
+ "tests/e2e",
9
+ "test/e2e",
10
+ "playwright",
11
+ "playwright/tests",
12
+ "cypress/e2e",
13
+ "cypress/integration",
14
+ ];
15
+ const SPEC_FILE_RE = /\.(?:spec|test|cy)\.(?:[jt]sx?)$/i;
16
+ const TEST_BLOCK_RE = /\b(?:test|it)(?:\.\w+)?\s*\(\s*['"`]([^'"`\n]+)['"`]/g;
17
+ const PAGE_GOTO_RE = /\bpage\s*\.\s*goto\s*\(\s*['"`]([^'"`\n]+)['"`]/g;
18
+ const PAGE_REQUEST_RE = /\bpage\s*\.\s*request\s*\.\s*(get|post|put|patch|delete|head|options)\s*\(\s*['"`]([^'"`\n]+)['"`]/gi;
19
+ const CY_VISIT_RE = /\bcy\s*\.\s*visit\s*\(\s*['"`]([^'"`\n]+)['"`]/g;
20
+ const CY_REQUEST_RE = /\bcy\s*\.\s*request\s*\(\s*['"`](GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)['"`]\s*,\s*['"`]([^'"`\n]+)['"`]/gi;
21
+ const CY_REQUEST_OBJ_RE = /\bcy\s*\.\s*request\s*\(\s*\{\s*method\s*:\s*['"`](GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)['"`]\s*,\s*url\s*:\s*['"`]([^'"`\n]+)['"`]/gi;
22
+ const FLOW_ANNOTATION_RE = /\/\/\s*@claudia\s+flow\s*:\s*([^\n]+)/gi;
23
+ const SHARED_SETUP_RE = /\b(?:beforeAll|before)\s*\(/;
24
+ /**
25
+ * Walk known E2E test directories and parse each spec file. Returns the per-test
26
+ * coverage map plus a reverse index.
27
+ */
28
+ export function discoverSpecs(opts) {
29
+ const { rootDir } = opts;
30
+ const maxDepth = opts.maxDepth ?? 8;
31
+ const specs = [];
32
+ const fileToSpecs = {};
33
+ for (const candidate of SPEC_DIRS) {
34
+ const abs = join(rootDir, candidate);
35
+ if (!existsSync(abs))
36
+ continue;
37
+ if (!statSync(abs).isDirectory())
38
+ continue;
39
+ walkSpecDir(abs, rootDir, maxDepth, specs, fileToSpecs);
40
+ }
41
+ specs.sort((a, b) => (a.file === b.file ? a.name.localeCompare(b.name) : a.file.localeCompare(b.file)));
42
+ for (const k of Object.keys(fileToSpecs)) {
43
+ fileToSpecs[k] = Array.from(new Set(fileToSpecs[k])).sort();
44
+ }
45
+ return { specs, fileToSpecs };
46
+ }
47
+ function walkSpecDir(dir, rootDir, depthLeft, specs, fileToSpecs) {
48
+ if (depthLeft <= 0)
49
+ return;
50
+ let entries;
51
+ try {
52
+ entries = readdirSync(dir, { withFileTypes: true });
53
+ }
54
+ catch {
55
+ return;
56
+ }
57
+ for (const entry of entries) {
58
+ if (entry.name.startsWith("."))
59
+ continue;
60
+ if (entry.name === "node_modules")
61
+ continue;
62
+ const abs = join(dir, entry.name);
63
+ if (entry.isDirectory()) {
64
+ walkSpecDir(abs, rootDir, depthLeft - 1, specs, fileToSpecs);
65
+ continue;
66
+ }
67
+ if (!entry.isFile())
68
+ continue;
69
+ if (!SPEC_FILE_RE.test(entry.name))
70
+ continue;
71
+ parseSpecFile(abs, rootDir, specs, fileToSpecs);
72
+ }
73
+ }
74
+ function parseSpecFile(abs, rootDir, specs, fileToSpecs) {
75
+ let src;
76
+ try {
77
+ src = readFileSync(abs, "utf8");
78
+ }
79
+ catch {
80
+ return;
81
+ }
82
+ const relFile = relative(rootDir, abs).split(sep).join("/");
83
+ const framework = inferFramework(src, relFile);
84
+ const hasSharedSetup = SHARED_SETUP_RE.test(src);
85
+ const routesCovered = uniqSorted(matchAll(PAGE_GOTO_RE, src, (m) => m[1]).concat(matchAll(CY_VISIT_RE, src, (m) => m[1])));
86
+ const endpointsCovered = uniqSorted(matchAll(PAGE_REQUEST_RE, src, (m) => `${m[1].toUpperCase()} ${m[2]}`)
87
+ .concat(matchAll(CY_REQUEST_RE, src, (m) => `${m[1].toUpperCase()} ${m[2]}`))
88
+ .concat(matchAll(CY_REQUEST_OBJ_RE, src, (m) => `${m[1].toUpperCase()} ${m[2]}`)));
89
+ const flowAnnotations = uniqSorted(matchAll(FLOW_ANNOTATION_RE, src, (m) => m[1].trim()));
90
+ const testNames = matchAll(TEST_BLOCK_RE, src, (m) => m[1]);
91
+ // Filter out files that look like specs by name but have no actual `test()` /
92
+ // `it()` blocks (e.g. helper modules in an e2e folder).
93
+ if (testNames.length === 0)
94
+ return;
95
+ for (const name of testNames) {
96
+ specs.push({
97
+ framework,
98
+ file: relFile,
99
+ name,
100
+ routesCovered,
101
+ endpointsCovered,
102
+ hasSharedSetup,
103
+ flowAnnotations,
104
+ });
105
+ (fileToSpecs[relFile] ??= []).push(name);
106
+ }
107
+ }
108
+ function inferFramework(src, file) {
109
+ if (file.includes("cypress/") || file.endsWith(".cy.ts") || file.endsWith(".cy.js"))
110
+ return "cypress";
111
+ if (/\bcy\s*\./.test(src) && !/from\s+['"]@playwright/.test(src))
112
+ return "cypress";
113
+ return "playwright";
114
+ }
115
+ function matchAll(re, src, take) {
116
+ const out = [];
117
+ re.lastIndex = 0;
118
+ let m;
119
+ while ((m = re.exec(src)))
120
+ out.push(take(m));
121
+ return out;
122
+ }
123
+ function uniqSorted(xs) {
124
+ return Array.from(new Set(xs)).sort();
125
+ }
126
+ //# sourceMappingURL=specs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs.js","sourceRoot":"","sources":["../../src/adapters/specs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { InfraEntry } from "../types.js";
2
+ export interface TerraformDiscoveryOptions {
3
+ rootDir: string;
4
+ /** Directories to ignore while walking. */
5
+ ignore?: string[];
6
+ /** Max recursion depth. */
7
+ maxDepth?: number;
8
+ }
9
+ /**
10
+ * Walk the project root looking for *.tf files and extract resource declarations.
11
+ * Regex-based — does not parse HCL fully. Handles 95% of vanilla resource blocks;
12
+ * misses dynamic blocks, for_each, count, modules, locals, etc.
13
+ */
14
+ export declare function discoverTerraformResources(opts: TerraformDiscoveryOptions): {
15
+ infra: InfraEntry[];
16
+ fileToInfra: Record<string, string[]>;
17
+ };
@@ -0,0 +1,82 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { join, relative, sep } from "node:path";
3
+ const DEFAULT_IGNORE = new Set([
4
+ "node_modules",
5
+ ".git",
6
+ ".next",
7
+ "dist",
8
+ "build",
9
+ ".terraform",
10
+ ".turbo",
11
+ "coverage",
12
+ ]);
13
+ // `resource "aws_s3_bucket" "attachments" {` (block opener; we don't parse the body).
14
+ const RESOURCE_RE = /resource\s+"([^"]+)"\s+"([^"]+)"\s*\{/g;
15
+ /**
16
+ * Walk the project root looking for *.tf files and extract resource declarations.
17
+ * Regex-based — does not parse HCL fully. Handles 95% of vanilla resource blocks;
18
+ * misses dynamic blocks, for_each, count, modules, locals, etc.
19
+ */
20
+ export function discoverTerraformResources(opts) {
21
+ const rootDir = opts.rootDir;
22
+ const ignore = new Set([...DEFAULT_IGNORE, ...(opts.ignore ?? [])]);
23
+ const maxDepth = opts.maxDepth ?? 8;
24
+ const infra = [];
25
+ const fileToInfra = {};
26
+ walk(rootDir, rootDir, ignore, maxDepth, infra, fileToInfra);
27
+ // Stable order — easier to diff cached maps + nicer prompt output.
28
+ infra.sort((a, b) => a.address.localeCompare(b.address));
29
+ for (const k of Object.keys(fileToInfra)) {
30
+ fileToInfra[k] = Array.from(new Set(fileToInfra[k])).sort();
31
+ }
32
+ return { infra, fileToInfra };
33
+ }
34
+ function walk(dir, rootDir, ignore, depthLeft, infra, fileToInfra) {
35
+ if (depthLeft <= 0)
36
+ return;
37
+ let entries;
38
+ try {
39
+ entries = readdirSync(dir, { withFileTypes: true });
40
+ }
41
+ catch {
42
+ return;
43
+ }
44
+ for (const entry of entries) {
45
+ // Skip dotfiles/dirs entirely. .terraform/.git/etc. are also in DEFAULT_IGNORE
46
+ // but this catches anything else (.idea, .vscode, etc.).
47
+ if (entry.name.startsWith("."))
48
+ continue;
49
+ const abs = join(dir, entry.name);
50
+ if (entry.isDirectory()) {
51
+ if (ignore.has(entry.name))
52
+ continue;
53
+ walk(abs, rootDir, ignore, depthLeft - 1, infra, fileToInfra);
54
+ continue;
55
+ }
56
+ if (!entry.isFile())
57
+ continue;
58
+ if (!entry.name.endsWith(".tf"))
59
+ continue;
60
+ const relFile = toRel(rootDir, abs);
61
+ let src;
62
+ try {
63
+ src = readFileSync(abs, "utf8");
64
+ }
65
+ catch {
66
+ continue;
67
+ }
68
+ RESOURCE_RE.lastIndex = 0;
69
+ let m;
70
+ while ((m = RESOURCE_RE.exec(src))) {
71
+ const type = m[1];
72
+ const name = m[2];
73
+ const address = `${type}.${name}`;
74
+ infra.push({ tool: "terraform", type, name, address, file: relFile });
75
+ (fileToInfra[relFile] ??= []).push(address);
76
+ }
77
+ }
78
+ }
79
+ function toRel(rootDir, p) {
80
+ return relative(rootDir, p).split(sep).join("/");
81
+ }
82
+ //# sourceMappingURL=terraform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terraform.js","sourceRoot":"","sources":["../../src/adapters/terraform.ts"],"names":[],"mappings":"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"}
package/dist/diff.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import type { Diff } from "./types.js";
2
+ export interface DiffOptions {
3
+ base: string;
4
+ head: string;
5
+ cwd?: string;
6
+ }
7
+ export declare function readDiff(opts: DiffOptions): Diff;
8
+ interface SkipDecision {
9
+ skip: boolean;
10
+ reason?: string;
11
+ }
12
+ export declare function classifySkip(diff: Diff): SkipDecision;
13
+ export {};
package/dist/diff.js ADDED
@@ -0,0 +1,187 @@
1
+ import { execFileSync } from "node:child_process";
2
+ export function readDiff(opts) {
3
+ const { base, head, cwd } = opts;
4
+ const range = `${base}..${head}`;
5
+ const numstat = run(["git", "diff", "--numstat", "-z", range], cwd);
6
+ const nameStatus = run(["git", "diff", "--name-status", "-z", range], cwd);
7
+ const patch = run(["git", "diff", "--unified=3", range], cwd);
8
+ const statusByPath = parseNameStatus(nameStatus);
9
+ const numByPath = parseNumstat(numstat);
10
+ const hunksByPath = parsePatch(patch);
11
+ const paths = new Set([
12
+ ...Object.keys(statusByPath),
13
+ ...Object.keys(numByPath),
14
+ ...Object.keys(hunksByPath),
15
+ ]);
16
+ const files = [];
17
+ for (const path of paths) {
18
+ const s = statusByPath[path];
19
+ const n = numByPath[path];
20
+ files.push({
21
+ path,
22
+ status: s?.status ?? "modified",
23
+ oldPath: s?.oldPath,
24
+ additions: n?.additions ?? 0,
25
+ deletions: n?.deletions ?? 0,
26
+ binary: n?.binary ?? false,
27
+ hunks: hunksByPath[path] ?? [],
28
+ });
29
+ }
30
+ files.sort((a, b) => a.path.localeCompare(b.path));
31
+ return { base, head, files };
32
+ }
33
+ const DOC_PATTERNS = [/\.md$/i, /\.mdx$/i, /^docs\//i, /^README/i, /^CHANGELOG/i, /^LICENSE/i];
34
+ const LOCK_PATTERNS = [
35
+ /^pnpm-lock\.yaml$/,
36
+ /^package-lock\.json$/,
37
+ /^yarn\.lock$/,
38
+ /^bun\.lockb?$/,
39
+ /^Cargo\.lock$/,
40
+ /^poetry\.lock$/,
41
+ /^Pipfile\.lock$/,
42
+ ];
43
+ const CI_PATTERNS = [/^\.github\//, /^\.circleci\//, /^\.gitlab-ci\.yml$/];
44
+ const TEST_PATTERNS = [
45
+ /\.test\.[tj]sx?$/,
46
+ /\.spec\.[tj]sx?$/,
47
+ /(^|\/)__tests__\//,
48
+ /(^|\/)tests?\//,
49
+ /(^|\/)e2e\//,
50
+ /(^|\/)cypress\//,
51
+ /(^|\/)playwright\//,
52
+ ];
53
+ const INFRA_PATTERNS = [
54
+ /\.tf$/,
55
+ /\.tfvars$/,
56
+ /(^|\/)terraform\//,
57
+ /(^|\/)infra\//,
58
+ /(^|\/)deploy\//,
59
+ /(^|\/)k8s\//,
60
+ /(^|\/)helm\//,
61
+ /^Dockerfile$/,
62
+ /^docker-compose\.ya?ml$/,
63
+ ];
64
+ const ASSET_PATTERNS = [
65
+ /\.(png|jpe?g|gif|webp|avif|svg|ico)$/i,
66
+ /\.(woff2?|ttf|otf|eot)$/i,
67
+ /\.(mp4|webm|mov|m4v)$/i,
68
+ /^public\//,
69
+ ];
70
+ const TRIVIAL_PATTERN_GROUPS = [
71
+ { name: "documentation", patterns: DOC_PATTERNS },
72
+ { name: "lockfile", patterns: LOCK_PATTERNS },
73
+ { name: "CI config", patterns: CI_PATTERNS },
74
+ { name: "test", patterns: TEST_PATTERNS },
75
+ { name: "infrastructure", patterns: INFRA_PATTERNS },
76
+ { name: "asset", patterns: ASSET_PATTERNS },
77
+ ];
78
+ export function classifySkip(diff) {
79
+ if (diff.files.length === 0) {
80
+ return { skip: true, reason: "No file changes between base and head." };
81
+ }
82
+ // Single-category short-circuits give a clearer reason string.
83
+ for (const group of TRIVIAL_PATTERN_GROUPS) {
84
+ if (diff.files.every((f) => matches(f.path, group.patterns))) {
85
+ return { skip: true, reason: `Diff is ${group.name}-only.` };
86
+ }
87
+ }
88
+ // Mixed-trivial — still no runtime risk.
89
+ const allTrivial = diff.files.every((f) => TRIVIAL_PATTERN_GROUPS.some((g) => matches(f.path, g.patterns)));
90
+ if (allTrivial) {
91
+ return { skip: true, reason: "Diff contains only non-runtime changes (docs, lockfiles, CI, tests, infra, assets)." };
92
+ }
93
+ return { skip: false };
94
+ }
95
+ function matches(path, patterns) {
96
+ return patterns.some((p) => p.test(path));
97
+ }
98
+ function run(args, cwd) {
99
+ return execFileSync(args[0], args.slice(1), {
100
+ cwd,
101
+ encoding: "utf8",
102
+ maxBuffer: 64 * 1024 * 1024,
103
+ });
104
+ }
105
+ function parseNameStatus(out) {
106
+ const result = {};
107
+ const tokens = out.split("\0").filter(Boolean);
108
+ let i = 0;
109
+ while (i < tokens.length) {
110
+ const code = tokens[i];
111
+ i++;
112
+ const letter = code[0];
113
+ if (letter === "R" || letter === "C") {
114
+ const oldPath = tokens[i];
115
+ const newPath = tokens[i + 1];
116
+ i += 2;
117
+ result[newPath] = { status: "renamed", oldPath };
118
+ }
119
+ else {
120
+ const path = tokens[i];
121
+ i++;
122
+ const status = letter === "A" ? "added" : letter === "D" ? "deleted" : "modified";
123
+ result[path] = { status };
124
+ }
125
+ }
126
+ return result;
127
+ }
128
+ function parseNumstat(out) {
129
+ const result = {};
130
+ const tokens = out.split("\0").filter(Boolean);
131
+ let i = 0;
132
+ while (i < tokens.length) {
133
+ const line = tokens[i];
134
+ i++;
135
+ const parts = line.split("\t");
136
+ if (parts.length < 3)
137
+ continue;
138
+ const [a, d, maybePath] = parts;
139
+ let path;
140
+ if (maybePath === "") {
141
+ const oldPath = tokens[i];
142
+ const newPath = tokens[i + 1];
143
+ i += 2;
144
+ path = newPath;
145
+ void oldPath;
146
+ }
147
+ else {
148
+ path = maybePath;
149
+ }
150
+ const binary = a === "-" || d === "-";
151
+ result[path] = {
152
+ additions: binary ? 0 : parseInt(a, 10) || 0,
153
+ deletions: binary ? 0 : parseInt(d, 10) || 0,
154
+ binary,
155
+ };
156
+ }
157
+ return result;
158
+ }
159
+ function parsePatch(out) {
160
+ const result = {};
161
+ const lines = out.split("\n");
162
+ let currentPath = null;
163
+ let currentHunk = null;
164
+ const flush = () => {
165
+ if (currentPath && currentHunk && currentHunk.length > 0) {
166
+ (result[currentPath] ??= []).push(currentHunk.join("\n"));
167
+ }
168
+ currentHunk = null;
169
+ };
170
+ for (const line of lines) {
171
+ if (line.startsWith("diff --git ")) {
172
+ flush();
173
+ const m = /diff --git a\/(.+?) b\/(.+)$/.exec(line);
174
+ currentPath = m ? m[2] : null;
175
+ }
176
+ else if (line.startsWith("@@")) {
177
+ flush();
178
+ currentHunk = [line];
179
+ }
180
+ else if (currentHunk) {
181
+ currentHunk.push(line);
182
+ }
183
+ }
184
+ flush();
185
+ return result;
186
+ }
187
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { Plan, PlanFlow } from "./types.js";
2
+ export type GatingMode = "shadow" | "advisory" | "gating";
3
+ export type RiskLevel = PlanFlow["risk"];
4
+ export interface GatingDecision {
5
+ /** Whether to post a check at all (false in shadow mode). */
6
+ postCheck: boolean;
7
+ /** Final conclusion of the check, when posted. */
8
+ conclusion: "success" | "failure" | "neutral";
9
+ /** Short summary line that goes into the check's title field. */
10
+ title: string;
11
+ /** Flows that triggered a failure, for human-facing detail. */
12
+ failingFlows: PlanFlow[];
13
+ }
14
+ export interface GatingOptions {
15
+ mode: GatingMode;
16
+ /** Minimum risk level that causes a `gating`-mode failure. Default `high`. */
17
+ blockingRisk?: RiskLevel;
18
+ }
19
+ /**
20
+ * Pure decision function — takes the plan and the user's gating config and
21
+ * tells the action what check (if any) to post.
22
+ *
23
+ * Rules:
24
+ * shadow → never post a check
25
+ * advisory → post a neutral check unconditionally
26
+ * gating → post a check; fails if any flow has risk >= blockingRisk;
27
+ * skip-verdict plans always pass (nothing to verify)
28
+ */
29
+ export declare function decideGating(plan: Plan, opts: GatingOptions): GatingDecision;
package/dist/gating.js ADDED
@@ -0,0 +1,59 @@
1
+ const RISK_ORDER = { low: 0, medium: 1, high: 2 };
2
+ /**
3
+ * Pure decision function — takes the plan and the user's gating config and
4
+ * tells the action what check (if any) to post.
5
+ *
6
+ * Rules:
7
+ * shadow → never post a check
8
+ * advisory → post a neutral check unconditionally
9
+ * gating → post a check; fails if any flow has risk >= blockingRisk;
10
+ * skip-verdict plans always pass (nothing to verify)
11
+ */
12
+ export function decideGating(plan, opts) {
13
+ if (opts.mode === "shadow") {
14
+ return { postCheck: false, conclusion: "success", title: "", failingFlows: [] };
15
+ }
16
+ if (opts.mode === "advisory") {
17
+ return {
18
+ postCheck: true,
19
+ conclusion: "neutral",
20
+ title: summarizeForCheck(plan, "advisory"),
21
+ failingFlows: [],
22
+ };
23
+ }
24
+ // mode === "gating"
25
+ const threshold = opts.blockingRisk ?? "high";
26
+ const thresholdLevel = RISK_ORDER[threshold];
27
+ if (plan.verdict === "skip") {
28
+ return {
29
+ postCheck: true,
30
+ conclusion: "success",
31
+ title: `claudia: skipped (${plan.skipReason ?? "no testable changes"})`,
32
+ failingFlows: [],
33
+ };
34
+ }
35
+ const failing = plan.flows.filter((f) => RISK_ORDER[f.risk] >= thresholdLevel);
36
+ if (failing.length === 0) {
37
+ return {
38
+ postCheck: true,
39
+ conclusion: "success",
40
+ title: summarizeForCheck(plan, "gating-pass"),
41
+ failingFlows: [],
42
+ };
43
+ }
44
+ return {
45
+ postCheck: true,
46
+ conclusion: "failure",
47
+ title: `${failing.length} flow${failing.length === 1 ? "" : "s"} at or above risk threshold "${threshold}"`,
48
+ failingFlows: failing,
49
+ };
50
+ }
51
+ function summarizeForCheck(plan, kind) {
52
+ if (plan.verdict === "skip")
53
+ return `claudia: skipped (${plan.skipReason ?? "no testable changes"})`;
54
+ const flows = plan.flows.length;
55
+ if (kind === "gating-pass")
56
+ return `claudia: ${flows} flow${flows === 1 ? "" : "s"} to verify — no blocking risk`;
57
+ return `claudia: ${flows} flow${flows === 1 ? "" : "s"} to verify`;
58
+ }
59
+ //# sourceMappingURL=gating.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gating.js","sourceRoot":"","sources":["../src/gating.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,GAA8B,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAmB7E;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,IAAU,EAAE,IAAmB;IAC1D,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC;IAClF,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,OAAO;YACL,SAAS,EAAE,IAAI;YACf,UAAU,EAAE,SAAS;YACrB,KAAK,EAAE,iBAAiB,CAAC,IAAI,EAAE,UAAU,CAAC;YAC1C,YAAY,EAAE,EAAE;SACjB,CAAC;IACJ,CAAC;IAED,oBAAoB;IACpB,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC;IAC9C,MAAM,cAAc,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IAE7C,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO;YACL,SAAS,EAAE,IAAI;YACf,UAAU,EAAE,SAAS;YACrB,KAAK,EAAE,qBAAqB,IAAI,CAAC,UAAU,IAAI,qBAAqB,GAAG;YACvE,YAAY,EAAE,EAAE;SACjB,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,CAAC;IAC/E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,SAAS,EAAE,IAAI;YACf,UAAU,EAAE,SAAS;YACrB,KAAK,EAAE,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC;YAC7C,YAAY,EAAE,EAAE;SACjB,CAAC;IACJ,CAAC;IACD,OAAO;QACL,SAAS,EAAE,IAAI;QACf,UAAU,EAAE,SAAS;QACrB,KAAK,EAAE,GAAG,OAAO,CAAC,MAAM,QAAQ,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,gCAAgC,SAAS,GAAG;QAC3G,YAAY,EAAE,OAAO;KACtB,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAU,EAAE,IAAgC;IACrE,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM;QAAE,OAAO,qBAAqB,IAAI,CAAC,UAAU,IAAI,qBAAqB,GAAG,CAAC;IACrG,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAChC,IAAI,IAAI,KAAK,aAAa;QAAE,OAAO,YAAY,KAAK,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,+BAA+B,CAAC;IAClH,OAAO,YAAY,KAAK,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC;AACrE,CAAC"}
@@ -0,0 +1,15 @@
1
+ export * from "./types.js";
2
+ export { readDiff, classifySkip } from "./diff.js";
3
+ export { buildNextMap } from "./adapters/nextjs.js";
4
+ export { loadOrBuildMap, readMap, writeMap } from "./map.js";
5
+ export { buildUserMessage, SYSTEM_PROMPT } from "./prompt.js";
6
+ export { callPlanner, PlannerError } from "./llm.js";
7
+ export { runPlan } from "./plan.js";
8
+ export type { PlanRunOptions, PlanRunResult } from "./plan.js";
9
+ export { formatPlanMarkdown, formatPlanJson } from "./plan-format.js";
10
+ export { decideGating } from "./gating.js";
11
+ export type { GatingDecision, GatingMode, GatingOptions, RiskLevel } from "./gating.js";
12
+ export { runSelect, formatSelectionMarkdown } from "./select.js";
13
+ export type { SelectOptions, SelectionResult, SelectedSpec } from "./select.js";
14
+ export { buildPlaywrightCommand, parsePlaywrightReport, formatRunMarkdown } from "./runner.js";
15
+ export type { RunCommandOptions, PlaywrightCommand, PlaywrightReport, FailedTest, } from "./runner.js";
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export * from "./types.js";
2
+ export { readDiff, classifySkip } from "./diff.js";
3
+ export { buildNextMap } from "./adapters/nextjs.js";
4
+ export { loadOrBuildMap, readMap, writeMap } from "./map.js";
5
+ export { buildUserMessage, SYSTEM_PROMPT } from "./prompt.js";
6
+ export { callPlanner, PlannerError } from "./llm.js";
7
+ export { runPlan } from "./plan.js";
8
+ export { formatPlanMarkdown, formatPlanJson } from "./plan-format.js";
9
+ export { decideGating } from "./gating.js";
10
+ export { runSelect, formatSelectionMarkdown } from "./select.js";
11
+ export { buildPlaywrightCommand, parsePlaywrightReport, formatRunMarkdown } from "./runner.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
package/dist/llm.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { type Plan } from "./types.js";
2
+ export declare class PlannerError extends Error {
3
+ readonly details: Record<string, unknown>;
4
+ constructor(message: string, details: Record<string, unknown>);
5
+ }
6
+ export interface LlmCallOptions {
7
+ apiKey?: string;
8
+ model?: string;
9
+ systemPrompt?: string;
10
+ mapBlock: string;
11
+ diffBlock: string;
12
+ maxTokens?: number;
13
+ }
14
+ export interface LlmResult {
15
+ plan: Plan;
16
+ usage: {
17
+ inputTokens: number;
18
+ outputTokens: number;
19
+ cacheCreationTokens: number;
20
+ cacheReadTokens: number;
21
+ };
22
+ model: string;
23
+ }
24
+ export declare function callPlanner(opts: LlmCallOptions): Promise<LlmResult>;