@zivis/mcp 0.1.0-alpha.22 → 0.1.0-alpha.24

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 (87) hide show
  1. package/dist/lib/inspect-cache.d.ts +32 -0
  2. package/dist/lib/inspect-cache.js +99 -0
  3. package/dist/lib/inspect-cache.js.map +1 -0
  4. package/dist/matcher/ast-eval.d.ts +27 -0
  5. package/dist/matcher/ast-eval.js +132 -0
  6. package/dist/matcher/ast-eval.js.map +1 -0
  7. package/dist/matcher/finding.d.ts +76 -0
  8. package/dist/matcher/finding.js +14 -0
  9. package/dist/matcher/finding.js.map +1 -0
  10. package/dist/matcher/index.d.ts +30 -0
  11. package/dist/matcher/index.js +255 -0
  12. package/dist/matcher/index.js.map +1 -0
  13. package/dist/matcher/inference-candidates.d.ts +54 -0
  14. package/dist/matcher/inference-candidates.js +106 -0
  15. package/dist/matcher/inference-candidates.js.map +1 -0
  16. package/dist/matcher/walker.d.ts +14 -0
  17. package/dist/matcher/walker.js +79 -0
  18. package/dist/matcher/walker.js.map +1 -0
  19. package/dist/pattern-pack/index.d.ts +21 -0
  20. package/dist/pattern-pack/index.js +12 -0
  21. package/dist/pattern-pack/index.js.map +1 -1
  22. package/dist/pattern-packs/zivis-public-0.1.0/capsules/agents/privilege-separation.yaml +3 -7
  23. package/dist/pattern-packs/zivis-public-0.1.0/capsules/prompting/self-consistency-no-isolation.yaml +16 -19
  24. package/dist/pattern-packs/zivis-public-0.1.0/capsules/security/late-org-filter.yaml +14 -18
  25. package/dist/pattern-packs/zivis-public-0.1.0/controls/external_validator_on_facts.yaml +37 -0
  26. package/dist/pattern-packs/zivis-public-0.1.0/controls/human_approval_gate.yaml +44 -0
  27. package/dist/pattern-packs/zivis-public-0.1.0/controls/langgraph_state_machine_with_per_node_tools.yaml +35 -0
  28. package/dist/pattern-packs/zivis-public-0.1.0/controls/per_sample_retrieval_diversification.yaml +38 -0
  29. package/dist/pattern-packs/zivis-public-0.1.0/controls/prisma_extension_or_middleware_attaching_org_filter.yaml +31 -0
  30. package/dist/pattern-packs/zivis-public-0.1.0/controls/row_level_security_policy.yaml +37 -0
  31. package/dist/pattern-packs/zivis-public-0.1.0/manifest.json +65 -4
  32. package/dist/pattern-packs/zivis-public-0.2.0/capsules/agents/privilege-separation.yaml +224 -0
  33. package/dist/pattern-packs/zivis-public-0.2.0/capsules/prompting/self-consistency-no-isolation.yaml +237 -0
  34. package/dist/pattern-packs/zivis-public-0.2.0/capsules/retrieval/context-injection-no-validator.yaml +348 -0
  35. package/dist/pattern-packs/zivis-public-0.2.0/capsules/security/late-org-filter.yaml +266 -0
  36. package/dist/pattern-packs/zivis-public-0.2.0/controls/external_validator_on_facts.yaml +37 -0
  37. package/dist/pattern-packs/zivis-public-0.2.0/controls/fact_validator_present.yaml +68 -0
  38. package/dist/pattern-packs/zivis-public-0.2.0/controls/human_approval_gate.yaml +44 -0
  39. package/dist/pattern-packs/zivis-public-0.2.0/controls/langgraph_state_machine_with_per_node_tools.yaml +35 -0
  40. package/dist/pattern-packs/zivis-public-0.2.0/controls/per_sample_retrieval_diversification.yaml +38 -0
  41. package/dist/pattern-packs/zivis-public-0.2.0/controls/prisma_extension_or_middleware_attaching_org_filter.yaml +31 -0
  42. package/dist/pattern-packs/zivis-public-0.2.0/controls/row_level_security_policy.yaml +37 -0
  43. package/dist/pattern-packs/zivis-public-0.2.0/controls/source_attribution_present.yaml +58 -0
  44. package/dist/pattern-packs/zivis-public-0.2.0/manifest.json +167 -0
  45. package/dist/pattern-packs/zivis-public-0.2.0/prompts/agents/privilege-separation/identify.md +54 -0
  46. package/dist/pattern-packs/zivis-public-0.2.0/prompts/prompting/self-consistency-no-isolation/identify.md +60 -0
  47. package/dist/pattern-packs/zivis-public-0.2.0/prompts/retrieval/context-injection-no-validator/identify.md +109 -0
  48. package/dist/pattern-packs/zivis-public-0.2.0/prompts/security/late-org-filter/identify.md +78 -0
  49. package/dist/server.js +10 -0
  50. package/dist/server.js.map +1 -1
  51. package/dist/tools/check-project.d.ts +1 -1
  52. package/dist/tools/check-project.js +2 -4
  53. package/dist/tools/check-project.js.map +1 -1
  54. package/dist/tools/enterprise/manage-actor.d.ts +1 -1
  55. package/dist/tools/enterprise/manage-actor.js +14 -1
  56. package/dist/tools/enterprise/manage-actor.js.map +1 -1
  57. package/dist/tools/enterprise/manage-attack-scenario.d.ts +1 -1
  58. package/dist/tools/enterprise/manage-attack-scenario.js +13 -1
  59. package/dist/tools/enterprise/manage-attack-scenario.js.map +1 -1
  60. package/dist/tools/enterprise/manage-component.d.ts +1 -1
  61. package/dist/tools/enterprise/manage-component.js +20 -1
  62. package/dist/tools/enterprise/manage-component.js.map +1 -1
  63. package/dist/tools/enterprise/manage-data-asset.d.ts +1 -1
  64. package/dist/tools/enterprise/manage-data-asset.js +17 -1
  65. package/dist/tools/enterprise/manage-data-asset.js.map +1 -1
  66. package/dist/tools/enterprise/manage-data-flow.d.ts +1 -1
  67. package/dist/tools/enterprise/manage-data-flow.js +19 -1
  68. package/dist/tools/enterprise/manage-data-flow.js.map +1 -1
  69. package/dist/tools/enterprise/manage-security-control.d.ts +1 -1
  70. package/dist/tools/enterprise/manage-security-control.js +18 -1
  71. package/dist/tools/enterprise/manage-security-control.js.map +1 -1
  72. package/dist/tools/enterprise/manage-test-case.d.ts +1 -1
  73. package/dist/tools/enterprise/manage-test-case.js +8 -0
  74. package/dist/tools/enterprise/manage-test-case.js.map +1 -1
  75. package/dist/tools/get-started.d.ts +1 -1
  76. package/dist/tools/get-started.js +151 -9
  77. package/dist/tools/get-started.js.map +1 -1
  78. package/dist/tools/inspect.d.ts +16 -9
  79. package/dist/tools/inspect.js +200 -46
  80. package/dist/tools/inspect.js.map +1 -1
  81. package/dist/tools/threat-guide.d.ts +41 -0
  82. package/dist/tools/threat-guide.js +203 -0
  83. package/dist/tools/threat-guide.js.map +1 -0
  84. package/dist/tools/threat-run-matcher.d.ts +27 -0
  85. package/dist/tools/threat-run-matcher.js +164 -0
  86. package/dist/tools/threat-run-matcher.js.map +1 -0
  87. package/package.json +17 -13
@@ -0,0 +1,32 @@
1
+ /**
2
+ * inspect-cache — shared helpers for locating local `zivis inspect` artifacts.
3
+ *
4
+ * The repo-id derivation must match the algorithm `zivis inspect` uses when it
5
+ * writes runs (git remote URL hash, falling back to root-path hash). Both
6
+ * `zivis_get_started` and `zivis_threat_guide` read this cache to avoid
7
+ * nagging the user to re-inspect when a fresh run already exists on disk.
8
+ */
9
+ export interface LocatedArtifact {
10
+ path: string;
11
+ mtimeMs: number;
12
+ }
13
+ export declare function userCacheRoot(): string;
14
+ export declare function deriveRepoId(cwd: string): Promise<string>;
15
+ /**
16
+ * Locate the most recent inspect artifact for THIS repo.
17
+ *
18
+ * Returns undefined if no runs exist for this repo, even when other repos
19
+ * have artifacts. Returns both the path and mtime so callers can apply
20
+ * freshness logic without re-stat'ing.
21
+ */
22
+ export declare function locateLatestArtifact(cwd: string): Promise<LocatedArtifact | undefined>;
23
+ /**
24
+ * Default freshness TTL for the inspect cache. After this, callers should
25
+ * treat the artifact as stale and recommend re-running inspect.
26
+ *
27
+ * 15 min matches the typical IDE-session length for "I just inspected; don't
28
+ * make me do it again." Override via ZIVIS_INSPECT_CACHE_TTL_MS.
29
+ */
30
+ export declare const DEFAULT_INSPECT_CACHE_TTL_MS: number;
31
+ export declare function inspectCacheTtlMs(): number;
32
+ export declare function isFresh(mtimeMs: number, now?: number): boolean;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * inspect-cache — shared helpers for locating local `zivis inspect` artifacts.
3
+ *
4
+ * The repo-id derivation must match the algorithm `zivis inspect` uses when it
5
+ * writes runs (git remote URL hash, falling back to root-path hash). Both
6
+ * `zivis_get_started` and `zivis_threat_guide` read this cache to avoid
7
+ * nagging the user to re-inspect when a fresh run already exists on disk.
8
+ */
9
+ import * as fs from "node:fs/promises";
10
+ import * as fssync from "node:fs";
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+ import * as crypto from "node:crypto";
14
+ import { exec } from "node:child_process";
15
+ import { promisify } from "node:util";
16
+ const execAsync = promisify(exec);
17
+ export function userCacheRoot() {
18
+ const home = os.homedir();
19
+ if (process.platform === "darwin") {
20
+ return path.join(home, "Library", "Caches", "zivis");
21
+ }
22
+ if (process.platform === "win32") {
23
+ const localAppData = process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local");
24
+ return path.join(localAppData, "zivis", "Cache");
25
+ }
26
+ const xdg = process.env.XDG_CACHE_HOME;
27
+ return path.join(xdg ?? path.join(home, ".cache"), "zivis");
28
+ }
29
+ export async function deriveRepoId(cwd) {
30
+ const remote = await tryGit(cwd, "remote get-url origin");
31
+ if (remote) {
32
+ return "remote-" + sha256Short(remote);
33
+ }
34
+ return "path-" + sha256Short(cwd);
35
+ }
36
+ async function tryGit(cwd, args) {
37
+ try {
38
+ const { stdout } = await execAsync(`git ${args}`, { cwd });
39
+ const value = stdout.trim();
40
+ return value || undefined;
41
+ }
42
+ catch {
43
+ return undefined;
44
+ }
45
+ }
46
+ function sha256Short(value) {
47
+ return crypto.createHash("sha256").update(value).digest("hex").slice(0, 16);
48
+ }
49
+ /**
50
+ * Locate the most recent inspect artifact for THIS repo.
51
+ *
52
+ * Returns undefined if no runs exist for this repo, even when other repos
53
+ * have artifacts. Returns both the path and mtime so callers can apply
54
+ * freshness logic without re-stat'ing.
55
+ */
56
+ export async function locateLatestArtifact(cwd) {
57
+ const repoId = await deriveRepoId(cwd);
58
+ const runsDir = path.join(userCacheRoot(), "repos", repoId, "inspect", "runs");
59
+ if (!fssync.existsSync(runsDir))
60
+ return undefined;
61
+ const candidates = [];
62
+ for (const entry of await fs.readdir(runsDir)) {
63
+ if (!entry.endsWith(".json"))
64
+ continue;
65
+ const full = path.join(runsDir, entry);
66
+ try {
67
+ const stat = await fs.stat(full);
68
+ candidates.push({ path: full, mtimeMs: stat.mtimeMs });
69
+ }
70
+ catch {
71
+ // skip
72
+ }
73
+ }
74
+ if (candidates.length === 0)
75
+ return undefined;
76
+ candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
77
+ return candidates[0];
78
+ }
79
+ /**
80
+ * Default freshness TTL for the inspect cache. After this, callers should
81
+ * treat the artifact as stale and recommend re-running inspect.
82
+ *
83
+ * 15 min matches the typical IDE-session length for "I just inspected; don't
84
+ * make me do it again." Override via ZIVIS_INSPECT_CACHE_TTL_MS.
85
+ */
86
+ export const DEFAULT_INSPECT_CACHE_TTL_MS = 15 * 60 * 1000;
87
+ export function inspectCacheTtlMs() {
88
+ const raw = process.env.ZIVIS_INSPECT_CACHE_TTL_MS;
89
+ if (!raw)
90
+ return DEFAULT_INSPECT_CACHE_TTL_MS;
91
+ const parsed = Number.parseInt(raw, 10);
92
+ if (!Number.isFinite(parsed) || parsed <= 0)
93
+ return DEFAULT_INSPECT_CACHE_TTL_MS;
94
+ return parsed;
95
+ }
96
+ export function isFresh(mtimeMs, now = Date.now()) {
97
+ return now - mtimeMs <= inspectCacheTtlMs();
98
+ }
99
+ //# sourceMappingURL=inspect-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspect-cache.js","sourceRoot":"","sources":["../../src/lib/inspect-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * AST strategy evaluator.
3
+ *
4
+ * Wraps @ast-grep/napi. Capsule strategies of code:AST carry a YAML rule
5
+ * body in `expression` (already parsed by js-yaml when the capsule was
6
+ * loaded). We run that rule against source files of the strategy's
7
+ * language and collect line spans as evidence.
8
+ *
9
+ * Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md (Phase 3 — Matching engine)
10
+ */
11
+ export type AstLanguage = "typescript" | "javascript" | "python" | "go";
12
+ export interface AstMatch {
13
+ file: string;
14
+ start_line: number;
15
+ end_line: number;
16
+ matched_text: string;
17
+ }
18
+ /**
19
+ * Run an ast-grep rule (already parsed from the capsule YAML) against the
20
+ * given source files and return all matches.
21
+ *
22
+ * The expression is the capsule's `strategy.expression` field. Capsules
23
+ * may write the rule as either:
24
+ * - a raw string (parsed as YAML) containing a top-level `rule:` block, or
25
+ * - a structured object (already an `{ rule: { ... } }` shape)
26
+ */
27
+ export declare function evaluateAstStrategy(expression: unknown, language: AstLanguage, files: string[], rootDir: string): Promise<AstMatch[]>;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * AST strategy evaluator.
3
+ *
4
+ * Wraps @ast-grep/napi. Capsule strategies of code:AST carry a YAML rule
5
+ * body in `expression` (already parsed by js-yaml when the capsule was
6
+ * loaded). We run that rule against source files of the strategy's
7
+ * language and collect line spans as evidence.
8
+ *
9
+ * Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md (Phase 3 — Matching engine)
10
+ */
11
+ import * as fs from "node:fs/promises";
12
+ import * as path from "node:path";
13
+ import * as yaml from "js-yaml";
14
+ import * as napi from "@ast-grep/napi";
15
+ let registered = false;
16
+ async function ensureLanguagesRegistered() {
17
+ if (registered)
18
+ return;
19
+ // ts/js are built into napi; python and go come from dynamic language packs.
20
+ const py = (await import("@ast-grep/lang-python")).default;
21
+ const go = (await import("@ast-grep/lang-go")).default;
22
+ napi.registerDynamicLanguage({ python: py, go: go });
23
+ registered = true;
24
+ }
25
+ /**
26
+ * Run an ast-grep rule (already parsed from the capsule YAML) against the
27
+ * given source files and return all matches.
28
+ *
29
+ * The expression is the capsule's `strategy.expression` field. Capsules
30
+ * may write the rule as either:
31
+ * - a raw string (parsed as YAML) containing a top-level `rule:` block, or
32
+ * - a structured object (already an `{ rule: { ... } }` shape)
33
+ */
34
+ export async function evaluateAstStrategy(expression, language, files, rootDir) {
35
+ await ensureLanguagesRegistered();
36
+ const rule = normalizeExpressionToRule(expression);
37
+ if (!rule)
38
+ return [];
39
+ const matches = [];
40
+ const langKey = languageKey(language);
41
+ for (const file of files) {
42
+ let raw;
43
+ try {
44
+ raw = await fs.readFile(file, "utf8");
45
+ }
46
+ catch {
47
+ continue;
48
+ }
49
+ if (!raw)
50
+ continue;
51
+ let root;
52
+ try {
53
+ root = napi.parse(langKey, raw);
54
+ }
55
+ catch {
56
+ continue;
57
+ }
58
+ let foundNodes;
59
+ try {
60
+ // Cast: napi types restrict findAll to string|number, but at runtime it
61
+ // accepts a NapiConfig object (which is what capsule rules deserialize to).
62
+ foundNodes = root.root().findAll(rule);
63
+ }
64
+ catch {
65
+ // ast-grep throws on malformed rules; skip this strategy on this file.
66
+ continue;
67
+ }
68
+ for (const node of foundNodes) {
69
+ const range = node.range();
70
+ matches.push({
71
+ file: relPosix(rootDir, file),
72
+ start_line: (range.start.line ?? 0) + 1,
73
+ end_line: (range.end.line ?? 0) + 1,
74
+ matched_text: truncate(node.text(), 240),
75
+ });
76
+ }
77
+ }
78
+ return matches;
79
+ }
80
+ // ── Helpers ──
81
+ function normalizeExpressionToRule(expression) {
82
+ if (typeof expression === "string") {
83
+ try {
84
+ return ensureRuleWrapper(yaml.load(expression));
85
+ }
86
+ catch {
87
+ return undefined;
88
+ }
89
+ }
90
+ if (expression && typeof expression === "object") {
91
+ return ensureRuleWrapper(expression);
92
+ }
93
+ return undefined;
94
+ }
95
+ /**
96
+ * ast-grep's findAll requires a top-level `rule:` wrapper. Capsule AST
97
+ * strategies have it explicit in YAML; control detection rules carry just
98
+ * the bare rule body. Wrap consistently here so both shapes evaluate.
99
+ *
100
+ * NapiConfig keys recognized as outer-level: rule, constraints, utils,
101
+ * filters, language, transform, fix. Any of those at the top means we're
102
+ * already wrapped.
103
+ */
104
+ function ensureRuleWrapper(parsed) {
105
+ if (!parsed || typeof parsed !== "object")
106
+ return parsed;
107
+ const obj = parsed;
108
+ if ("rule" in obj)
109
+ return obj;
110
+ return { rule: obj };
111
+ }
112
+ function languageKey(lang) {
113
+ // ts and tsx are separate engines in napi; we use ts for .ts/.tsx and
114
+ // js for .js/.jsx/.mjs/.cjs. The matcher passes the file's language tag,
115
+ // not the file extension — so .tsx files routed as 'typescript' work.
116
+ switch (lang) {
117
+ case "typescript": return "ts";
118
+ case "javascript": return "js";
119
+ case "python": return "python";
120
+ case "go": return "go";
121
+ }
122
+ }
123
+ function relPosix(rootDir, abs) {
124
+ const rel = path.relative(rootDir, abs);
125
+ return rel.split(path.sep).join("/");
126
+ }
127
+ function truncate(s, max) {
128
+ if (s.length <= max)
129
+ return s;
130
+ return s.slice(0, max) + "…";
131
+ }
132
+ //# sourceMappingURL=ast-eval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast-eval.js","sourceRoot":"","sources":["../../src/matcher/ast-eval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Normalized Finding type emitted by the matcher.
3
+ *
4
+ * Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md
5
+ * "Normalize outputs to a single finding schema (patternId/version,
6
+ * matched graph evidence, confidence, lane, explanation, remediation)."
7
+ *
8
+ * Lane determination:
9
+ * - trusted: count(fired deterministic strategies) >= required_evidence_count
10
+ * - inferred: any strategy fired but bar not met (host LLM should confirm)
11
+ * - (no finding emitted otherwise)
12
+ */
13
+ export type Lane = "trusted" | "inferred";
14
+ export interface MatchedEvidence {
15
+ /** Where the strategy fired. */
16
+ file: string;
17
+ start_line: number;
18
+ end_line?: number;
19
+ /** Truncated source snippet for human review (NOT for re-execution). */
20
+ matched_text?: string;
21
+ /** Which strategy code emitted this evidence (AST, GRAPH, META, INFER, ...). */
22
+ strategy_code: string;
23
+ }
24
+ export interface FiredStrategy {
25
+ code: string;
26
+ language?: string;
27
+ trusted: boolean;
28
+ confidence_weight: number;
29
+ match_count: number;
30
+ }
31
+ export interface InferenceCandidate {
32
+ prompt_ref: string;
33
+ expected_output_schema?: unknown;
34
+ minimum_context_required?: {
35
+ lines?: number;
36
+ symbols?: string[];
37
+ };
38
+ /** Why the host LLM should run inference on this finding. */
39
+ reason: string;
40
+ }
41
+ export interface Finding {
42
+ pattern_id: string;
43
+ pattern_version: string;
44
+ lane: Lane;
45
+ /** 0..1 — sum of fired-strategy weights, capped at 1. */
46
+ confidence: number;
47
+ /** Strategies that contributed (AST matches, INFER candidate, etc.). */
48
+ fired_strategies: FiredStrategy[];
49
+ /** Evidence spans from deterministic strategies that fired. */
50
+ evidence: MatchedEvidence[];
51
+ /** Present iff the capsule has an INFER strategy — host LLM should pick this up. */
52
+ inference_candidate?: InferenceCandidate;
53
+ /** Surfaced from the capsule for the host's framing. */
54
+ safe_summary?: string;
55
+ threats?: unknown[];
56
+ repair_contract?: unknown;
57
+ }
58
+ export interface MatcherSummary {
59
+ trusted_count: number;
60
+ inferred_count: number;
61
+ total: number;
62
+ }
63
+ export interface MatcherResult {
64
+ pack: {
65
+ id: string;
66
+ version: string;
67
+ };
68
+ artifact_path: string;
69
+ cwd: string;
70
+ capsules_relevant: number;
71
+ capsules_evaluated: number;
72
+ ast_files_scanned: number;
73
+ duration_ms: number;
74
+ summary: MatcherSummary;
75
+ findings: Finding[];
76
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Normalized Finding type emitted by the matcher.
3
+ *
4
+ * Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md
5
+ * "Normalize outputs to a single finding schema (patternId/version,
6
+ * matched graph evidence, confidence, lane, explanation, remediation)."
7
+ *
8
+ * Lane determination:
9
+ * - trusted: count(fired deterministic strategies) >= required_evidence_count
10
+ * - inferred: any strategy fired but bar not met (host LLM should confirm)
11
+ * - (no finding emitted otherwise)
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=finding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding.js","sourceRoot":"","sources":["../../src/matcher/finding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Phase 3 matcher — runs the local public pack against a repo graph
3
+ * artifact + the source files in cwd.
4
+ *
5
+ * v1 scope (locked):
6
+ * - AST strategies are evaluated for real (via @ast-grep/napi)
7
+ * - INFER strategies emit inference candidates; host LLM runs them
8
+ * - GRAPH / META / DEP / CONFIG / STRUCT / REGEX / SEM / RECON: not
9
+ * evaluated yet, but visible in `fired_strategies` as `match_count: 0`
10
+ * so we don't lose the contract.
11
+ *
12
+ * Lane assignment per finding:
13
+ * - trusted: count of fired deterministic (trusted: true) strategies
14
+ * reaches `required_evidence_count`
15
+ * - inferred: any strategy fired but bar not met
16
+ * - (no finding) if nothing fired
17
+ *
18
+ * Plan: docs/plans/LOCAL-REPO-GRAPH-IMPLEMENTATION.md (Phase 3)
19
+ */
20
+ import type { MatcherResult } from "./finding.js";
21
+ export type { Finding, FiredStrategy, MatchedEvidence, MatcherResult, Lane } from "./finding.js";
22
+ export interface RunMatcherOpts {
23
+ /** Path to the RepoGraphArtifactV1 JSON produced by `zivis inspect`. */
24
+ artifactPath: string;
25
+ /** Repo root for source-file walking (capsule AST strategies match against this). */
26
+ cwd: string;
27
+ /** Override default pack id. */
28
+ packId?: string;
29
+ }
30
+ export declare function runMatcher(opts: RunMatcherOpts): Promise<MatcherResult>;