@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,108 @@
1
+ /**
2
+ * Polishd — analytics digest (server only).
3
+ *
4
+ * This is the token-saving heart of the AI layer. Instead of shipping raw
5
+ * events (thousands of rows) to a model, we pre-aggregate everything on the
6
+ * server — the same numbers the dashboard already computes — and render them as
7
+ * a terse, structured text block. A model can read the whole picture of a site
8
+ * in a few hundred tokens.
9
+ *
10
+ * `fingerprintDigest` hashes the digest so the orchestrator can skip the model
11
+ * call entirely when nothing has changed since the last summary (see summary.ts).
12
+ */
13
+ import { createHash } from "node:crypto";
14
+ /** Round to keep digests stable across tiny float jitter. */
15
+ const r = (n) => (n === null ? "—" : String(Math.round(n)));
16
+ /** Trim and cap a free-text label so one outlier can't bloat the prompt. */
17
+ const clip = (s, max = 40) => !s ? "" : s.replace(/\s+/g, " ").trim().slice(0, max);
18
+ /**
19
+ * Render the captured analytics as a compact, model-readable brief. Sections
20
+ * are omitted when empty so the prompt only ever contains real signal.
21
+ */
22
+ export function buildDigest(data, context) {
23
+ const { overview, pages, elements, devices, topUsed, errors, monitored } = data;
24
+ const lines = [];
25
+ if (context && context.trim()) {
26
+ lines.push(`SITE: ${context.trim()}`);
27
+ }
28
+ lines.push(`TOTALS: ${overview.sessions} sessions, ${overview.pageViews} page views, ` +
29
+ `${overview.rageClicks} rage clicks, ${overview.deadClicks} dead clicks, ` +
30
+ `${overview.jsErrors} JS errors (${overview.totalEvents} events total).`);
31
+ if (devices.length) {
32
+ lines.push("DEVICES: " +
33
+ devices.map((d) => `${d.category} ${d.pct}%`).join(", ") +
34
+ ".");
35
+ }
36
+ if (topUsed.length) {
37
+ lines.push("MOST-USED ELEMENTS (by clicks):");
38
+ for (const t of topUsed.slice(0, 8)) {
39
+ const label = clip(t.label) || clip(t.sampleText) || t.selector || "(unknown)";
40
+ lines.push(` - ${label}: ${t.clicks} clicks, ${t.pages} pages`);
41
+ }
42
+ }
43
+ // Pages ranked worst-first by issue score (rage/dead/errors/shallow scroll).
44
+ const worst = [...pages]
45
+ .map((p) => ({
46
+ ...p,
47
+ score: p.rageClicks * 3 +
48
+ p.deadClicks * 2 +
49
+ p.jsErrors * 2.5 +
50
+ (p.avgScrollDepth !== null && p.avgScrollDepth < 50
51
+ ? (50 - p.avgScrollDepth) / 25
52
+ : 0),
53
+ }))
54
+ .filter((p) => p.score > 0)
55
+ .sort((a, b) => b.score - a.score)
56
+ .slice(0, 6);
57
+ if (worst.length) {
58
+ lines.push("PAGES WITH FRICTION (worst first):");
59
+ for (const p of worst) {
60
+ lines.push(` - ${p.path}: ${p.sessions} sessions, ${p.rageClicks} rage, ` +
61
+ `${p.deadClicks} dead, ${p.jsErrors} err, scroll ${r(p.avgScrollDepth)}%`);
62
+ }
63
+ }
64
+ const brokenEls = elements.filter((e) => e.rageClicks > 0 || e.deadClicks > 0).slice(0, 8);
65
+ if (brokenEls.length) {
66
+ lines.push("ELEMENTS USERS STRUGGLE WITH:");
67
+ for (const e of brokenEls) {
68
+ const label = clip(e.sampleText) || e.label;
69
+ lines.push(` - ${label}: ${e.rageClicks} rage, ${e.deadClicks} dead, ${e.pages} pages`);
70
+ }
71
+ }
72
+ if (monitored.length) {
73
+ lines.push("MONITORED COMPONENTS (engagement):");
74
+ for (const m of monitored.slice(0, 6)) {
75
+ const parts = [`${m.componentViews} views`];
76
+ if (m.avgViewMs !== null)
77
+ parts.push(`avg ${Math.round(m.avgViewMs / 100) / 10}s visible`);
78
+ if (m.avgScrollDepth !== null)
79
+ parts.push(`${m.avgScrollDepth}% scrolled`);
80
+ if (m.rageClicks > 0)
81
+ parts.push(`${m.rageClicks} rage`);
82
+ lines.push(` - ${m.name}: ${parts.join(", ")}`);
83
+ }
84
+ }
85
+ if (errors.length) {
86
+ // Dedupe by message so a single recurring error doesn't fill the section.
87
+ const seen = new Set();
88
+ const uniq = [];
89
+ for (const e of errors) {
90
+ if (seen.has(e.message))
91
+ continue;
92
+ seen.add(e.message);
93
+ uniq.push(e);
94
+ if (uniq.length >= 5)
95
+ break;
96
+ }
97
+ lines.push("RECENT ERRORS:");
98
+ for (const e of uniq) {
99
+ lines.push(` - "${clip(e.message, 80)}" on ${e.path}`);
100
+ }
101
+ }
102
+ return lines.join("\n");
103
+ }
104
+ /** Stable fingerprint of a digest — drives the "data unchanged" short-circuit. */
105
+ export function fingerprintDigest(digest) {
106
+ return createHash("sha256").update(digest).digest("hex").slice(0, 16);
107
+ }
108
+ //# sourceMappingURL=digest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.js","sourceRoot":"","sources":["../../src/ai/digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,6DAA6D;AAC7D,MAAM,CAAC,GAAG,CAAC,CAAgB,EAAU,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnF,4EAA4E;AAC5E,MAAM,IAAI,GAAG,CAAC,CAA4B,EAAE,GAAG,GAAG,EAAE,EAAU,EAAE,CAC9D,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAA0B,EAAE,OAAgB;IACtE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAChF,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,SAAS,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,IAAI,CACR,WAAW,QAAQ,CAAC,QAAQ,cAAc,QAAQ,CAAC,SAAS,eAAe;QACzE,GAAG,QAAQ,CAAC,UAAU,iBAAiB,QAAQ,CAAC,UAAU,gBAAgB;QAC1E,GAAG,QAAQ,CAAC,QAAQ,eAAe,QAAQ,CAAC,WAAW,iBAAiB,CAC3E,CAAC;IAEF,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CACR,WAAW;YACT,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACxD,GAAG,CACN,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;QAC9C,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,WAAW,CAAC;YAC/E,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,GAAG,CAAC;QACJ,KAAK,EACH,CAAC,CAAC,UAAU,GAAG,CAAC;YAChB,CAAC,CAAC,UAAU,GAAG,CAAC;YAChB,CAAC,CAAC,QAAQ,GAAG,GAAG;YAChB,CAAC,CAAC,CAAC,cAAc,KAAK,IAAI,IAAI,CAAC,CAAC,cAAc,GAAG,EAAE;gBACjD,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,cAAc,CAAC,GAAG,EAAE;gBAC9B,CAAC,CAAC,CAAC,CAAC;KACT,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;SAC1B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;SACjC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACf,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CACR,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,UAAU,SAAS;gBAC7D,GAAG,CAAC,CAAC,UAAU,UAAU,CAAC,CAAC,QAAQ,gBAAgB,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAC5E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3F,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC5C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,UAAU,UAAU,CAAC,CAAC,UAAU,UAAU,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,cAAc,QAAQ,CAAC,CAAC;YAC5C,IAAI,CAAC,CAAC,SAAS,KAAK,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAC3F,IAAI,CAAC,CAAC,cAAc,KAAK,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,cAAc,YAAY,CAAC,CAAC;YAC3E,IAAI,CAAC,CAAC,UAAU,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,UAAU,OAAO,CAAC,CAAC;YACzD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,0EAA0E;QAC1E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,MAAM,IAAI,GAAkB,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;gBAAE,SAAS;YAClC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACb,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;gBAAE,MAAM;QAC9B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC7B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;YACrB,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { PolishdGithubStatus, VerifyGithubResult } from "./types";
2
+ /** True when both a repo and a token are configured. */
3
+ export declare function isGithubConnected(): Promise<boolean>;
4
+ /**
5
+ * Check the saved token against the saved repo: does it exist, can we read it,
6
+ * can we push (branches/PRs), are issues enabled? Called right after the owner
7
+ * saves the connection so bad tokens fail at setup, not at first use.
8
+ */
9
+ export declare function verifyGithubConnection(): Promise<VerifyGithubResult>;
10
+ /** The verified connection status, or null when not connected / unreachable. */
11
+ export declare function getGithubStatus(): Promise<PolishdGithubStatus | null>;
12
+ /**
13
+ * Read one file from the connected repo (default branch unless `ref` given).
14
+ * Returns null when the path doesn't exist or nothing is connected — callers
15
+ * treat GitHub as a fallback source tree, not a hard dependency.
16
+ */
17
+ export declare function readGithubFile(path: string, ref?: string): Promise<string | null>;
18
+ /**
19
+ * Find files in the connected repo whose content matches the given terms
20
+ * (ANDed), via GitHub's code search. Returns file paths, best matches first,
21
+ * or [] on any failure — search is an enrichment, never a hard dependency.
22
+ */
23
+ export declare function searchGithubCode(terms: string[], max?: number): Promise<string[]>;
24
+ /**
25
+ * Create a branch off the repo's default branch (or `from`). Returns the new
26
+ * branch name. Throws on failure — callers surface GitHub's message.
27
+ */
28
+ export declare function createGithubBranch(name: string, from?: string): Promise<string>;
29
+ /**
30
+ * Open a pull request from `head` into `base` (default branch when omitted).
31
+ * Returns the PR's URL and number. Throws on failure.
32
+ */
33
+ export declare function createGithubPullRequest(input: {
34
+ title: string;
35
+ body?: string;
36
+ head: string;
37
+ base?: string;
38
+ }): Promise<{
39
+ url: string;
40
+ number: number;
41
+ }>;
42
+ /** File an issue in the connected repo. Throws on failure. */
43
+ export declare function createGithubIssue(input: {
44
+ title: string;
45
+ body?: string;
46
+ labels?: string[];
47
+ }): Promise<{
48
+ url: string;
49
+ number: number;
50
+ }>;
51
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/ai/github.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAavE,wDAAwD;AACxD,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAE1D;AA0CD;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CA2B1E;AAED,gFAAgF;AAChF,wBAAsB,eAAe,IAAI,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAG3E;AAID;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,SAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAclF;AAID;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAa3C;AAID,8DAA8D;AAC9D,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAU3C"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Polishd — GitHub API client (server only).
3
+ *
4
+ * A thin client over GitHub's REST API for the repo the owner connected during
5
+ * onboarding. Capabilities, all scoped to that one repo: read and search source
6
+ * (Contents + code-search APIs — lets the AI layer ground its findings in real
7
+ * code even where the source tree isn't on disk, e.g. Vercel), create branches,
8
+ * open pull requests, and file issues. The "loss → verified bug" orchestration
9
+ * that uses this client lives in issues.ts.
10
+ *
11
+ * The token lives in the same server-side settings blob as the model API key
12
+ * and never reaches the browser; the client only sees a "connected" flag. A
13
+ * fine-grained PAT with Contents (read), Issues (write), and Pull requests
14
+ * (write) on the one repo is all it needs.
15
+ */
16
+ import { resolveSettings } from "./settings";
17
+ const API = "https://api.github.com";
18
+ /** The connected repo + token, or null when either half is missing. */
19
+ async function connection() {
20
+ const { settings } = await resolveSettings();
21
+ const repo = settings.githubRepo?.trim();
22
+ const token = settings.githubToken?.trim();
23
+ if (!repo || !token)
24
+ return null;
25
+ return { repo, token };
26
+ }
27
+ /** True when both a repo and a token are configured. */
28
+ export async function isGithubConnected() {
29
+ return (await connection()) !== null;
30
+ }
31
+ /** One GitHub API call. Throws with GitHub's own message on non-2xx. */
32
+ async function gh(token, method, path, body) {
33
+ const res = await fetch(`${API}${path}`, {
34
+ method,
35
+ headers: {
36
+ Authorization: `Bearer ${token}`,
37
+ Accept: "application/vnd.github+json",
38
+ "X-GitHub-Api-Version": "2022-11-28",
39
+ ...(body !== undefined ? { "Content-Type": "application/json" } : {}),
40
+ },
41
+ body: body !== undefined ? JSON.stringify(body) : undefined,
42
+ cache: "no-store",
43
+ });
44
+ if (!res.ok) {
45
+ let message = `GitHub responded ${res.status}`;
46
+ try {
47
+ const err = (await res.json());
48
+ if (err.message)
49
+ message = `GitHub: ${err.message} (${res.status})`;
50
+ }
51
+ catch {
52
+ /* keep the status-only message */
53
+ }
54
+ throw new Error(message);
55
+ }
56
+ return (await res.json());
57
+ }
58
+ /**
59
+ * Check the saved token against the saved repo: does it exist, can we read it,
60
+ * can we push (branches/PRs), are issues enabled? Called right after the owner
61
+ * saves the connection so bad tokens fail at setup, not at first use.
62
+ */
63
+ export async function verifyGithubConnection() {
64
+ const conn = await connection();
65
+ if (!conn) {
66
+ return {
67
+ ok: false,
68
+ error: "not-connected",
69
+ message: "Add a repository and access token first.",
70
+ };
71
+ }
72
+ try {
73
+ const repo = await gh(conn.token, "GET", `/repos/${conn.repo}`);
74
+ return {
75
+ ok: true,
76
+ status: {
77
+ repo: repo.full_name,
78
+ defaultBranch: repo.default_branch,
79
+ canPush: repo.permissions?.push === true,
80
+ issuesEnabled: repo.has_issues,
81
+ },
82
+ };
83
+ }
84
+ catch (err) {
85
+ return {
86
+ ok: false,
87
+ error: "github-error",
88
+ message: err instanceof Error ? err.message : "Could not reach GitHub.",
89
+ };
90
+ }
91
+ }
92
+ /** The verified connection status, or null when not connected / unreachable. */
93
+ export async function getGithubStatus() {
94
+ const res = await verifyGithubConnection();
95
+ return res.ok ? res.status : null;
96
+ }
97
+ // ── Read source ──────────────────────────────────────────────────────────────
98
+ /**
99
+ * Read one file from the connected repo (default branch unless `ref` given).
100
+ * Returns null when the path doesn't exist or nothing is connected — callers
101
+ * treat GitHub as a fallback source tree, not a hard dependency.
102
+ */
103
+ export async function readGithubFile(path, ref) {
104
+ const conn = await connection();
105
+ if (!conn)
106
+ return null;
107
+ try {
108
+ const file = await gh(conn.token, "GET", `/repos/${conn.repo}/contents/${encodeURI(path)}${ref ? `?ref=${encodeURIComponent(ref)}` : ""}`);
109
+ if (file.encoding !== "base64" || typeof file.content !== "string")
110
+ return null;
111
+ return Buffer.from(file.content, "base64").toString("utf8");
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ }
117
+ /**
118
+ * Find files in the connected repo whose content matches the given terms
119
+ * (ANDed), via GitHub's code search. Returns file paths, best matches first,
120
+ * or [] on any failure — search is an enrichment, never a hard dependency.
121
+ */
122
+ export async function searchGithubCode(terms, max = 3) {
123
+ const conn = await connection();
124
+ if (!conn || terms.length === 0)
125
+ return [];
126
+ try {
127
+ const q = encodeURIComponent(`${terms.join(" ")} repo:${conn.repo}`);
128
+ const res = await gh(conn.token, "GET", `/search/code?q=${q}&per_page=${max}`);
129
+ return (res.items ?? []).map((i) => i.path).slice(0, max);
130
+ }
131
+ catch {
132
+ return [];
133
+ }
134
+ }
135
+ // ── Branches & pull requests ─────────────────────────────────────────────────
136
+ /**
137
+ * Create a branch off the repo's default branch (or `from`). Returns the new
138
+ * branch name. Throws on failure — callers surface GitHub's message.
139
+ */
140
+ export async function createGithubBranch(name, from) {
141
+ const conn = await connection();
142
+ if (!conn)
143
+ throw new Error("GitHub is not connected.");
144
+ const base = from ??
145
+ (await gh(conn.token, "GET", `/repos/${conn.repo}`)).default_branch;
146
+ const ref = await gh(conn.token, "GET", `/repos/${conn.repo}/git/ref/heads/${encodeURIComponent(base)}`);
147
+ await gh(conn.token, "POST", `/repos/${conn.repo}/git/refs`, {
148
+ ref: `refs/heads/${name}`,
149
+ sha: ref.object.sha,
150
+ });
151
+ return name;
152
+ }
153
+ /**
154
+ * Open a pull request from `head` into `base` (default branch when omitted).
155
+ * Returns the PR's URL and number. Throws on failure.
156
+ */
157
+ export async function createGithubPullRequest(input) {
158
+ const conn = await connection();
159
+ if (!conn)
160
+ throw new Error("GitHub is not connected.");
161
+ const base = input.base ??
162
+ (await gh(conn.token, "GET", `/repos/${conn.repo}`)).default_branch;
163
+ const pr = await gh(conn.token, "POST", `/repos/${conn.repo}/pulls`, { title: input.title, body: input.body ?? "", head: input.head, base });
164
+ return { url: pr.html_url, number: pr.number };
165
+ }
166
+ // ── Issues ───────────────────────────────────────────────────────────────────
167
+ /** File an issue in the connected repo. Throws on failure. */
168
+ export async function createGithubIssue(input) {
169
+ const conn = await connection();
170
+ if (!conn)
171
+ throw new Error("GitHub is not connected.");
172
+ const issue = await gh(conn.token, "POST", `/repos/${conn.repo}/issues`, { title: input.title, body: input.body ?? "", labels: input.labels });
173
+ return { url: issue.html_url, number: issue.number };
174
+ }
175
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/ai/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Polishd — server-action authorization (server only).
3
+ *
4
+ * Next.js server actions are independently addressable POST endpoints: their
5
+ * ids ship inside the public client bundle, so anyone can invoke one directly
6
+ * with a `Next-Action` header. Gating the dashboard *page* therefore protects
7
+ * only what renders — it does nothing for the actions behind it. Every action
8
+ * this package exports must `await requirePolishdAuth()` before doing any work.
9
+ *
10
+ * `createPolishdPage()` records the host app's `authenticate` callback here when
11
+ * the page module is evaluated, and the actions re-run it on each request. The
12
+ * check is therefore made against the caller's own cookies every time; nothing
13
+ * about the decision is trusted from the client.
14
+ *
15
+ * Fails closed: an action reached before any dashboard page has registered a
16
+ * policy is denied.
17
+ */
18
+ export type PolishdAuthPolicy =
19
+ /** `createPolishdPage()` with no `authenticate` — deliberately public. */
20
+ {
21
+ mode: "open";
22
+ }
23
+ /** `createPolishdPage({ authenticate })` — re-checked per action call. */
24
+ | {
25
+ mode: "guarded";
26
+ authenticate: () => boolean | Promise<boolean>;
27
+ };
28
+ /** Record the dashboard's access policy. Called by `createPolishdPage()`. */
29
+ export declare function registerPolishdAuth(next: PolishdAuthPolicy): void;
30
+ /**
31
+ * Thrown when an action is invoked without authorization. Next replaces the
32
+ * message with an opaque digest in production, so nothing leaks to the caller.
33
+ */
34
+ export declare class PolishdUnauthorizedError extends Error {
35
+ constructor();
36
+ }
37
+ /**
38
+ * Authorize the current server-action request, or throw. A callback that
39
+ * itself throws is treated as a denial — an auth helper blowing up must never
40
+ * open the gate.
41
+ */
42
+ export declare function requirePolishdAuth(): Promise<void>;
43
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/ai/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,MAAM,iBAAiB;AAC3B,0EAA0E;AACxE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE;AAClB,0EAA0E;GACxE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC;AAIxE,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAEjE;AAED;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;;CAKlD;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkBxD"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Polishd — server-action authorization (server only).
3
+ *
4
+ * Next.js server actions are independently addressable POST endpoints: their
5
+ * ids ship inside the public client bundle, so anyone can invoke one directly
6
+ * with a `Next-Action` header. Gating the dashboard *page* therefore protects
7
+ * only what renders — it does nothing for the actions behind it. Every action
8
+ * this package exports must `await requirePolishdAuth()` before doing any work.
9
+ *
10
+ * `createPolishdPage()` records the host app's `authenticate` callback here when
11
+ * the page module is evaluated, and the actions re-run it on each request. The
12
+ * check is therefore made against the caller's own cookies every time; nothing
13
+ * about the decision is trusted from the client.
14
+ *
15
+ * Fails closed: an action reached before any dashboard page has registered a
16
+ * policy is denied.
17
+ */
18
+ let policy = null;
19
+ /** Record the dashboard's access policy. Called by `createPolishdPage()`. */
20
+ export function registerPolishdAuth(next) {
21
+ policy = next;
22
+ }
23
+ /**
24
+ * Thrown when an action is invoked without authorization. Next replaces the
25
+ * message with an opaque digest in production, so nothing leaks to the caller.
26
+ */
27
+ export class PolishdUnauthorizedError extends Error {
28
+ constructor() {
29
+ super("[polishd] unauthorized");
30
+ this.name = "PolishdUnauthorizedError";
31
+ }
32
+ }
33
+ /**
34
+ * Authorize the current server-action request, or throw. A callback that
35
+ * itself throws is treated as a denial — an auth helper blowing up must never
36
+ * open the gate.
37
+ */
38
+ export async function requirePolishdAuth() {
39
+ // No dashboard page registered a policy in this runtime: deny rather than
40
+ // guess. Reaching an action without the page module loaded is not a flow the
41
+ // dashboard produces.
42
+ if (!policy)
43
+ throw new PolishdUnauthorizedError();
44
+ if (policy.mode === "open")
45
+ return;
46
+ let ok = false;
47
+ try {
48
+ ok = await policy.authenticate();
49
+ }
50
+ catch (err) {
51
+ console.warn("[polishd] authenticate() threw during an action call, denying:", err instanceof Error ? err.message : err);
52
+ ok = false;
53
+ }
54
+ if (!ok)
55
+ throw new PolishdUnauthorizedError();
56
+ }
57
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/ai/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,IAAI,MAAM,GAA6B,IAAI,CAAC;AAE5C,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,IAAuB;IACzD,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IACjD;QACE,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,0EAA0E;IAC1E,6EAA6E;IAC7E,sBAAsB;IACtB,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,wBAAwB,EAAE,CAAC;IAClD,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO;IAEnC,IAAI,EAAE,GAAG,KAAK,CAAC;IACf,IAAI,CAAC;QACH,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC;IACnC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CACV,gEAAgE,EAChE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CACzC,CAAC;QACF,EAAE,GAAG,KAAK,CAAC;IACb,CAAC;IACD,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,wBAAwB,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { PolishdLossItem, CreateIssueResult } from "./types";
2
+ /** The slice of a loss the pipeline needs. */
3
+ type Loss = Pick<PolishdLossItem, "issue" | "evidence" | "location">;
4
+ /**
5
+ * File a bug from one loss — after verifying it against the source (see the
6
+ * module doc for the pipeline). Returns the issue on success, the stored issue
7
+ * when this evidence was already filed, or `not-a-bug` with the verdict's
8
+ * reasoning when the code disproves the report.
9
+ */
10
+ export declare function createIssueFromLoss(loss: Loss): Promise<CreateIssueResult>;
11
+ /**
12
+ * Decorate a fresh summary's losses with their GitHub issues: attach the link
13
+ * for anything already filed and — when the owner enabled auto-filing — run
14
+ * the verify-and-file pipeline on the new ones. Losses whose report the code
15
+ * disproved are skipped (and remembered, so they aren't re-investigated).
16
+ * Never throws; a hiccup just leaves a loss undecorated, with the manual
17
+ * "file bug" button as the fallback.
18
+ */
19
+ export declare function attachGithubIssues(losses: PolishdLossItem[]): Promise<PolishdLossItem[]>;
20
+ export {};
21
+ //# sourceMappingURL=issues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../../src/ai/issues.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAElE,8CAA8C;AAC9C,KAAK,IAAI,GAAG,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC;AAyPrE;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA4ChF;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAoB9F"}