@polishd/next 0.1.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 (127) hide show
  1. package/AGENTS.md +262 -0
  2. package/DATABASE.md +147 -0
  3. package/LICENSE +21 -0
  4. package/README.md +310 -0
  5. package/bin/init.mjs +224 -0
  6. package/dist/ai/actions.d.ts +23 -0
  7. package/dist/ai/actions.d.ts.map +1 -0
  8. package/dist/ai/actions.js +58 -0
  9. package/dist/ai/actions.js.map +1 -0
  10. package/dist/ai/digest.d.ts +9 -0
  11. package/dist/ai/digest.d.ts.map +1 -0
  12. package/dist/ai/digest.js +108 -0
  13. package/dist/ai/digest.js.map +1 -0
  14. package/dist/ai/github.d.ts +51 -0
  15. package/dist/ai/github.d.ts.map +1 -0
  16. package/dist/ai/github.js +175 -0
  17. package/dist/ai/github.js.map +1 -0
  18. package/dist/ai/guard.d.ts +43 -0
  19. package/dist/ai/guard.d.ts.map +1 -0
  20. package/dist/ai/guard.js +57 -0
  21. package/dist/ai/guard.js.map +1 -0
  22. package/dist/ai/issues.d.ts +21 -0
  23. package/dist/ai/issues.d.ts.map +1 -0
  24. package/dist/ai/issues.js +291 -0
  25. package/dist/ai/issues.js.map +1 -0
  26. package/dist/ai/profile.d.ts +26 -0
  27. package/dist/ai/profile.d.ts.map +1 -0
  28. package/dist/ai/profile.js +160 -0
  29. package/dist/ai/profile.js.map +1 -0
  30. package/dist/ai/providers.d.ts +40 -0
  31. package/dist/ai/providers.d.ts.map +1 -0
  32. package/dist/ai/providers.js +142 -0
  33. package/dist/ai/providers.js.map +1 -0
  34. package/dist/ai/scan.d.ts +38 -0
  35. package/dist/ai/scan.d.ts.map +1 -0
  36. package/dist/ai/scan.js +274 -0
  37. package/dist/ai/scan.js.map +1 -0
  38. package/dist/ai/settings.d.ts +31 -0
  39. package/dist/ai/settings.d.ts.map +1 -0
  40. package/dist/ai/settings.js +182 -0
  41. package/dist/ai/settings.js.map +1 -0
  42. package/dist/ai/summary.d.ts +26 -0
  43. package/dist/ai/summary.d.ts.map +1 -0
  44. package/dist/ai/summary.js +260 -0
  45. package/dist/ai/summary.js.map +1 -0
  46. package/dist/ai/types.d.ts +188 -0
  47. package/dist/ai/types.d.ts.map +1 -0
  48. package/dist/ai/types.js +13 -0
  49. package/dist/ai/types.js.map +1 -0
  50. package/dist/client/init.d.ts +14 -0
  51. package/dist/client/init.d.ts.map +1 -0
  52. package/dist/client/init.js +274 -0
  53. package/dist/client/init.js.map +1 -0
  54. package/dist/client/monitor.d.ts +23 -0
  55. package/dist/client/monitor.d.ts.map +1 -0
  56. package/dist/client/monitor.js +176 -0
  57. package/dist/client/monitor.js.map +1 -0
  58. package/dist/client.d.ts +11 -0
  59. package/dist/client.d.ts.map +1 -0
  60. package/dist/client.js +10 -0
  61. package/dist/client.js.map +1 -0
  62. package/dist/config.d.ts +36 -0
  63. package/dist/config.d.ts.map +1 -0
  64. package/dist/config.js +26 -0
  65. package/dist/config.js.map +1 -0
  66. package/dist/dashboard/elements.d.ts +6 -0
  67. package/dist/dashboard/elements.d.ts.map +1 -0
  68. package/dist/dashboard/elements.js +35 -0
  69. package/dist/dashboard/elements.js.map +1 -0
  70. package/dist/dashboard/features.d.ts +6 -0
  71. package/dist/dashboard/features.d.ts.map +1 -0
  72. package/dist/dashboard/features.js +28 -0
  73. package/dist/dashboard/features.js.map +1 -0
  74. package/dist/dashboard/index.d.ts +54 -0
  75. package/dist/dashboard/index.d.ts.map +1 -0
  76. package/dist/dashboard/index.js +140 -0
  77. package/dist/dashboard/index.js.map +1 -0
  78. package/dist/dashboard/journeys.d.ts +5 -0
  79. package/dist/dashboard/journeys.d.ts.map +1 -0
  80. package/dist/dashboard/journeys.js +158 -0
  81. package/dist/dashboard/journeys.js.map +1 -0
  82. package/dist/dashboard/pages.d.ts +5 -0
  83. package/dist/dashboard/pages.d.ts.map +1 -0
  84. package/dist/dashboard/pages.js +104 -0
  85. package/dist/dashboard/pages.js.map +1 -0
  86. package/dist/dashboard/summary.d.ts +13 -0
  87. package/dist/dashboard/summary.d.ts.map +1 -0
  88. package/dist/dashboard/summary.js +333 -0
  89. package/dist/dashboard/summary.js.map +1 -0
  90. package/dist/index.d.ts +10 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +10 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/proxy.d.ts +40 -0
  95. package/dist/proxy.d.ts.map +1 -0
  96. package/dist/proxy.js +29 -0
  97. package/dist/proxy.js.map +1 -0
  98. package/dist/route.d.ts +24 -0
  99. package/dist/route.d.ts.map +1 -0
  100. package/dist/route.js +37 -0
  101. package/dist/route.js.map +1 -0
  102. package/dist/server/ingest.d.ts +14 -0
  103. package/dist/server/ingest.d.ts.map +1 -0
  104. package/dist/server/ingest.js +73 -0
  105. package/dist/server/ingest.js.map +1 -0
  106. package/dist/server/queries.d.ts +245 -0
  107. package/dist/server/queries.d.ts.map +1 -0
  108. package/dist/server/queries.js +547 -0
  109. package/dist/server/queries.js.map +1 -0
  110. package/dist/server/store.d.ts +29 -0
  111. package/dist/server/store.d.ts.map +1 -0
  112. package/dist/server/store.js +359 -0
  113. package/dist/server/store.js.map +1 -0
  114. package/dist/server.d.ts +15 -0
  115. package/dist/server.d.ts.map +1 -0
  116. package/dist/server.js +15 -0
  117. package/dist/server.js.map +1 -0
  118. package/dist/session.d.ts +16 -0
  119. package/dist/session.d.ts.map +1 -0
  120. package/dist/session.js +29 -0
  121. package/dist/session.js.map +1 -0
  122. package/dist/shared/types.d.ts +56 -0
  123. package/dist/shared/types.d.ts.map +1 -0
  124. package/dist/shared/types.js +27 -0
  125. package/dist/shared/types.js.map +1 -0
  126. package/docs/SETUP.md +315 -0
  127. package/package.json +92 -0
@@ -0,0 +1,142 @@
1
+ /** Sensible default model per provider, used when none is configured. */
2
+ export const DEFAULT_MODEL = {
3
+ // Anthropic's most capable model; the owner can downgrade for cost.
4
+ anthropic: "claude-opus-4-8",
5
+ openai: "gpt-4o-mini",
6
+ "openai-compatible": "gpt-4o-mini",
7
+ google: "gemini-1.5-flash",
8
+ };
9
+ /**
10
+ * Default cap. Reasoning models (e.g. the Claude 4.x family) spend output
11
+ * tokens on internal thinking before the visible text, and those count against
12
+ * `max_tokens` — so the cap needs generous headroom beyond the paragraph we
13
+ * actually want. It's a worst-case ceiling, not expected spend: providers bill
14
+ * only tokens actually generated.
15
+ */
16
+ const DEFAULT_MAX_OUTPUT_TOKENS = 2048;
17
+ class ProviderError extends Error {
18
+ }
19
+ async function postJson(url, headers, body) {
20
+ let res;
21
+ try {
22
+ res = await fetch(url, {
23
+ method: "POST",
24
+ headers: { "content-type": "application/json", ...headers },
25
+ body: JSON.stringify(body),
26
+ });
27
+ }
28
+ catch (err) {
29
+ throw new ProviderError(`network error contacting model provider: ${err instanceof Error ? err.message : String(err)}`);
30
+ }
31
+ const raw = await res.text();
32
+ if (!res.ok) {
33
+ // Surface the provider's message but trim it so a key never lands in a log.
34
+ const detail = raw.slice(0, 300).replace(/\s+/g, " ").trim();
35
+ throw new ProviderError(`provider returned ${res.status}: ${detail}`);
36
+ }
37
+ try {
38
+ return JSON.parse(raw);
39
+ }
40
+ catch {
41
+ throw new ProviderError("provider returned a non-JSON response");
42
+ }
43
+ }
44
+ function num(v) {
45
+ return typeof v === "number" && Number.isFinite(v) ? v : undefined;
46
+ }
47
+ // ── Anthropic Messages API ───────────────────────────────────────────────────
48
+ async function callAnthropic(s, system, user, maxTokens) {
49
+ const data = (await postJson("https://api.anthropic.com/v1/messages", { "x-api-key": s.apiKey, "anthropic-version": "2023-06-01" }, {
50
+ model: s.model || DEFAULT_MODEL.anthropic,
51
+ max_tokens: maxTokens,
52
+ system,
53
+ messages: [{ role: "user", content: user }],
54
+ }));
55
+ const text = (data.content ?? [])
56
+ .filter((b) => b.type === "text" && typeof b.text === "string")
57
+ .map((b) => b.text)
58
+ .join("")
59
+ .trim();
60
+ return {
61
+ text,
62
+ truncated: data.stop_reason === "max_tokens",
63
+ usage: {
64
+ inputTokens: num(data.usage?.input_tokens),
65
+ outputTokens: num(data.usage?.output_tokens),
66
+ },
67
+ };
68
+ }
69
+ // ── OpenAI (and OpenAI-compatible) Chat Completions ──────────────────────────
70
+ async function callOpenAI(s, system, user, maxTokens) {
71
+ const base = (s.baseUrl?.replace(/\/$/, "") || "https://api.openai.com/v1");
72
+ const data = (await postJson(`${base}/chat/completions`, { authorization: `Bearer ${s.apiKey}` }, {
73
+ model: s.model || DEFAULT_MODEL.openai,
74
+ max_tokens: maxTokens,
75
+ messages: [
76
+ { role: "system", content: system },
77
+ { role: "user", content: user },
78
+ ],
79
+ }));
80
+ const text = (data.choices?.[0]?.message?.content ?? "").trim();
81
+ return {
82
+ text,
83
+ truncated: data.choices?.[0]?.finish_reason === "length",
84
+ usage: {
85
+ inputTokens: num(data.usage?.prompt_tokens),
86
+ outputTokens: num(data.usage?.completion_tokens),
87
+ },
88
+ };
89
+ }
90
+ // ── Google Gemini generateContent ────────────────────────────────────────────
91
+ async function callGoogle(s, system, user, maxTokens) {
92
+ const model = s.model || DEFAULT_MODEL.google;
93
+ const data = (await postJson(`https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(s.apiKey)}`, {}, {
94
+ systemInstruction: { parts: [{ text: system }] },
95
+ contents: [{ role: "user", parts: [{ text: user }] }],
96
+ generationConfig: { maxOutputTokens: maxTokens },
97
+ }));
98
+ const text = (data.candidates?.[0]?.content?.parts ?? [])
99
+ .map((p) => p.text ?? "")
100
+ .join("")
101
+ .trim();
102
+ return {
103
+ text,
104
+ truncated: data.candidates?.[0]?.finishReason === "MAX_TOKENS",
105
+ usage: {
106
+ inputTokens: num(data.usageMetadata?.promptTokenCount),
107
+ outputTokens: num(data.usageMetadata?.candidatesTokenCount),
108
+ },
109
+ };
110
+ }
111
+ function dispatch(settings, system, user, maxTokens) {
112
+ switch (settings.provider) {
113
+ case "anthropic":
114
+ return callAnthropic(settings, system, user, maxTokens);
115
+ case "openai":
116
+ case "openai-compatible":
117
+ return callOpenAI(settings, system, user, maxTokens);
118
+ case "google":
119
+ return callGoogle(settings, system, user, maxTokens);
120
+ default:
121
+ throw new ProviderError(`unknown provider: ${settings.provider}`);
122
+ }
123
+ }
124
+ /**
125
+ * Dispatch to the configured provider. Throws on transport / API errors with a
126
+ * message safe to surface (no key material), which the caller turns into a
127
+ * `provider-error` result.
128
+ *
129
+ * If the reply hits the output cap (reasoning models can burn most of it on
130
+ * thinking), retry once at double the cap — a clipped reply is 100% wasted
131
+ * spend, so one bigger attempt is the cheaper path. Still-truncated replies
132
+ * are returned flagged; callers must not cache them.
133
+ */
134
+ export async function callModel(settings, system, user, opts = {}) {
135
+ const maxTokens = opts.maxTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
136
+ const reply = await dispatch(settings, system, user, maxTokens);
137
+ if (!reply.truncated)
138
+ return reply;
139
+ console.warn(`[polishd] model hit the ${maxTokens}-token output cap — retrying once at ${maxTokens * 2}`);
140
+ return dispatch(settings, system, user, maxTokens * 2);
141
+ }
142
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/ai/providers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export interface ScannedFile {
2
+ /** Path relative to the project root. */
3
+ path: string;
4
+ content: string;
5
+ }
6
+ export interface SourceScan {
7
+ /** False when no recognizable source tree exists on disk. */
8
+ available: boolean;
9
+ files: ScannedFile[];
10
+ /** PolishdMonitor / data-component identifiers found in the scanned source. */
11
+ identifiers: string[];
12
+ /** Hash of every candidate file's path+size — detects source drift cheaply. */
13
+ fingerprint: string;
14
+ /** True when the budget forced us to drop or clip files. */
15
+ truncated: boolean;
16
+ }
17
+ /**
18
+ * Collect the host app's source for the project profile.
19
+ *
20
+ * @param sourceDirs Owner-configured comma-separated roots (optional).
21
+ * @param componentNames Identifiers seen in analytics — used to prioritize the
22
+ * files that explain them.
23
+ */
24
+ export declare function collectSource(sourceDirs?: string, componentNames?: string[]): SourceScan;
25
+ /**
26
+ * Locate the first source file containing any of the given needles (most
27
+ * specific — longest — first). Used to verify that a loss the model reported
28
+ * points at something that really exists in the codebase. Returns the path
29
+ * relative to the project root, or null (including when source isn't on disk).
30
+ */
31
+ export declare function findInSource(needles: string[], sourceDirs?: string): string | null;
32
+ /**
33
+ * Targeted read for summary-time gaps: return ONLY files that mention one of
34
+ * the given identifiers, under a much smaller budget. This is the "the agent
35
+ * only needs the codebase again when the profile doesn't cover something" path.
36
+ */
37
+ export declare function collectTargeted(identifiers: string[], sourceDirs?: string): ScannedFile[];
38
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/ai/scan.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,WAAW;IAC1B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,SAAS,EAAE,OAAO,CAAC;CACpB;AA2GD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,GAAE,MAAM,EAAO,GAC5B,UAAU,CA2EZ;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmBlF;AAKD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EAAE,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,WAAW,EAAE,CAuBf"}
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Polishd — host-app source scanner (server only).
3
+ *
4
+ * Reads the consuming app's own source from disk (`process.cwd()`) so the
5
+ * one-time project profile can be generated from real code. This is the ONLY
6
+ * place the AI layer touches source files, and it runs only when the owner
7
+ * clicks "Scan" (or when a summary hits a component the profile doesn't cover,
8
+ * which triggers a tiny targeted read).
9
+ *
10
+ * Token thrift rules:
11
+ * - Hard budgets: per-file char cap and a total scan budget, so a big app
12
+ * can't produce a big prompt. Files are ranked (pages/layouts first, then
13
+ * files matching known component names, then components dirs) and the
14
+ * budget is spent in that order.
15
+ * - Only app code: node_modules/.next/tests/etc. are never read.
16
+ *
17
+ * On hosts where source isn't on disk (e.g. a serverless bundle), the scan
18
+ * reports `available: false` and everything degrades gracefully — the profile
19
+ * generated at dev/CI time keeps serving from the database.
20
+ */
21
+ import { createHash } from "node:crypto";
22
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
23
+ import { join, relative } from "node:path";
24
+ const IGNORE_DIRS = new Set([
25
+ "node_modules",
26
+ ".next",
27
+ ".git",
28
+ "dist",
29
+ "build",
30
+ "out",
31
+ "coverage",
32
+ ".polishd",
33
+ ".vercel",
34
+ ".turbo",
35
+ "polishd-next", // never scan ourselves when developed in-repo
36
+ ]);
37
+ const CODE_EXT = /\.(tsx|ts|jsx|js|mjs)$/;
38
+ const SKIP_FILE = /(\.d\.ts$|\.test\.|\.spec\.|__tests__|\.stories\.)/;
39
+ /** Roots tried in order when the owner hasn't configured `sourceDirs`. */
40
+ const DEFAULT_ROOTS = ["src", "app", "components", "pages", "lib"];
41
+ /** Root-level docs that anchor the model's understanding of the project. */
42
+ const ROOT_DOCS = ["package.json", "README.md"];
43
+ const PER_FILE_CHARS = 6_000;
44
+ const TOTAL_BUDGET_CHARS = 90_000;
45
+ const MAX_FILES = 60;
46
+ const MAX_DEPTH = 7;
47
+ /** Resolve the directories to walk, relative to the project root. */
48
+ function resolveRoots(projectRoot, sourceDirs) {
49
+ const wanted = sourceDirs
50
+ ?.split(",")
51
+ .map((s) => s.trim().replace(/^\/+|\/+$/g, ""))
52
+ .filter(Boolean);
53
+ const candidates = wanted?.length ? wanted : DEFAULT_ROOTS;
54
+ const roots = candidates.filter((d) => {
55
+ const full = join(projectRoot, d);
56
+ try {
57
+ return existsSync(full) && statSync(full).isDirectory();
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ });
63
+ // With a `src/` layout everything lives inside it — don't double-walk.
64
+ if (!wanted?.length && roots.includes("src"))
65
+ return ["src"];
66
+ return roots;
67
+ }
68
+ /** Recursively list candidate code files under the roots. */
69
+ function listFiles(projectRoot, roots) {
70
+ const found = [];
71
+ const walk = (dir, depth) => {
72
+ if (depth > MAX_DEPTH)
73
+ return;
74
+ let entries;
75
+ try {
76
+ entries = readdirSync(dir, { withFileTypes: true });
77
+ }
78
+ catch {
79
+ return;
80
+ }
81
+ for (const entry of entries) {
82
+ if (entry.name.startsWith("."))
83
+ continue;
84
+ const full = join(dir, entry.name);
85
+ if (entry.isDirectory()) {
86
+ if (!IGNORE_DIRS.has(entry.name))
87
+ walk(full, depth + 1);
88
+ }
89
+ else if (CODE_EXT.test(entry.name) && !SKIP_FILE.test(entry.name)) {
90
+ found.push(full);
91
+ }
92
+ }
93
+ };
94
+ for (const root of roots)
95
+ walk(join(projectRoot, root), 0);
96
+ return found;
97
+ }
98
+ /**
99
+ * Rank a file for the budget. Lower = read first. Pages and layouts define the
100
+ * site's structure; files matching component identifiers explain the things
101
+ * the analytics actually talk about.
102
+ */
103
+ function priority(relPath, componentNames) {
104
+ const base = relPath.toLowerCase();
105
+ if (/(^|\/)(page|layout|route)\.(tsx|ts|jsx|js)$/.test(base))
106
+ return 0;
107
+ if (componentNames.some((n) => n && base.includes(n.toLowerCase())))
108
+ return 1;
109
+ if (base.includes("/components/") || base.startsWith("components/"))
110
+ return 2;
111
+ if (base.includes("/app/") || base.startsWith("app/"))
112
+ return 3;
113
+ return 4;
114
+ }
115
+ const MONITOR_NAME = /<PolishdMonitor[^>]*\sname\s*=\s*["']([^"']+)["']/g;
116
+ const DATA_COMPONENT = /data-component\s*=\s*["']([^"']+)["']/g;
117
+ function extractIdentifiers(content, into) {
118
+ for (const re of [MONITOR_NAME, DATA_COMPONENT]) {
119
+ re.lastIndex = 0;
120
+ let m;
121
+ while ((m = re.exec(content)) !== null)
122
+ into.add(m[1]);
123
+ }
124
+ }
125
+ /** Clip a file to the per-file cap, preferring its head (imports + exports). */
126
+ function clipFile(content) {
127
+ const clean = content.replace(/\r\n/g, "\n");
128
+ if (clean.length <= PER_FILE_CHARS)
129
+ return { text: clean, clipped: false };
130
+ return { text: `${clean.slice(0, PER_FILE_CHARS)}\n/* …clipped by Polishd scan */`, clipped: true };
131
+ }
132
+ /**
133
+ * Collect the host app's source for the project profile.
134
+ *
135
+ * @param sourceDirs Owner-configured comma-separated roots (optional).
136
+ * @param componentNames Identifiers seen in analytics — used to prioritize the
137
+ * files that explain them.
138
+ */
139
+ export function collectSource(sourceDirs, componentNames = []) {
140
+ const projectRoot = process.cwd();
141
+ const roots = resolveRoots(projectRoot, sourceDirs);
142
+ const absPaths = listFiles(projectRoot, roots);
143
+ if (!absPaths.length) {
144
+ return { available: false, files: [], identifiers: [], fingerprint: "", truncated: false };
145
+ }
146
+ // Fingerprint every candidate (path+size) BEFORE budgeting, so any source
147
+ // change — even in a file we later drop — flips the fingerprint.
148
+ const hash = createHash("sha256");
149
+ const sized = absPaths
150
+ .map((abs) => {
151
+ const rel = relative(projectRoot, abs);
152
+ let size = 0;
153
+ try {
154
+ size = statSync(abs).size;
155
+ }
156
+ catch {
157
+ /* raced deletion — keep size 0 */
158
+ }
159
+ return { abs, rel, size };
160
+ })
161
+ .sort((a, b) => a.rel.localeCompare(b.rel));
162
+ for (const f of sized)
163
+ hash.update(`${f.rel}:${f.size}\n`);
164
+ const fingerprint = hash.digest("hex").slice(0, 16);
165
+ const ranked = sized.sort((a, b) => priority(a.rel, componentNames) - priority(b.rel, componentNames) ||
166
+ a.rel.localeCompare(b.rel));
167
+ const files = [];
168
+ const identifiers = new Set();
169
+ let spent = 0;
170
+ let truncated = false;
171
+ // Root docs first — cheap and high-signal for "what is this project".
172
+ for (const doc of ROOT_DOCS) {
173
+ const abs = join(projectRoot, doc);
174
+ if (!existsSync(abs))
175
+ continue;
176
+ try {
177
+ const { text, clipped } = clipFile(readFileSync(abs, "utf8"));
178
+ files.push({ path: doc, content: text });
179
+ spent += text.length;
180
+ truncated ||= clipped;
181
+ }
182
+ catch {
183
+ /* unreadable — skip */
184
+ }
185
+ }
186
+ for (const f of ranked) {
187
+ if (files.length >= MAX_FILES || spent >= TOTAL_BUDGET_CHARS) {
188
+ truncated = true;
189
+ break;
190
+ }
191
+ let raw;
192
+ try {
193
+ raw = readFileSync(f.abs, "utf8");
194
+ }
195
+ catch {
196
+ continue;
197
+ }
198
+ extractIdentifiers(raw, identifiers);
199
+ const { text, clipped } = clipFile(raw);
200
+ if (spent + text.length > TOTAL_BUDGET_CHARS) {
201
+ truncated = true;
202
+ continue; // a later, smaller file may still fit
203
+ }
204
+ files.push({ path: f.rel, content: text });
205
+ spent += text.length;
206
+ truncated ||= clipped;
207
+ }
208
+ return { available: true, files, identifiers: [...identifiers].sort(), fingerprint, truncated };
209
+ }
210
+ /**
211
+ * Locate the first source file containing any of the given needles (most
212
+ * specific — longest — first). Used to verify that a loss the model reported
213
+ * points at something that really exists in the codebase. Returns the path
214
+ * relative to the project root, or null (including when source isn't on disk).
215
+ */
216
+ export function findInSource(needles, sourceDirs) {
217
+ const usable = needles.filter((n) => n.length >= 4);
218
+ if (!usable.length)
219
+ return null;
220
+ const projectRoot = process.cwd();
221
+ const roots = resolveRoots(projectRoot, sourceDirs);
222
+ const ordered = [...usable].sort((a, b) => b.length - a.length);
223
+ for (const needle of ordered) {
224
+ const lower = needle.toLowerCase();
225
+ for (const abs of listFiles(projectRoot, roots)) {
226
+ let raw;
227
+ try {
228
+ raw = readFileSync(abs, "utf8");
229
+ }
230
+ catch {
231
+ continue;
232
+ }
233
+ if (raw.toLowerCase().includes(lower))
234
+ return relative(projectRoot, abs);
235
+ }
236
+ }
237
+ return null;
238
+ }
239
+ /** Budget for the targeted gap-fill read at summary time — deliberately tiny. */
240
+ const TARGETED_BUDGET_CHARS = 12_000;
241
+ /**
242
+ * Targeted read for summary-time gaps: return ONLY files that mention one of
243
+ * the given identifiers, under a much smaller budget. This is the "the agent
244
+ * only needs the codebase again when the profile doesn't cover something" path.
245
+ */
246
+ export function collectTargeted(identifiers, sourceDirs) {
247
+ if (!identifiers.length)
248
+ return [];
249
+ const projectRoot = process.cwd();
250
+ const roots = resolveRoots(projectRoot, sourceDirs);
251
+ const absPaths = listFiles(projectRoot, roots);
252
+ const out = [];
253
+ let spent = 0;
254
+ for (const abs of absPaths) {
255
+ if (spent >= TARGETED_BUDGET_CHARS)
256
+ break;
257
+ let raw;
258
+ try {
259
+ raw = readFileSync(abs, "utf8");
260
+ }
261
+ catch {
262
+ continue;
263
+ }
264
+ if (!identifiers.some((id) => raw.includes(id)))
265
+ continue;
266
+ const { text } = clipFile(raw);
267
+ if (spent + text.length > TARGETED_BUDGET_CHARS)
268
+ continue;
269
+ out.push({ path: relative(projectRoot, abs), content: text });
270
+ spent += text.length;
271
+ }
272
+ return out;
273
+ }
274
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/ai/scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAoB3C,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,cAAc;IACd,OAAO;IACP,MAAM;IACN,MAAM;IACN,OAAO;IACP,KAAK;IACL,UAAU;IACV,UAAU;IACV,SAAS;IACT,QAAQ;IACR,cAAc,EAAE,8CAA8C;CAC/D,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,wBAAwB,CAAC;AAC1C,MAAM,SAAS,GAAG,oDAAoD,CAAC;AAEvE,0EAA0E;AAC1E,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAEnE,4EAA4E;AAC5E,MAAM,SAAS,GAAG,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAEhD,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,qEAAqE;AACrE,SAAS,YAAY,CAAC,WAAmB,EAAE,UAAmB;IAC5D,MAAM,MAAM,GAAG,UAAU;QACvB,EAAE,KAAK,CAAC,GAAG,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;SAC9C,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,MAAM,UAAU,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;IAC3D,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IACH,uEAAuE;IACvE,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7D,OAAO,KAAK,CAAC;AACf,CAAC;AAED,6DAA6D;AAC7D,SAAS,SAAS,CAAC,WAAmB,EAAE,KAAe;IACrD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE;QAC1C,IAAI,KAAK,GAAG,SAAS;YAAE,OAAO;QAC9B,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YACzC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAC1D,CAAC;iBAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,QAAQ,CAAC,OAAe,EAAE,cAAwB;IACzD,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACnC,IAAI,6CAA6C,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACvE,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9E,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,CAAC,CAAC;IAC9E,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,CAAC,CAAC;IAChE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,YAAY,GAAG,oDAAoD,CAAC;AAC1E,MAAM,cAAc,GAAG,wCAAwC,CAAC;AAEhE,SAAS,kBAAkB,CAAC,OAAe,EAAE,IAAiB;IAC5D,KAAK,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,cAAc,CAAC,EAAE,CAAC;QAChD,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC;QACjB,IAAI,CAAyB,CAAC;QAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI;YAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,SAAS,QAAQ,CAAC,OAAe;IAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,MAAM,IAAI,cAAc;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC3E,OAAO,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,kCAAkC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACtG,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,UAAmB,EACnB,iBAA2B,EAAE;IAE7B,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAEpD,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC7F,CAAC;IAED,0EAA0E;IAC1E,iEAAiE;IACjE,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,QAAQ;SACnB,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACX,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QACvC,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,IAAI,CAAC;YACH,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,kCAAkC;QACpC,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,KAAK;QAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEpD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CACvB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC;QACjE,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAC7B,CAAC;IAEF,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,sEAAsE;IACtE,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;YAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACzC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;YACrB,SAAS,KAAK,OAAO,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS,IAAI,KAAK,IAAI,kBAAkB,EAAE,CAAC;YAC7D,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM;QACR,CAAC;QACD,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,kBAAkB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QACrC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;YAC7C,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS,CAAC,sCAAsC;QAClD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;QACrB,SAAS,KAAK,OAAO,CAAC;IACxB,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AAClG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,OAAiB,EAAE,UAAmB;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IACpD,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAChE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;QACnC,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC;YAChD,IAAI,GAAW,CAAC;YAChB,IAAI,CAAC;gBACH,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAClC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,OAAO,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iFAAiF;AACjF,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAErC;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,WAAqB,EACrB,UAAmB;IAEnB,IAAI,CAAC,WAAW,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAE/C,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,KAAK,IAAI,qBAAqB;YAAE,MAAM;QAC1C,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAAE,SAAS;QAC1D,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,qBAAqB;YAAE,SAAS;QAC1D,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { PolishdAISettings, PolishdAISettingsPublic } from "./types";
2
+ /**
3
+ * Effective settings = saved (dashboard) over env defaults. Returns the full
4
+ * settings (with key) for server use, plus whether the *provider config* came
5
+ * purely from env (so the UI can show it as a read-only default).
6
+ */
7
+ export declare function resolveSettings(): Promise<{
8
+ settings: PolishdAISettings;
9
+ fromEnv: boolean;
10
+ }>;
11
+ /** Public (no-secret) settings for the dashboard. */
12
+ export declare function getAISettingsPublic(): Promise<PolishdAISettingsPublic>;
13
+ /** Fields the dashboard form can write. A blank apiKey leaves the saved one. */
14
+ export interface SaveAISettingsInput {
15
+ provider?: string;
16
+ model?: string;
17
+ apiKey?: string;
18
+ baseUrl?: string;
19
+ instructions?: string;
20
+ context?: string;
21
+ audience?: string;
22
+ ideology?: string;
23
+ sourceDirs?: string;
24
+ refreshCadence?: string;
25
+ githubRepo?: string;
26
+ githubToken?: string;
27
+ githubAutoIssues?: boolean;
28
+ }
29
+ /** Persist owner settings. Returns the refreshed public view. */
30
+ export declare function saveAISettings(input: SaveAISettingsInput): Promise<PolishdAISettingsPublic>;
31
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/ai/settings.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAEV,iBAAiB,EACjB,uBAAuB,EAExB,MAAM,SAAS,CAAC;AAoFjB;;;;GAIG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAAE,QAAQ,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAuBlG;AAED,qDAAqD;AACrD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAkB5E;AAED,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,iEAAiE;AACjE,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,uBAAuB,CAAC,CA4BlC"}