codecartographer-pi 0.24.1 → 0.26.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 (55) hide show
  1. package/.codecarto/broadside/SKILL.md +20 -1
  2. package/.codecarto/workflow/scaffold-version.yaml +1 -1
  3. package/README.md +5 -4
  4. package/agent-skill/codecartographer/references/broadside.md +5 -1
  5. package/dist/core/broadside/client.d.ts +56 -0
  6. package/dist/core/broadside/client.js +200 -0
  7. package/dist/core/broadside/collect.d.ts +68 -0
  8. package/dist/core/broadside/collect.js +676 -0
  9. package/dist/core/broadside/constants.d.ts +51 -0
  10. package/dist/core/broadside/constants.js +74 -0
  11. package/dist/core/broadside/lenses.d.ts +31 -0
  12. package/dist/core/broadside/lenses.js +312 -0
  13. package/dist/core/broadside/models.d.ts +46 -0
  14. package/dist/core/broadside/models.js +321 -0
  15. package/dist/core/broadside/render.d.ts +20 -0
  16. package/dist/core/broadside/render.js +285 -0
  17. package/dist/core/broadside/repo.d.ts +58 -0
  18. package/dist/core/broadside/repo.js +592 -0
  19. package/dist/core/broadside/requests.d.ts +23 -0
  20. package/dist/core/broadside/requests.js +71 -0
  21. package/dist/core/broadside/results.d.ts +36 -0
  22. package/dist/core/broadside/results.js +163 -0
  23. package/dist/core/broadside/schemas.d.ts +2 -0
  24. package/dist/core/broadside/schemas.js +342 -0
  25. package/dist/core/broadside/state.d.ts +99 -0
  26. package/dist/core/broadside/state.js +384 -0
  27. package/dist/core/broadside/submit.d.ts +30 -0
  28. package/dist/core/broadside/submit.js +350 -0
  29. package/dist/core/broadside/types.d.ts +491 -0
  30. package/dist/core/broadside/types.js +107 -0
  31. package/dist/core/{broadside-verify.d.ts → broadside/verify.d.ts} +23 -2
  32. package/dist/core/{broadside-verify.js → broadside/verify.js} +43 -5
  33. package/dist/core/broadside.d.ts +14 -890
  34. package/dist/core/broadside.js +25 -3564
  35. package/dist/core/completion.js +91 -72
  36. package/dist/core/dashboard-writer.js +9 -1
  37. package/dist/core/index.d.ts +0 -1
  38. package/dist/core/index.js +0 -1
  39. package/dist/core/library.d.ts +24 -1
  40. package/dist/core/library.js +46 -15
  41. package/dist/core/orchestrator-config.js +22 -8
  42. package/dist/core/status.d.ts +42 -23
  43. package/dist/core/status.js +163 -137
  44. package/dist/core/workspace.d.ts +2 -0
  45. package/dist/core/workspace.js +49 -25
  46. package/dist/core/yaml.js +9 -3
  47. package/dist/extensions/codecarto/auto-runner.d.ts +7 -0
  48. package/dist/extensions/codecarto/auto-runner.js +54 -23
  49. package/dist/extensions/codecarto/broadside-flags.d.ts +3 -1
  50. package/dist/extensions/codecarto/broadside-flags.js +13 -0
  51. package/dist/extensions/codecarto/index.js +13 -7
  52. package/dist/extensions/codecarto/phase-compaction.js +6 -2
  53. package/dist/mcp-server/server.d.ts +1 -0
  54. package/dist/mcp-server/server.js +28 -5
  55. package/package.json +1 -1
@@ -0,0 +1,51 @@
1
+ export declare const BROADSIDE_MODEL = "google/gemini-3.7-flash:batch";
2
+ export declare const BROADSIDE_BATCH_URL = "https://openrouter.ai/api/beta/batches";
3
+ export declare const BROADSIDE_DIR = "broadside";
4
+ /** Name Broad-Side answers to on the skill surfaces. Not a post-pipeline skill — see readBroadsideSkill. */
5
+ export declare const BROADSIDE_SKILL_NAME = "broadside";
6
+ export declare const BROADSIDE_STATE_FILE = "state.json";
7
+ export declare const BROADSIDE_CONFIG_FILE = "config.yaml";
8
+ export declare const BROADSIDE_STATE_SCHEMA_VERSION = 1;
9
+ export declare const BROADSIDE_INPUT_PRICE_PER_M = 0.375;
10
+ export declare const BROADSIDE_OUTPUT_PRICE_PER_M = 1.875;
11
+ export declare const BROADSIDE_MODELS_URL = "https://openrouter.ai/api/v1/models";
12
+ export declare const BROADSIDE_BENCHMARKS_URL = "https://openrouter.ai/api/v1/benchmarks";
13
+ export declare const BROADSIDE_CATALOG_CACHE_FILE = "model-catalog.json";
14
+ /**
15
+ * What this repository's own submits learned about batch endpoints: which
16
+ * `:batch` ids OpenRouter accepted a job for and which it refused with
17
+ * "does not have a :batch endpoint". The catalog cannot tell the two apart
18
+ * (#141), so the `models` action annotates its rows from this file.
19
+ */
20
+ export declare const BROADSIDE_ENDPOINTS_FILE = "batch-endpoints.json";
21
+ export declare const BROADSIDE_CATALOG_CACHE_TTL_MS: number;
22
+ export declare const BROADSIDE_LENS_IDS: readonly ["architecture", "api", "security", "defect", "conventions", "porting"];
23
+ export type BroadsideLensId = (typeof BROADSIDE_LENS_IDS)[number];
24
+ export declare const BROADSIDE_POLL_INTERVAL_MS = 15000;
25
+ export declare const BROADSIDE_DEFAULT_POLL_BUDGET_MS: number;
26
+ /**
27
+ * The run expense limit in USD a repository gets before it configures one.
28
+ * Pi asks a human before submitting over the estimate; the MCP surface cannot,
29
+ * and shipped with no limit at all, so a host calling submit with the stock
30
+ * config spent whatever the estimate came to (#231). One dollar covers a
31
+ * six-lens run of a repository this size with room to spare; a larger one
32
+ * raises `max_cost` in config.yaml, passes `max_cost` on the call, or sets it
33
+ * to 0 for no limit.
34
+ */
35
+ export declare const BROADSIDE_DEFAULT_MAX_COST = 1;
36
+ /**
37
+ * Batch statuses that will never produce a result.
38
+ *
39
+ * Deliberately excludes the synthetic `timeout` this module returns when a poll
40
+ * budget expires: that batch is still running server-side and has already been
41
+ * charged, so callers must come back for it rather than retire it.
42
+ */
43
+ export declare const BROADSIDE_DEAD_BATCH_STATUSES: string[];
44
+ /**
45
+ * Batch entry statuses collect never polls again: the dead ones above, plus
46
+ * `completed`, plus the two a submit assigns without a batch (`skipped`: no
47
+ * matching files; `rejected`: the provider refused it). The 0.19.1 changelog
48
+ * called the dead set "a named constant rather than two hand-maintained
49
+ * lists"; this set was still three literal copies (self-audit sem 5.8).
50
+ */
51
+ export declare const BROADSIDE_TERMINAL_ENTRY_STATUSES: string[];
@@ -0,0 +1,74 @@
1
+ // Broad-Side constants: model, endpoints, file names, defaults.
2
+ //
3
+ // Split out of core/broadside.ts (#339); the barrel there re-exports every
4
+ // name, so `core/index.ts` and the tests see one module as before.
5
+ // ---------- constants ----------
6
+ export const BROADSIDE_MODEL = "google/gemini-3.7-flash:batch";
7
+ export const BROADSIDE_BATCH_URL = "https://openrouter.ai/api/beta/batches";
8
+ export const BROADSIDE_DIR = "broadside"; // relative to .codecarto/
9
+ /** Name Broad-Side answers to on the skill surfaces. Not a post-pipeline skill — see readBroadsideSkill. */
10
+ export const BROADSIDE_SKILL_NAME = "broadside";
11
+ export const BROADSIDE_STATE_FILE = "state.json";
12
+ export const BROADSIDE_CONFIG_FILE = "config.yaml";
13
+ export const BROADSIDE_STATE_SCHEMA_VERSION = 1;
14
+ // Per-token pricing in USD (OpenRouter, google/gemini-3.7-flash:batch).
15
+ // OpenRouter's listed rates for the `:batch` variant, which already carry the
16
+ // batch discount — the sync model is $0.75/$3.75. These were half these values
17
+ // until a live run compared them against the catalog: the batch discount had
18
+ // been applied a second time by hand, so every estimate for the default model
19
+ // came out at half its true cost and `max_cost` bound at twice what the user
20
+ // asked for. They are the offline fallback only; the live catalog wins.
21
+ export const BROADSIDE_INPUT_PRICE_PER_M = 0.375;
22
+ export const BROADSIDE_OUTPUT_PRICE_PER_M = 1.875;
23
+ // OpenRouter's public model catalog; pricing, context, and capabilities live
24
+ // per model id. The benchmarks endpoint adds coding/intelligence indices.
25
+ export const BROADSIDE_MODELS_URL = "https://openrouter.ai/api/v1/models";
26
+ export const BROADSIDE_BENCHMARKS_URL = "https://openrouter.ai/api/v1/benchmarks";
27
+ export const BROADSIDE_CATALOG_CACHE_FILE = "model-catalog.json";
28
+ /**
29
+ * What this repository's own submits learned about batch endpoints: which
30
+ * `:batch` ids OpenRouter accepted a job for and which it refused with
31
+ * "does not have a :batch endpoint". The catalog cannot tell the two apart
32
+ * (#141), so the `models` action annotates its rows from this file.
33
+ */
34
+ export const BROADSIDE_ENDPOINTS_FILE = "batch-endpoints.json";
35
+ export const BROADSIDE_CATALOG_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
36
+ export const BROADSIDE_LENS_IDS = [
37
+ "architecture",
38
+ "api",
39
+ "security",
40
+ "defect",
41
+ "conventions",
42
+ "porting",
43
+ ];
44
+ export const BROADSIDE_POLL_INTERVAL_MS = 15_000;
45
+ export const BROADSIDE_DEFAULT_POLL_BUDGET_MS = 25 * 60 * 1000;
46
+ /**
47
+ * The run expense limit in USD a repository gets before it configures one.
48
+ * Pi asks a human before submitting over the estimate; the MCP surface cannot,
49
+ * and shipped with no limit at all, so a host calling submit with the stock
50
+ * config spent whatever the estimate came to (#231). One dollar covers a
51
+ * six-lens run of a repository this size with room to spare; a larger one
52
+ * raises `max_cost` in config.yaml, passes `max_cost` on the call, or sets it
53
+ * to 0 for no limit.
54
+ */
55
+ export const BROADSIDE_DEFAULT_MAX_COST = 1;
56
+ // Batch and entry status sets, here rather than beside the client that
57
+ // produces them so the state module can rank entries without importing a
58
+ // module above it in the layer order (#371).
59
+ /**
60
+ * Batch statuses that will never produce a result.
61
+ *
62
+ * Deliberately excludes the synthetic `timeout` this module returns when a poll
63
+ * budget expires: that batch is still running server-side and has already been
64
+ * charged, so callers must come back for it rather than retire it.
65
+ */
66
+ export const BROADSIDE_DEAD_BATCH_STATUSES = ["failed", "expired", "cancelled", "auth-failed"];
67
+ /**
68
+ * Batch entry statuses collect never polls again: the dead ones above, plus
69
+ * `completed`, plus the two a submit assigns without a batch (`skipped`: no
70
+ * matching files; `rejected`: the provider refused it). The 0.19.1 changelog
71
+ * called the dead set "a named constant rather than two hand-maintained
72
+ * lists"; this set was still three literal copies (self-audit sem 5.8).
73
+ */
74
+ export const BROADSIDE_TERMINAL_ENTRY_STATUSES = ["completed", ...BROADSIDE_DEAD_BATCH_STATUSES, "skipped", "rejected"];
@@ -0,0 +1,31 @@
1
+ import { type BroadsideLensId } from "./constants.ts";
2
+ import { type BroadsideReasoning, type RepoInfo } from "./types.ts";
3
+ export type LensDefinition = {
4
+ id: BroadsideLensId;
5
+ name: string;
6
+ description: string;
7
+ schemaName: string;
8
+ sliceBy: "none" | "directory" | "auto";
9
+ maxChars: number;
10
+ maxTokens: number;
11
+ reasoning?: BroadsideReasoning;
12
+ skipTestFiles?: boolean;
13
+ globsFor: (info: RepoInfo) => string[];
14
+ /**
15
+ * Where to look when `globsFor` matches no source file (#319). The
16
+ * security and api lenses target server/, auth, and middleware paths
17
+ * because that is where the trust boundary usually lives; a service whose
18
+ * server is `src/server.js` matched none of them and got no security
19
+ * review at all. A match that is only documents is the same starvation:
20
+ * `SECURITY.md` satisfied the security lens on CodeCartographer itself,
21
+ * which then reviewed a policy and reported zero findings. The fallback
22
+ * is the language's whole source set, added to whatever did match —
23
+ * priced as such, and said so in the estimate, the run record, and the
24
+ * prompt.
25
+ */
26
+ fallbackGlobsFor?: (info: RepoInfo) => string[];
27
+ systemPrompt: (info: RepoInfo) => string;
28
+ userPrompt: (info: RepoInfo, source: string, moduleName: string) => string;
29
+ };
30
+ export declare function getLens(lensId: BroadsideLensId): LensDefinition;
31
+ export declare function listLenses(): LensDefinition[];
@@ -0,0 +1,312 @@
1
+ // The lens registry: per-language defect and convention profiles, each lens's scope, prompts, and caps.
2
+ //
3
+ // Split out of core/broadside.ts (#339); the barrel there re-exports every
4
+ // name, so `core/index.ts` and the tests see one module as before.
5
+ import { BROADSIDE_LENS_IDS } from "./constants.js";
6
+ const TS_PROFILE = {
7
+ defectPatterns: [
8
+ "Null/undefined dereference risks (unchecked optional access)",
9
+ "Error handling gaps (unhandled promise rejections, swallowed catches)",
10
+ "Resource leaks (unclosed handles, missing cleanup, dangling timers/listeners)",
11
+ "Race conditions (shared mutable state, async interleavings without guards)",
12
+ "Integer/precision assumptions in arithmetic",
13
+ "Unsafe type assumptions (as-casts, any leaks, non-null assertions)",
14
+ "Panic-prone code (out-of-bounds access, runtime TypeError paths)",
15
+ "Timezone/locale assumptions",
16
+ ],
17
+ conventionCategories: [
18
+ { key: "packages", label: "modules and imports" },
19
+ { key: "types", label: "interfaces and type aliases" },
20
+ { key: "functions", label: "functions (camelCase), components (PascalCase)" },
21
+ { key: "variables", label: "variables and constants (camelCase)" },
22
+ { key: "files", label: "file naming (kebab vs camel) and folder organization" },
23
+ { key: "tests", label: "test files (*.test.ts, describe/it patterns)" },
24
+ ],
25
+ idiomHints: ["strict null checks usage", "async/await vs promise chains", "dependency injection patterns"],
26
+ };
27
+ const LANGUAGE_PROFILES = {
28
+ go: {
29
+ defectPatterns: [
30
+ "Nil pointer dereference risks (unchecked returns, missing nil guards)",
31
+ "Error handling gaps (ignored errors, deferred errors unchecked)",
32
+ "Resource leaks (unclosed files, connections, goroutines without ctx)",
33
+ "Race conditions (shared state without sync, channel misuse)",
34
+ "Integer overflow/underflow in arithmetic or bounds",
35
+ "Unsafe type assertions without ok check",
36
+ "Panic-prone code (slice out of bounds, map access without ok)",
37
+ "Timezone/locale assumptions",
38
+ ],
39
+ conventionCategories: [
40
+ { key: "packages", label: "packages" },
41
+ { key: "types", label: "types and interfaces" },
42
+ { key: "functions", label: "functions and methods" },
43
+ { key: "variables", label: "variables and fields" },
44
+ { key: "files", label: "file and directory organization" },
45
+ { key: "tests", label: "test files and table-driven tests" },
46
+ ],
47
+ idiomHints: ["error wrapping with %w", "zero-value construction"],
48
+ },
49
+ python: {
50
+ defectPatterns: [
51
+ "None dereference risks (unchecked optional returns, AttributeError paths)",
52
+ "Exception handling gaps (bare except, swallowed exceptions, broad catch-all)",
53
+ "Resource leaks (unclosed files, sockets, connections, context managers)",
54
+ "Race conditions (shared mutable state, threading without locks, async pitfalls)",
55
+ "Integer/float precision assumptions in arithmetic",
56
+ "Unsafe type assumptions (unpacking mismatches, isinstance without fallback)",
57
+ "Panic-prone code (IndexError/KeyError paths, unbounded slicing)",
58
+ "Timezone/locale assumptions (naive datetimes)",
59
+ ],
60
+ conventionCategories: [
61
+ { key: "packages", label: "modules and packages" },
62
+ { key: "types", label: "classes and type hints" },
63
+ { key: "functions", label: "functions and methods (snake_case vs camelCase)" },
64
+ { key: "variables", label: "variables and constants" },
65
+ { key: "files", label: "file and module organization" },
66
+ { key: "tests", label: "test files (pytest fixtures, naming)" },
67
+ ],
68
+ idiomHints: ["dunder method usage", "context manager idioms", "dataclass/pydantic models"],
69
+ },
70
+ rust: {
71
+ defectPatterns: [
72
+ "Unwrap/expect panics on fallible paths",
73
+ "Error handling gaps (swallowed Results, lossy conversions)",
74
+ "Resource leaks (unclosed handles, drop order assumptions)",
75
+ "Data races and Send/Sync violations (unsafe blocks, interior mutability misuse)",
76
+ "Integer overflow/underflow (arithmetic, casting)",
77
+ "Unsafe type assumptions (transmute/casts without invariants)",
78
+ "Panic-prone code (indexing, slicing, unreachable! in library paths)",
79
+ "Timezone/locale assumptions",
80
+ ],
81
+ conventionCategories: [
82
+ { key: "packages", label: "crates and modules" },
83
+ { key: "types", label: "structs, enums, and traits" },
84
+ { key: "functions", label: "functions and methods (snake_case)" },
85
+ { key: "variables", label: "variables and constants (SCREAMING_SNAKE)" },
86
+ { key: "files", label: "module file organization" },
87
+ { key: "tests", label: "test modules and #[cfg(test)] patterns" },
88
+ ],
89
+ idiomHints: ["Result/Option handling with ?", "builder patterns", "trait-based extension"],
90
+ },
91
+ typescript: TS_PROFILE,
92
+ javascript: TS_PROFILE,
93
+ default: {
94
+ defectPatterns: [
95
+ "Null/undefined dereference risks (unchecked optional access)",
96
+ "Error handling gaps (ignored or swallowed errors)",
97
+ "Resource leaks (unclosed files, connections, handles)",
98
+ "Race conditions (shared mutable state without synchronization)",
99
+ "Integer overflow/underflow in arithmetic or bounds",
100
+ "Unsafe type assumptions and unchecked casts",
101
+ "Panic-prone code (out-of-bounds access, missing keys)",
102
+ "Timezone/locale assumptions",
103
+ ],
104
+ conventionCategories: [
105
+ { key: "packages", label: "modules, packages, or namespaces" },
106
+ { key: "types", label: "types, classes, and interfaces" },
107
+ { key: "functions", label: "functions and methods" },
108
+ { key: "variables", label: "variables and constants" },
109
+ { key: "files", label: "file and directory organization" },
110
+ { key: "tests", label: "test files and test organization" },
111
+ ],
112
+ idiomHints: [],
113
+ },
114
+ };
115
+ function languageProfile(language) {
116
+ return LANGUAGE_PROFILES[language] ?? LANGUAGE_PROFILES.default;
117
+ }
118
+ const LENSES = {
119
+ architecture: {
120
+ id: "architecture",
121
+ name: "Architecture, tech stack & module map",
122
+ description: "Repo-wide structural analysis from the manifest, entry point, README, and file tree.",
123
+ schemaName: "architecture",
124
+ sliceBy: "none",
125
+ maxChars: 0, // repo-info lens; no file slurping
126
+ maxTokens: 8000,
127
+ globsFor: () => [],
128
+ systemPrompt: () => "You are a senior software architect performing a structural analysis of a " +
129
+ "codebase. You receive the project manifest, entry point, README excerpt, and " +
130
+ "file tree. Return a JSON object following the architecture_report schema " +
131
+ "exactly. All findings must be traceable to the provided files — cite file " +
132
+ "paths. If you can't determine something, say so rather than guessing.",
133
+ userPrompt: (info) => {
134
+ const manifest = info.manifest
135
+ ? `## ${info.manifest.path}\n\`\`\`\n${info.manifest.content}\n\`\`\`\n\n`
136
+ : "## Manifest\n[no manifest found]\n\n";
137
+ return ("Analyze the architecture of this project.\n\n" +
138
+ manifest +
139
+ `## Entry point\n\`\`\`\n${info.mainFile || "[missing]"}\n\`\`\`\n\n` +
140
+ `## README (first 4000 chars)\n${info.readmeFirst || "[missing]"}\n\n` +
141
+ `## File tree (depth 3, capped)\n${info.fileTree || "[missing]"}\n\n` +
142
+ "## File counts by extension\n```json\n" +
143
+ JSON.stringify(info.fileCounts) +
144
+ "\n```\n\n" +
145
+ "Return the architecture_report JSON schema.");
146
+ },
147
+ },
148
+ api: {
149
+ id: "api",
150
+ name: "API surface audit",
151
+ description: "Endpoint catalog, request/response types, auth flow, error handling.",
152
+ schemaName: "api_surface",
153
+ sliceBy: "none",
154
+ maxChars: 70_000,
155
+ maxTokens: 8000,
156
+ skipTestFiles: true,
157
+ globsFor: (info) => info.language === "go"
158
+ ? ["server/**/*.go", "server/*.go", "api/**/*.go", "api/*.go"]
159
+ : [
160
+ "server/**",
161
+ "api/**",
162
+ "src/server/**",
163
+ "src/api/**",
164
+ "mcp-server/**",
165
+ "**/*routes*",
166
+ "**/*router*",
167
+ "**/*handler*",
168
+ "**/*endpoint*",
169
+ ],
170
+ fallbackGlobsFor: (info) => [info.sourceGlob],
171
+ systemPrompt: () => "You are a senior API auditor. Given source files from an HTTP server, " +
172
+ "extract every HTTP endpoint (method, path, handler function, auth requirement) " +
173
+ "and every key request/response data type. Return a JSON object following the " +
174
+ "api_surface_report schema exactly. Cite specific file:line locations.",
175
+ userPrompt: (info, source, moduleName) => "Extract the full API surface from these server source files:\n\n" +
176
+ source +
177
+ "\n\nReturn the api_surface_report JSON schema.",
178
+ },
179
+ security: {
180
+ id: "security",
181
+ name: "Security review",
182
+ description: "Auth, authorization, input validation, TLS, secrets, trust boundaries.",
183
+ schemaName: "security",
184
+ sliceBy: "none",
185
+ maxChars: 70_000,
186
+ maxTokens: 8000,
187
+ skipTestFiles: true,
188
+ globsFor: (info) => info.language === "go"
189
+ ? ["server/**/*.go", "server/*.go", "**/auth*.go", "**/middleware/**/*.go", "SECURITY.md"]
190
+ : ["server/**", "**/auth*", "**/middleware/**", "SECURITY.md"],
191
+ fallbackGlobsFor: (info) => [info.sourceGlob],
192
+ systemPrompt: () => "You are a security engineer performing a first-pass review of a codebase. " +
193
+ "Given source files, identify potential security issues — focusing on " +
194
+ "authentication, authorization, input validation, TLS, secrets handling, " +
195
+ "and trust boundaries. Return a JSON object following the security_review_report " +
196
+ "schema. Rate severity as critical/high/medium/low. Be specific: cite file:line. " +
197
+ "If the provided files don't cover an area, state the gap in coverage_note.",
198
+ userPrompt: (info, source, moduleName) => "Review these server source files for security issues:\n\n" +
199
+ source +
200
+ "\n\nReturn the security_review_report JSON schema.",
201
+ },
202
+ defect: {
203
+ id: "defect",
204
+ name: "Mechanical defect scan",
205
+ description: "Nil derefs, error gaps, leaks, races, panics — pattern-based, sliced per module.",
206
+ schemaName: "defect_mechanical",
207
+ sliceBy: "auto",
208
+ maxChars: 60_000,
209
+ maxTokens: 6000,
210
+ globsFor: (info) => [info.sourceGlob],
211
+ systemPrompt: (info) => {
212
+ const profile = languageProfile(info.language);
213
+ const patterns = profile.defectPatterns.map((p, i) => ` ${i + 1}. ${p}`).join("\n");
214
+ return (`You are a senior code reviewer performing an automated defect scan on ${info.language} ` +
215
+ "source files. Look for these specific patterns:\n" +
216
+ patterns +
217
+ "\n\n" +
218
+ "Return a JSON object following the defect_scan_report schema. " +
219
+ "Cite file:line for every finding. List which patterns you checked. " +
220
+ "If the code looks clean for a pattern, say so rather than staying silent. " +
221
+ "Prefer precision over volume — 3 solid findings beat 15 vague ones.\n\n" +
222
+ // The verification pass (#143) confirmed 2 of the 12 top findings a
223
+ // scan produced with the paragraph above alone; the other ten were
224
+ // casts and assertions every caller satisfied, guards that lived one
225
+ // call away, or environments the project does not target. The rubric
226
+ // the verifier applies is asked of the scan itself, up front.
227
+ "A finding is a reachable failure: name in the description the concrete input, call site, or sequence " +
228
+ "that reaches it and what then goes wrong. A cast, assertion, `any`, or non-null `!` that every caller " +
229
+ "you can see satisfies, a hypothetical about a runtime or environment the project does not target, or a " +
230
+ "style or type-hygiene observation is not a defect — leave it out, or if it is worth a note, report it " +
231
+ "at severity low under the pattern name `type-hygiene` so it ranks apart from reachable failures. " +
232
+ "When the guard you looked for may live in another module, say which check you could not find " +
233
+ "rather than asserting it is absent; severity high or medium is for failures you traced to a trigger.");
234
+ },
235
+ userPrompt: (info, source, moduleName) => `Scan this ${info.language} module for mechanical defects.\n\n` +
236
+ `Module: ${moduleName}\n\n` +
237
+ "## Source files\n\n" +
238
+ source +
239
+ "\n\nReturn the defect_scan_report JSON schema.",
240
+ },
241
+ conventions: {
242
+ id: "conventions",
243
+ name: "Convention extraction",
244
+ description: "Naming, error handling, idioms, inconsistencies, promotable conventions.",
245
+ schemaName: "conventions",
246
+ sliceBy: "auto",
247
+ maxChars: 60_000,
248
+ maxTokens: 6000,
249
+ globsFor: (info) => [info.sourceGlob],
250
+ systemPrompt: (info) => {
251
+ const profile = languageProfile(info.language);
252
+ const categories = profile.conventionCategories.map((c) => `${c.key} (${c.label})`).join(", ");
253
+ const idiomHint = profile.idiomHints.length > 0
254
+ ? ` Keep an eye out for ${info.language} idioms such as ${profile.idiomHints.join(", ")}.`
255
+ : "";
256
+ return (`You are a code style analyst extracting conventions from ${info.language} source files. ` +
257
+ "Catalog naming conventions per category — " + categories + " — plus the dominant " +
258
+ "error-handling pattern, logging approach, test organization patterns, file/package " +
259
+ "organization rules, and recurring idioms." + idiomHint +
260
+ " Also flag inconsistencies — places where the same convention is violated. " +
261
+ "If you find well-established conventions worth formalizing, list them as " +
262
+ "promotable_conventions with a title, rule, and evidence from the code. " +
263
+ "Return a JSON object following the conventions_report schema.");
264
+ },
265
+ userPrompt: (info, source, moduleName) => "Extract coding conventions from this module.\n\n" +
266
+ `Module: ${moduleName}\n\n` +
267
+ "## Source files\n\n" +
268
+ source +
269
+ "\n\nReturn the conventions_report JSON schema.",
270
+ },
271
+ porting: {
272
+ id: "porting",
273
+ name: "Porting surface assessment",
274
+ description: "Platform coupling, external deps, build complexity, porting risk areas.",
275
+ schemaName: "porting",
276
+ sliceBy: "auto",
277
+ maxChars: 60_000,
278
+ maxTokens: 6000,
279
+ skipTestFiles: true,
280
+ globsFor: (info) => [
281
+ info.sourceGlob,
282
+ "**/*.c",
283
+ "**/*.h",
284
+ "**/*.cpp",
285
+ "**/*.cc",
286
+ "**/*.m",
287
+ "**/*.mm",
288
+ "**/CMakeLists.txt",
289
+ "**/*.cmake",
290
+ "go.mod",
291
+ ],
292
+ systemPrompt: () => "You are a software portability analyst. Examine source files and " +
293
+ "identify everything that ties this codebase to a specific platform, OS, " +
294
+ "architecture, or external dependency. Catalog: platform-specific build tags, " +
295
+ "FFI usage, OS-specific syscalls, external library bindings, and " +
296
+ "compile-time constants that encode platform assumptions. " +
297
+ "For each external dependency, note whether it could be replaced by a " +
298
+ "cross-platform alternative. Assess the build system complexity. " +
299
+ "Return a JSON object following the porting_surface_report schema.",
300
+ userPrompt: (info, source, moduleName) => "Assess porting surface for this module.\n\n" +
301
+ `Module: ${moduleName}\n\n` +
302
+ "## Source files\n\n" +
303
+ source +
304
+ "\n\nReturn the porting_surface_report JSON schema.",
305
+ },
306
+ };
307
+ export function getLens(lensId) {
308
+ return LENSES[lensId];
309
+ }
310
+ export function listLenses() {
311
+ return BROADSIDE_LENS_IDS.map((id) => LENSES[id]);
312
+ }
@@ -0,0 +1,46 @@
1
+ import { type BroadsideCatalogResult, type BroadsideConfig, type CatalogEntry, type CodingBenchmarks, type ModelPricing } from "./types.ts";
2
+ import { type FetchLike } from "./client.ts";
3
+ /** The catalog cache schema this build writes; a file from another is not read. */
4
+ export declare const BROADSIDE_CATALOG_CACHE_SCHEMA = 3;
5
+ /** One model's most recent submit outcome, as remembered in {@link BROADSIDE_ENDPOINTS_FILE}. */
6
+ export type BatchEndpointRecord = {
7
+ status: "accepted" | "rejected";
8
+ /** ISO timestamp of the submit that produced this record. */
9
+ at: string;
10
+ /** The provider's refusal, for a rejected endpoint. */
11
+ error?: string;
12
+ };
13
+ export declare function readBatchEndpoints(broadsideDir: string): Promise<Record<string, BatchEndpointRecord>>;
14
+ /**
15
+ * The refusal OpenRouter returns for a catalog id that has no batch endpoint
16
+ * behind it. Matched loosely: the message is the only signal there is.
17
+ */
18
+ /**
19
+ * Remember what a submit learned about each model it posted to. An accepted
20
+ * job proves the endpoint exists; a "does not have a :batch endpoint"
21
+ * refusal proves it does not. Any other rejection (quota, malformed request,
22
+ * auth) says nothing about the endpoint and leaves the record alone.
23
+ */
24
+ export declare function recordBatchEndpoints(broadsideDir: string, outcomes: Array<{
25
+ model: string;
26
+ batchId: string;
27
+ error?: unknown;
28
+ }>): Promise<void>;
29
+ export declare function builtInCatalogEntry(model: string): CatalogEntry | null;
30
+ export declare function builtInPricing(model: string): ModelPricing | null;
31
+ export declare function resolveCatalogEntry(broadsideDir: string, config: BroadsideConfig, model: string, apiKey: string, fetcher?: FetchLike): Promise<BroadsideCatalogResult>;
32
+ export declare function resolveModelPricing(broadsideDir: string, config: BroadsideConfig, model: string, apiKey: string, fetcher?: FetchLike): Promise<ModelPricing>;
33
+ /** Base slug with the OpenRouter variant suffix (e.g. `:batch`) stripped. */
34
+ export declare function baseSlug(modelId: string): string;
35
+ export declare function fetchCodingBenchmarks(apiKey: string, fetcher?: FetchLike): Promise<CodingBenchmarks | null>;
36
+ export declare function listBatchModels(broadsideDir: string, config: BroadsideConfig, apiKey: string, opts?: {
37
+ includeBenchmarks?: boolean;
38
+ fetcher?: FetchLike;
39
+ }): Promise<{
40
+ entries: CatalogEntry[];
41
+ source: string;
42
+ benchmarks: CodingBenchmarks | null;
43
+ defaultModel: string;
44
+ /** This repository's remembered submit outcomes per model, from {@link BROADSIDE_ENDPOINTS_FILE}. */
45
+ endpoints: Record<string, BatchEndpointRecord>;
46
+ }>;