@printwithsynergy/synergy-mcp 0.1.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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * blast_radius — given a target (symbol, HTTP route, or env var),
3
+ * scan every configured repo's working tree and return the call
4
+ * sites.
5
+ *
6
+ * V1 uses ripgrep-style regex matching tuned to the language of
7
+ * each repo (Python vs TypeScript). It's not full AST analysis —
8
+ * ctxo (https://github.com/anthropics/code-review-graph) does that
9
+ * for TS/Go/C# but doesn't ship a Python plugin yet. Grep is the
10
+ * working substitute that lint-pdf's CLAUDE.md already calls out.
11
+ *
12
+ * Three target kinds:
13
+ *
14
+ * 1. `symbol` — e.g. `CodexDocument`, `withTenant`. Matches Python
15
+ * `from X import Y` lines and TS `import { Y } from 'X'` lines,
16
+ * plus bare usage sites.
17
+ * 2. `route` — e.g. `POST /v1/extract` or `/api/v1/jobs`. Matches
18
+ * quoted occurrences of the path in any source file.
19
+ * 3. `env_var` — e.g. `LENS_SERVER_URL`, `STRIPE_SECRET_KEY`.
20
+ * Matches `process.env.X`, `process.env["X"]`, `os.getenv("X")`,
21
+ * `os.environ.get("X")`, `os.environ["X"]`, and YAML `X:` lines.
22
+ */
23
+ import { readFileSync, readdirSync, statSync } from "node:fs";
24
+ import { extname, join } from "node:path";
25
+ import { z } from "zod";
26
+ import { KNOWN_REPOS, REPO_LANG, resolveAllRepoPaths, } from "../config.js";
27
+ export const BlastRadiusInput = z.object({
28
+ target: z
29
+ .string()
30
+ .min(1)
31
+ .describe("What to search for. Examples: 'CodexDocument' (a symbol), '/v1/extract' (an HTTP route), 'LENS_SERVER_URL' (an env var). Use `kind` to disambiguate."),
32
+ kind: z
33
+ .enum(["symbol", "route", "env_var"])
34
+ .default("symbol")
35
+ .describe("How to interpret `target`. 'symbol' matches import lines + bare usage. 'route' matches the path string as a quoted literal. 'env_var' matches process.env / os.environ / os.getenv access."),
36
+ repos: z
37
+ .array(z.string())
38
+ .optional()
39
+ .describe("Optional subset of repos to search. When omitted, searches all configured repos."),
40
+ max_results_per_repo: z
41
+ .number()
42
+ .int()
43
+ .positive()
44
+ .default(50)
45
+ .describe("Cap on hits per repo to keep the response bounded."),
46
+ });
47
+ function escapeRegex(s) {
48
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
49
+ }
50
+ function buildPatterns(target, kind, lang) {
51
+ const t = escapeRegex(target);
52
+ if (kind === "env_var") {
53
+ return [
54
+ {
55
+ kind: "env",
56
+ re: new RegExp(
57
+ // TS: process.env.X, process.env['X'], process.env["X"]
58
+ // Python: os.getenv("X"), os.environ["X"], os.environ.get("X")
59
+ // YAML: X: value
60
+ `(?:process\\.env(?:\\.${t}|\\[['"]${t}['"]\\]))|(?:os\\.getenv\\(['"]${t}['"]\\))|(?:os\\.environ(?:\\[['"]${t}['"]\\]|\\.get\\(['"]${t}['"]\\)))|(?:^\\s*${t}\\s*:)`, "m"),
61
+ },
62
+ ];
63
+ }
64
+ if (kind === "route") {
65
+ return [
66
+ {
67
+ kind: "route",
68
+ re: new RegExp(`['"\`]${t}['"\`]`),
69
+ },
70
+ ];
71
+ }
72
+ // symbol
73
+ const patterns = [];
74
+ if (lang === "python") {
75
+ patterns.push({
76
+ kind: "import",
77
+ re: new RegExp(`^\\s*from\\s+\\S+\\s+import\\s+[^#]*\\b${t}\\b`, "m"),
78
+ });
79
+ }
80
+ else {
81
+ patterns.push({
82
+ kind: "import",
83
+ re: new RegExp(`^\\s*import\\s+(?:[^;]*\\{[^}]*\\b${t}\\b[^}]*\\}|\\*\\s+as\\s+${t}|${t})`, "m"),
84
+ });
85
+ }
86
+ patterns.push({
87
+ kind: "usage",
88
+ re: new RegExp(`\\b${t}\\b`),
89
+ });
90
+ return patterns;
91
+ }
92
+ const SOURCE_EXT = new Set([
93
+ ".ts",
94
+ ".tsx",
95
+ ".js",
96
+ ".jsx",
97
+ ".mjs",
98
+ ".cjs",
99
+ ".py",
100
+ ".pyi",
101
+ ".yml",
102
+ ".yaml",
103
+ ]);
104
+ function isSearchable(name) {
105
+ return SOURCE_EXT.has(extname(name));
106
+ }
107
+ function walk(dir, onFile, depth = 0, maxDepth = 8) {
108
+ if (depth > maxDepth)
109
+ return true;
110
+ let entries;
111
+ try {
112
+ entries = readdirSync(dir);
113
+ }
114
+ catch {
115
+ return true;
116
+ }
117
+ for (const name of entries) {
118
+ if (name === "node_modules" ||
119
+ name === ".git" ||
120
+ name === "dist" ||
121
+ name === "build" ||
122
+ name === ".venv" ||
123
+ name === "__pycache__" ||
124
+ name === ".turbo" ||
125
+ name === ".next") {
126
+ continue;
127
+ }
128
+ const full = join(dir, name);
129
+ let s;
130
+ try {
131
+ s = statSync(full);
132
+ }
133
+ catch {
134
+ continue;
135
+ }
136
+ if (s.isDirectory()) {
137
+ const keepGoing = walk(full, onFile, depth + 1, maxDepth);
138
+ if (!keepGoing)
139
+ return false;
140
+ }
141
+ else if (s.isFile() && isSearchable(name)) {
142
+ const keepGoing = onFile(full);
143
+ if (!keepGoing)
144
+ return false;
145
+ }
146
+ }
147
+ return true;
148
+ }
149
+ function searchRepo(repo, root, patterns, cap) {
150
+ const hits = [];
151
+ walk(root, (file) => {
152
+ let text;
153
+ try {
154
+ text = readFileSync(file, "utf8");
155
+ }
156
+ catch {
157
+ return true;
158
+ }
159
+ const lines = text.split("\n");
160
+ for (let i = 0; i < lines.length; i++) {
161
+ const line = lines[i] ?? "";
162
+ for (const p of patterns) {
163
+ if (p.re.test(line)) {
164
+ hits.push({
165
+ repo,
166
+ file,
167
+ line: i + 1,
168
+ text: line.trim().slice(0, 300),
169
+ kind: p.kind,
170
+ });
171
+ if (hits.length >= cap)
172
+ return false;
173
+ break;
174
+ }
175
+ }
176
+ }
177
+ return true;
178
+ });
179
+ return hits;
180
+ }
181
+ export async function blastRadius(input) {
182
+ const paths = resolveAllRepoPaths();
183
+ const notes = [];
184
+ const subset = input.repos
185
+ ? input.repos.filter((r) => KNOWN_REPOS.includes(r))
186
+ : KNOWN_REPOS;
187
+ const allHits = [];
188
+ for (const repo of subset) {
189
+ const root = paths[repo];
190
+ if (!root) {
191
+ notes.push(`repo ${repo}: not configured`);
192
+ continue;
193
+ }
194
+ const patterns = buildPatterns(input.target, input.kind, REPO_LANG[repo]);
195
+ const hits = searchRepo(repo, root, patterns, input.max_results_per_repo);
196
+ allHits.push(...hits);
197
+ }
198
+ return {
199
+ target: input.target,
200
+ kind: input.kind,
201
+ hits: allHits,
202
+ notes,
203
+ };
204
+ }
205
+ //# sourceMappingURL=blast-radius.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blast-radius.js","sourceRoot":"","sources":["../../src/tools/blast-radius.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import { type RepoName } from "../config.js";
3
+ export declare const FloorPinGridInput: z.ZodObject<{
4
+ package: z.ZodOptional<z.ZodString>;
5
+ }, "strip", z.ZodTypeAny, {
6
+ package?: string | undefined;
7
+ }, {
8
+ package?: string | undefined;
9
+ }>;
10
+ /** A single row in the grid. */
11
+ export interface FloorPinRow {
12
+ repo: RepoName;
13
+ package: string;
14
+ spec: string;
15
+ /** Which manifest file + section the pin came from. */
16
+ source: string;
17
+ }
18
+ export interface FloorPinGridResult {
19
+ rows: FloorPinRow[];
20
+ notes: string[];
21
+ }
22
+ export declare function floorPinGrid(input: z.infer<typeof FloorPinGridInput>): Promise<FloorPinGridResult>;
23
+ //# sourceMappingURL=floor-pin-grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floor-pin-grid.d.ts","sourceRoot":"","sources":["../../src/tools/floor-pin-grid.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAe,KAAK,QAAQ,EAAuB,MAAM,cAAc,CAAC;AAE/E,eAAO,MAAM,iBAAiB;;;;;;EAO5B,CAAC;AAEH,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CAChB;AA+FD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,GACvC,OAAO,CAAC,kBAAkB,CAAC,CA8B7B"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * floor_pin_grid — return the version-pin matrix for org packages
3
+ * across every configured repo.
4
+ *
5
+ * Audits Python `pyproject.toml` `dependencies = [...]` lists and
6
+ * Node `package.json` `dependencies` + `devDependencies` +
7
+ * `peerDependencies` for the known printwithsynergy package names.
8
+ *
9
+ * Audit finding #2 in the cross-stack audit caught lint-pdf pinning
10
+ * `codex-pdf>=1.19.0` with no upper bound while compile-pdf pinned
11
+ * `>=1.15.0,<2.0`. This tool surfaces that mismatch as a one-call
12
+ * grid query.
13
+ */
14
+ import { existsSync, readFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { z } from "zod";
17
+ import { KNOWN_REPOS, resolveAllRepoPaths } from "../config.js";
18
+ export const FloorPinGridInput = z.object({
19
+ package: z
20
+ .string()
21
+ .optional()
22
+ .describe("Optional package-name filter (e.g. 'codex-pdf' or '@printwithsynergy/lens-pdf'). When omitted, returns rows for every detected org package across every repo."),
23
+ });
24
+ /** Known org package names worth surfacing in the grid. */
25
+ const ORG_PACKAGES = [
26
+ "codex-pdf",
27
+ "lint-pdf",
28
+ "compile-pdf",
29
+ "@printwithsynergy/codex-client",
30
+ "@printwithsynergy/lens-pdf",
31
+ "@printwithsynergy/client",
32
+ "@printwithsynergy/synergy-mcp",
33
+ ];
34
+ function isOrgPackage(name) {
35
+ return ORG_PACKAGES.includes(name);
36
+ }
37
+ /** Parse a single pyproject.toml `dependencies = [...]` entry.
38
+ *
39
+ * Hand-rolled rather than `[\\s\\S]*?\\]` regex because package specs
40
+ * legitimately contain `]` (e.g. `uvicorn[standard]>=0.30.0`), which
41
+ * a lazy regex would close on. We walk lines instead: find
42
+ * `^dependencies = [`, then accumulate until `^]` at line start. */
43
+ function parsePyprojectDeps(repo, root) {
44
+ const path = join(root, "pyproject.toml");
45
+ if (!existsSync(path))
46
+ return [];
47
+ const text = readFileSync(path, "utf8");
48
+ const rows = [];
49
+ const lines = text.split("\n");
50
+ let inDeps = false;
51
+ for (const line of lines) {
52
+ if (!inDeps) {
53
+ if (/^dependencies\s*=\s*\[/.test(line))
54
+ inDeps = true;
55
+ continue;
56
+ }
57
+ if (/^\]\s*$/.test(line))
58
+ break;
59
+ // Match the package name + version spec inside quotes; the spec
60
+ // may legitimately contain commas + < > = ~ ! characters.
61
+ const m = line.match(/['"]([A-Za-z0-9_.\-]+)(\[[^\]]*\])?([<>=!~,][^'"]*)?['"]/);
62
+ if (m?.[1]) {
63
+ const name = m[1].trim();
64
+ const spec = (m[3] ?? "").trim() || "(unpinned)";
65
+ if (isOrgPackage(name)) {
66
+ rows.push({
67
+ repo,
68
+ package: name,
69
+ spec,
70
+ source: "pyproject.toml#dependencies",
71
+ });
72
+ }
73
+ }
74
+ }
75
+ return rows;
76
+ }
77
+ /** Parse package.json dep sections for org packages. */
78
+ function parsePackageJson(repo, root) {
79
+ const path = join(root, "package.json");
80
+ if (!existsSync(path))
81
+ return [];
82
+ const rows = [];
83
+ try {
84
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
85
+ for (const section of [
86
+ "dependencies",
87
+ "devDependencies",
88
+ "peerDependencies",
89
+ ]) {
90
+ const block = parsed[section];
91
+ if (block && typeof block === "object") {
92
+ for (const [name, spec] of Object.entries(block)) {
93
+ if (isOrgPackage(name)) {
94
+ rows.push({
95
+ repo,
96
+ package: name,
97
+ spec,
98
+ source: `package.json#${section}`,
99
+ });
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ catch {
106
+ // Malformed JSON — skip rather than throw; surface as empty rows.
107
+ }
108
+ return rows;
109
+ }
110
+ export async function floorPinGrid(input) {
111
+ const paths = resolveAllRepoPaths();
112
+ const rows = [];
113
+ const notes = [];
114
+ for (const repo of KNOWN_REPOS) {
115
+ const root = paths[repo];
116
+ if (!root) {
117
+ notes.push(`repo ${repo}: not configured (set SYNERGY_MCP_PATH_${repo
118
+ .toUpperCase()
119
+ .replace(/-/g, "_")} or SYNERGY_MCP_STACK_ROOT)`);
120
+ continue;
121
+ }
122
+ rows.push(...parsePyprojectDeps(repo, root));
123
+ rows.push(...parsePackageJson(repo, root));
124
+ }
125
+ const filtered = input.package
126
+ ? rows.filter((r) => r.package === input.package)
127
+ : rows;
128
+ return {
129
+ rows: filtered.sort((a, b) => a.package.localeCompare(b.package) || a.repo.localeCompare(b.repo)),
130
+ notes,
131
+ };
132
+ }
133
+ //# sourceMappingURL=floor-pin-grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floor-pin-grid.js","sourceRoot":"","sources":["../../src/tools/floor-pin-grid.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { type RepoName } from "../config.js";
3
+ export declare const PatternAuditInput: z.ZodObject<{
4
+ patterns: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
5
+ }, "strip", z.ZodTypeAny, {
6
+ patterns?: string[] | undefined;
7
+ }, {
8
+ patterns?: string[] | undefined;
9
+ }>;
10
+ export interface PatternRow {
11
+ repo: RepoName;
12
+ pattern: string;
13
+ present: boolean;
14
+ evidence: string;
15
+ }
16
+ /** Return all pattern names this tool knows. */
17
+ export declare function knownPatterns(): {
18
+ name: string;
19
+ description: string;
20
+ }[];
21
+ export interface PatternAuditResult {
22
+ rows: PatternRow[];
23
+ notes: string[];
24
+ available_patterns: {
25
+ name: string;
26
+ description: string;
27
+ }[];
28
+ }
29
+ export declare function patternAudit(input: z.infer<typeof PatternAuditInput>): Promise<PatternAuditResult>;
30
+ //# sourceMappingURL=pattern-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-audit.d.ts","sourceRoot":"","sources":["../../src/tools/pattern-audit.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAe,KAAK,QAAQ,EAAuB,MAAM,cAAc,CAAC;AAE/E,eAAO,MAAM,iBAAiB;;;;;;EAO5B,CAAC;AAEH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiGD,gDAAgD;AAChD,wBAAgB,aAAa,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAKvE;AA+DD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7D;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,GACvC,OAAO,CAAC,kBAAkB,CAAC,CAwB7B"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * pattern_audit — return the (repo × pattern) matrix for the patterns
3
+ * the cross-stack audit found inconsistently applied.
4
+ *
5
+ * Each pattern has a probe function that decides {present, evidence}
6
+ * for a given repo working tree. Patterns are intentionally cheap:
7
+ * file-exists checks, regex matches against well-known manifests,
8
+ * or directory presence. No AST.
9
+ */
10
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { z } from "zod";
13
+ import { KNOWN_REPOS, resolveAllRepoPaths } from "../config.js";
14
+ export const PatternAuditInput = z.object({
15
+ patterns: z
16
+ .array(z.string())
17
+ .optional()
18
+ .describe("Optional subset of pattern names to evaluate. When omitted, evaluates all known patterns. See the description of `pattern` in each row for the full list."),
19
+ });
20
+ const PROBES = {
21
+ has_ci: {
22
+ description: "Repo has at least one .github/workflows/*.yml file. Audit finding #1 caught platform with zero workflows on main.",
23
+ probe: (root) => {
24
+ const dir = join(root, ".github", "workflows");
25
+ if (!existsSync(dir))
26
+ return { present: false, evidence: "no .github/workflows" };
27
+ const files = readdirSync(dir).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
28
+ return {
29
+ present: files.length > 0,
30
+ evidence: files.join(", ") || "(empty dir)",
31
+ };
32
+ },
33
+ },
34
+ has_openapi_contract_endpoint: {
35
+ description: "Repo exposes a /v1/contract HTTP endpoint (the codex/compile convention).",
36
+ probe: (root) => {
37
+ const hits = grepFirst(root, /["']\/v1\/contract["']/, ["src", "apps"]);
38
+ return hits
39
+ ? { present: true, evidence: hits }
40
+ : { present: false, evidence: "no /v1/contract reference" };
41
+ },
42
+ },
43
+ has_consume_surface_audit: {
44
+ description: "Repo has scripts/consume_surface_audit.py (compile-pdf's tripwire — banning re-implementation of an upstream's primitives).",
45
+ probe: (root) => {
46
+ const p = join(root, "scripts", "consume_surface_audit.py");
47
+ return existsSync(p)
48
+ ? { present: true, evidence: "scripts/consume_surface_audit.py" }
49
+ : { present: false, evidence: "not present" };
50
+ },
51
+ },
52
+ has_engine_purity_audit: {
53
+ description: "Repo has scripts/check_engine_purity.sh (lint-pdf's tripwire — banning analyzer-side imports of host services).",
54
+ probe: (root) => {
55
+ const p = join(root, "scripts", "check_engine_purity.sh");
56
+ return existsSync(p)
57
+ ? { present: true, evidence: "scripts/check_engine_purity.sh" }
58
+ : { present: false, evidence: "not present" };
59
+ },
60
+ },
61
+ has_openapi_descriptions_audit: {
62
+ description: "Repo has scripts/check_openapi_descriptions.py (lint-pdf's tripwire — every Pydantic Field must include description=).",
63
+ probe: (root) => {
64
+ const p = join(root, "scripts", "check_openapi_descriptions.py");
65
+ return existsSync(p)
66
+ ? { present: true, evidence: "scripts/check_openapi_descriptions.py" }
67
+ : { present: false, evidence: "not present" };
68
+ },
69
+ },
70
+ has_problem_details_errors: {
71
+ description: "Repo emits RFC 7807 Problem Details (audit finding #13: the org-aligned target error envelope).",
72
+ probe: (root) => {
73
+ const hits = grepFirst(root, /problem[-_]?details|application\/problem\+json|rfc7807/i, ["src", "apps", "packages"]);
74
+ return hits
75
+ ? { present: true, evidence: hits }
76
+ : { present: false, evidence: "no Problem Details reference" };
77
+ },
78
+ },
79
+ has_claude_md_deep: {
80
+ description: "CLAUDE.md is longer than 50 lines (a rough proxy for 'has architectural rules', not just process bullets).",
81
+ probe: (root) => {
82
+ const p = join(root, "CLAUDE.md");
83
+ if (!existsSync(p))
84
+ return { present: false, evidence: "no CLAUDE.md" };
85
+ const lines = readFileSync(p, "utf8").split("\n").length;
86
+ return {
87
+ present: lines >= 50,
88
+ evidence: `${lines} lines`,
89
+ };
90
+ },
91
+ },
92
+ };
93
+ /** Return all pattern names this tool knows. */
94
+ export function knownPatterns() {
95
+ return Object.entries(PROBES).map(([name, p]) => ({
96
+ name,
97
+ description: p.description,
98
+ }));
99
+ }
100
+ /** Walk a few top-level directories looking for the first match of `re`. */
101
+ function grepFirst(root, re, dirs) {
102
+ for (const dir of dirs) {
103
+ const p = join(root, dir);
104
+ if (!existsSync(p))
105
+ continue;
106
+ const hit = walkFirst(p, re, 0, 4);
107
+ if (hit)
108
+ return hit;
109
+ }
110
+ return null;
111
+ }
112
+ function walkFirst(dir, re, depth, maxDepth) {
113
+ if (depth > maxDepth)
114
+ return null;
115
+ let entries;
116
+ try {
117
+ entries = readdirSync(dir);
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ for (const name of entries) {
123
+ if (name === "node_modules" || name === ".git" || name === "dist")
124
+ continue;
125
+ const full = join(dir, name);
126
+ let s;
127
+ try {
128
+ s = statSync(full);
129
+ }
130
+ catch {
131
+ continue;
132
+ }
133
+ if (s.isDirectory()) {
134
+ const sub = walkFirst(full, re, depth + 1, maxDepth);
135
+ if (sub)
136
+ return sub;
137
+ }
138
+ else if (s.isFile() &&
139
+ /\.(ts|tsx|js|py|json|toml|yml|yaml|md)$/.test(name)) {
140
+ try {
141
+ const text = readFileSync(full, "utf8");
142
+ if (re.test(text)) {
143
+ // Find the first line that hits
144
+ const lines = text.split("\n");
145
+ for (let i = 0; i < lines.length; i++) {
146
+ const line = lines[i];
147
+ if (line !== undefined && re.test(line)) {
148
+ return `${full}:${i + 1}`;
149
+ }
150
+ }
151
+ return full;
152
+ }
153
+ }
154
+ catch {
155
+ // ignore binary/unreadable
156
+ }
157
+ }
158
+ }
159
+ return null;
160
+ }
161
+ export async function patternAudit(input) {
162
+ const paths = resolveAllRepoPaths();
163
+ const notes = [];
164
+ const probesToRun = input.patterns ?? Object.keys(PROBES);
165
+ const rows = [];
166
+ for (const repo of KNOWN_REPOS) {
167
+ const root = paths[repo];
168
+ if (!root) {
169
+ notes.push(`repo ${repo}: not configured`);
170
+ continue;
171
+ }
172
+ for (const name of probesToRun) {
173
+ const probe = PROBES[name];
174
+ if (!probe) {
175
+ notes.push(`pattern '${name}' is not known; ignored`);
176
+ continue;
177
+ }
178
+ const result = probe.probe(root);
179
+ rows.push({ repo, pattern: name, ...result });
180
+ }
181
+ }
182
+ return { rows, notes, available_patterns: knownPatterns() };
183
+ }
184
+ //# sourceMappingURL=pattern-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern-audit.js","sourceRoot":"","sources":["../../src/tools/pattern-audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * stack_health_grid — fetch /healthz (and /v1/contract when available)
3
+ * across the configured prod URLs for the stack, in parallel, and
4
+ * return a grid with status code + latency + version skew.
5
+ *
6
+ * Audit finding #15 in the cross-stack audit caught five different
7
+ * health/contract endpoint patterns. This tool branches per-service
8
+ * so the caller doesn't have to.
9
+ */
10
+ import { z } from "zod";
11
+ import { type RepoName } from "../config.js";
12
+ export declare const StackHealthGridInput: z.ZodObject<{
13
+ repos: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
14
+ timeout_ms: z.ZodDefault<z.ZodNumber>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ timeout_ms: number;
17
+ repos?: string[] | undefined;
18
+ }, {
19
+ repos?: string[] | undefined;
20
+ timeout_ms?: number | undefined;
21
+ }>;
22
+ export interface HealthRow {
23
+ repo: RepoName;
24
+ url: string;
25
+ endpoint: "healthz" | "v1/healthz" | "health" | "ready" | "v1/contract";
26
+ status: number | "timeout" | "error";
27
+ latency_ms: number | null;
28
+ body_summary: string;
29
+ }
30
+ export interface StackHealthGridResult {
31
+ rows: HealthRow[];
32
+ notes: string[];
33
+ }
34
+ export declare function stackHealthGrid(input: z.infer<typeof StackHealthGridInput>): Promise<StackHealthGridResult>;
35
+ //# sourceMappingURL=stack-health-grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack-health-grid.d.ts","sourceRoot":"","sources":["../../src/tools/stack-health-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAA0B,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAErE,eAAO,MAAM,oBAAoB;;;;;;;;;EAe/B,CAAC;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,SAAS,GAAG,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,aAAa,CAAC;IACxE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACrC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAsED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,GAC1C,OAAO,CAAC,qBAAqB,CAAC,CAmChC"}