gitnexus 1.6.4-rc.99 → 1.6.4

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 (143) hide show
  1. package/dist/_shared/index.d.ts +8 -0
  2. package/dist/_shared/index.d.ts.map +1 -1
  3. package/dist/_shared/index.js +9 -0
  4. package/dist/_shared/index.js.map +1 -1
  5. package/dist/_shared/integrations/circuit-breaker.d.ts +183 -0
  6. package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -0
  7. package/dist/_shared/integrations/circuit-breaker.js +236 -0
  8. package/dist/_shared/integrations/circuit-breaker.js.map +1 -0
  9. package/dist/_shared/integrations/resilient-fetch.d.ts +99 -0
  10. package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -0
  11. package/dist/_shared/integrations/resilient-fetch.js +204 -0
  12. package/dist/_shared/integrations/resilient-fetch.js.map +1 -0
  13. package/dist/_shared/integrations/retry.d.ts +60 -0
  14. package/dist/_shared/integrations/retry.d.ts.map +1 -0
  15. package/dist/_shared/integrations/retry.js +67 -0
  16. package/dist/_shared/integrations/retry.js.map +1 -0
  17. package/dist/_shared/integrations/understand-quickly.d.ts +79 -0
  18. package/dist/_shared/integrations/understand-quickly.d.ts.map +1 -0
  19. package/dist/_shared/integrations/understand-quickly.js +139 -0
  20. package/dist/_shared/integrations/understand-quickly.js.map +1 -0
  21. package/dist/_shared/test-helpers.d.ts +13 -0
  22. package/dist/_shared/test-helpers.d.ts.map +1 -0
  23. package/dist/_shared/test-helpers.js +13 -0
  24. package/dist/_shared/test-helpers.js.map +1 -0
  25. package/dist/cli/index.js +9 -0
  26. package/dist/cli/publish.d.ts +29 -0
  27. package/dist/cli/publish.js +174 -0
  28. package/dist/config/ignore-service.js +2 -0
  29. package/dist/core/embeddings/hf-env.d.ts +9 -37
  30. package/dist/core/embeddings/hf-env.js +65 -83
  31. package/dist/core/embeddings/http-client.d.ts +6 -0
  32. package/dist/core/embeddings/http-client.js +22 -18
  33. package/dist/core/group/bridge-db.js +7 -6
  34. package/dist/core/group/config-parser.js +19 -1
  35. package/dist/core/group/extractors/include-extractor.d.ts +39 -0
  36. package/dist/core/group/extractors/include-extractor.js +565 -0
  37. package/dist/core/group/extractors/manifest-extractor.js +8 -0
  38. package/dist/core/group/extractors/thrift-extractor.js +8 -0
  39. package/dist/core/group/matching.js +2 -0
  40. package/dist/core/group/storage.js +8 -1
  41. package/dist/core/group/sync.js +33 -0
  42. package/dist/core/group/types.d.ts +2 -1
  43. package/dist/core/ingestion/call-processor.js +6 -4
  44. package/dist/core/ingestion/cpp-ue-preprocessor.d.ts +12 -0
  45. package/dist/core/ingestion/cpp-ue-preprocessor.js +260 -0
  46. package/dist/core/ingestion/heritage-processor.js +9 -4
  47. package/dist/core/ingestion/import-processor.js +3 -2
  48. package/dist/core/ingestion/language-provider.d.ts +32 -0
  49. package/dist/core/ingestion/languages/c-cpp.js +2 -0
  50. package/dist/core/ingestion/parsing-processor.js +4 -0
  51. package/dist/core/ingestion/workers/parse-worker.js +4 -0
  52. package/dist/core/wiki/llm-client.d.ts +16 -0
  53. package/dist/core/wiki/llm-client.js +101 -63
  54. package/dist/server/api.js +7 -2
  55. package/package.json +2 -2
  56. package/vendor/node_modules/node-addon-api/node_addon_api.target.mk +16 -14
  57. package/vendor/node_modules/node-addon-api/node_addon_api_except.target.mk +16 -14
  58. package/vendor/node_modules/node-addon-api/node_addon_api_except_all.target.mk +16 -14
  59. package/vendor/node_modules/node-addon-api/node_addon_api_maybe.target.mk +16 -14
  60. package/web/assets/{agent-Dl2d-mDi.js → agent-BpHQleHE.js} +17 -17
  61. package/web/assets/architecture-YZFGNWBL-S5CXDPWN-BUzkWgDl.js +1 -0
  62. package/web/assets/{architectureDiagram-EMZXCZ2Q-uiiIna83.js → architectureDiagram-EMZXCZ2Q-CXbzok1G.js} +3 -3
  63. package/web/assets/{blockDiagram-IGV67L2C-uBKdDzXA.js → blockDiagram-IGV67L2C-DA1QJ4ZB.js} +4 -4
  64. package/web/assets/{c4Diagram-DFAF54RM-DTxaX43T.js → c4Diagram-DFAF54RM-DeJV1rCn.js} +2 -2
  65. package/web/assets/{chunk-3GS5O3IE-BxKKu7d7.js → chunk-3GS5O3IE-B8hpnia8.js} +2 -2
  66. package/web/assets/{chunk-3YCYZ6SJ-BJZnGFjg.js → chunk-3YCYZ6SJ-B-BVu21C.js} +1 -1
  67. package/web/assets/{chunk-6NTNNK5N-CIL_eO2d.js → chunk-6NTNNK5N-DW-jYSDr.js} +1 -1
  68. package/web/assets/{chunk-A34GCYZU-Dv4Vkem9.js → chunk-A34GCYZU-BbZl-IKy.js} +1 -1
  69. package/web/assets/{chunk-DJ7UZH7F-Cskl-nb3.js → chunk-DJ7UZH7F-B5tMfF87.js} +1 -1
  70. package/web/assets/{chunk-DKKBVRCY-D3S2BD56.js → chunk-DKKBVRCY-Djm-6K_e.js} +2 -2
  71. package/web/assets/{chunk-DU5LTGQ6-B8Zh51LG.js → chunk-DU5LTGQ6-QPoynYcp.js} +1 -1
  72. package/web/assets/{chunk-FXACKDTF-Cb4xHVUz.js → chunk-FXACKDTF-jieSmbe2.js} +3 -3
  73. package/web/assets/chunk-H3VCZNTA-8EMHZ3SV.js +1 -0
  74. package/web/assets/chunk-HN6EAY2L-CgE_agsD.js +1 -0
  75. package/web/assets/{chunk-O5ABG6QK-V3sk7Bps.js → chunk-O5ABG6QK-DEZV1O19.js} +1 -1
  76. package/web/assets/{chunk-PK6DOVAG-FvG7aKfZ.js → chunk-PK6DOVAG-BXv0JnsJ.js} +3 -3
  77. package/web/assets/{chunk-RNJOYNJ4-BPANsy8V.js → chunk-RNJOYNJ4-BiBkF9dN.js} +1 -1
  78. package/web/assets/chunk-RWUO3TPN-DTWzAhQ1.js +1 -0
  79. package/web/assets/chunk-TBF5ZNIQ-BSl6s9lT.js +1 -0
  80. package/web/assets/{chunk-TYMNRAUI-BnMe6HfP.js → chunk-TYMNRAUI-D1NDLIMG.js} +1 -1
  81. package/web/assets/{chunk-W7ZLLLMY-BqZFKM5j.js → chunk-W7ZLLLMY-Bu-cdLzs.js} +1 -1
  82. package/web/assets/{chunk-WSB5WSVC-EKS7-Nnc.js → chunk-WSB5WSVC-Dor71SOz.js} +1 -1
  83. package/web/assets/{chunk-XGPFEOL4-BBab-enK.js → chunk-XGPFEOL4-CUonLRle.js} +1 -1
  84. package/web/assets/classDiagram-PPOCWD7C-Cdd6UdVv.js +1 -0
  85. package/web/assets/classDiagram-v2-23LJLIIU-DNhxY2Ib.js +1 -0
  86. package/web/assets/{cose-bilkent-PNC4W37J-DjEQkTZ4.js → cose-bilkent-PNC4W37J-DdZq7Qz_.js} +1 -1
  87. package/web/assets/dagre-E77IOHMT-Cfj_6e17.js +4 -0
  88. package/web/assets/{diagram-H7BISOXX-CMuISoJL.js → diagram-H7BISOXX-BNvpsrBv.js} +2 -2
  89. package/web/assets/{diagram-JC5VWROH-DHq_eoeM.js → diagram-JC5VWROH-Cn0rtDbJ.js} +1 -1
  90. package/web/assets/{diagram-LXUTUG65-C0NczIgW.js → diagram-LXUTUG65-CHfoVKNm.js} +2 -2
  91. package/web/assets/{diagram-WEHSV5V5-WsBnpSuc.js → diagram-WEHSV5V5-BLxHksnu.js} +1 -1
  92. package/web/assets/{erDiagram-GCSMX5X6-DxHFHFR6.js → erDiagram-GCSMX5X6-DfdN6iqF.js} +2 -2
  93. package/web/assets/flowDiagram-OTCZ4VVT-aAZw4c_M.js +162 -0
  94. package/web/assets/ganttDiagram-MUNLMDZQ-BEfC_7Or.js +292 -0
  95. package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-B6ZeRLP0.js +1 -0
  96. package/web/assets/gitGraphDiagram-3HKGZ4G3-CG0kC8Q5.js +106 -0
  97. package/web/assets/{index-DDcYO1IJ.js → index-3NUCUW2s.js} +119 -119
  98. package/web/assets/info-OMHHGYJF-BF2H5H6G-BIOsZy0W.js +1 -0
  99. package/web/assets/infoDiagram-MN7RKWGX-DUoMH-pq.js +2 -0
  100. package/web/assets/{ishikawaDiagram-YMYX4NHK-C-JI2Ih7.js → ishikawaDiagram-YMYX4NHK-BF8AtDaj.js} +2 -2
  101. package/web/assets/{journeyDiagram-SO5T7YLQ-IPX4h3iD.js → journeyDiagram-SO5T7YLQ-BW8oKa3N.js} +3 -3
  102. package/web/assets/{kanban-definition-LJHFXRCJ-B23LtshQ.js → kanban-definition-LJHFXRCJ-DzbOsR-2.js} +6 -6
  103. package/web/assets/{mindmap-definition-2EUWGEK5-DJKUsrCo.js → mindmap-definition-2EUWGEK5-BIXGQALq.js} +25 -25
  104. package/web/assets/packet-4T2RLAQJ-EV4IVRXR-Bs7ysNMU.js +1 -0
  105. package/web/assets/pie-ZZUOXDRM-N23DN5KN-Csi8nnbH.js +1 -0
  106. package/web/assets/{pieDiagram-3IATQBI2-Bj26KTKk.js → pieDiagram-3IATQBI2-QY-u7P6a.js} +3 -3
  107. package/web/assets/{quadrantDiagram-E256RVCF-VAk5oIRr.js → quadrantDiagram-E256RVCF-Di4aNiU8.js} +3 -3
  108. package/web/assets/radar-PYXPWWZC-P6TP7ZYP-DJlyXh2-.js +1 -0
  109. package/web/assets/{requirementDiagram-M5DCFWZL-CmlTJ_vQ.js → requirementDiagram-M5DCFWZL-CzUUrhQD.js} +4 -4
  110. package/web/assets/{sankeyDiagram-L3NBLAOT-C-f_DdcX.js → sankeyDiagram-L3NBLAOT-D2fbMiIU.js} +3 -3
  111. package/web/assets/{sequenceDiagram-ZOUHS735-DmtNf1NF.js → sequenceDiagram-ZOUHS735-BNdkqguh.js} +6 -6
  112. package/web/assets/stateDiagram-MLPALWAM-51_Q2w2L.js +1 -0
  113. package/web/assets/stateDiagram-v2-B5LQ5ZB2-8nHs2aNE.js +1 -0
  114. package/web/assets/{timeline-definition-5SPVSISX-BkpqZxt2.js → timeline-definition-5SPVSISX-DkMiGj4Z.js} +9 -9
  115. package/web/assets/treeView-SZITEDCU-5DXDK3XO-Cy6P0_CZ.js +1 -0
  116. package/web/assets/treemap-W4RFUUIX-WYLRDWKO-C_pp1R7A.js +1 -0
  117. package/web/assets/{vennDiagram-IE5QUKF5-iAJ08JBe.js → vennDiagram-IE5QUKF5-BCxMmf9q.js} +3 -3
  118. package/web/assets/wardley-RL74JXVD-BCRCBASE-CMStJhpn.js +1 -0
  119. package/web/assets/{wardleyDiagram-XU3VSMPF-BDiKTkL4.js → wardleyDiagram-XU3VSMPF-ijlLF77s.js} +4 -4
  120. package/web/assets/{xychartDiagram-ZHJ5623Y-CknyZyQK.js → xychartDiagram-ZHJ5623Y-DZ7fP1_3.js} +3 -3
  121. package/web/index.html +1 -1
  122. package/web/assets/architecture-YZFGNWBL-S5CXDPWN-BYXnTu07.js +0 -1
  123. package/web/assets/chunk-H3VCZNTA-BOGhqvxW.js +0 -1
  124. package/web/assets/chunk-HN6EAY2L-CitKFy1e.js +0 -1
  125. package/web/assets/chunk-RWUO3TPN-BHusg2jI.js +0 -1
  126. package/web/assets/chunk-TBF5ZNIQ-CXamrpH3.js +0 -1
  127. package/web/assets/classDiagram-PPOCWD7C-B-LkKU0P.js +0 -1
  128. package/web/assets/classDiagram-v2-23LJLIIU-_NGI4VEh.js +0 -1
  129. package/web/assets/dagre-E77IOHMT-D6Xacg1x.js +0 -4
  130. package/web/assets/flowDiagram-OTCZ4VVT-_G1VxyQS.js +0 -162
  131. package/web/assets/ganttDiagram-MUNLMDZQ-BIHObj0t.js +0 -292
  132. package/web/assets/gitGraph-7Q5UKJZL-54BCDZD5-BXJXsixL.js +0 -1
  133. package/web/assets/gitGraphDiagram-3HKGZ4G3-COZUkHTD.js +0 -106
  134. package/web/assets/info-OMHHGYJF-BF2H5H6G-C820LK0J.js +0 -1
  135. package/web/assets/infoDiagram-MN7RKWGX-BeOjfPc_.js +0 -2
  136. package/web/assets/packet-4T2RLAQJ-EV4IVRXR-BvWtM5jq.js +0 -1
  137. package/web/assets/pie-ZZUOXDRM-N23DN5KN-BnDgj0TQ.js +0 -1
  138. package/web/assets/radar-PYXPWWZC-P6TP7ZYP-2IuDlZ6r.js +0 -1
  139. package/web/assets/stateDiagram-MLPALWAM-Lq7UVrkj.js +0 -1
  140. package/web/assets/stateDiagram-v2-B5LQ5ZB2-DCiXBPeS.js +0 -1
  141. package/web/assets/treeView-SZITEDCU-5DXDK3XO-Ipq1EIaF.js +0 -1
  142. package/web/assets/treemap-W4RFUUIX-WYLRDWKO-DESFxovu.js +0 -1
  143. package/web/assets/wardley-RL74JXVD-BCRCBASE-BVkhckBY.js +0 -1
@@ -0,0 +1,204 @@
1
+ /**
2
+ * `resilientFetch` — fetch wrapped in retry + circuit breaker, with
3
+ * GitHub-flavoured retry classification baked in (Retry-After parsing,
4
+ * 401/403/404/422 treated as terminal client errors).
5
+ *
6
+ * Designed for the `gitnexus publish` GitHub `repository_dispatch`
7
+ * call, but the classification rules apply to any GitHub REST endpoint.
8
+ * Runtime-agnostic — no Node-only imports.
9
+ */
10
+ import { CircuitOpenError, getBreaker, } from './circuit-breaker.js';
11
+ import { computeBackoffMs } from './retry.js';
12
+ export { CircuitOpenError };
13
+ /** Cap on any single Retry-After wait — protects CLI from a buggy registry. */
14
+ export const RETRY_AFTER_CAP_MS = 30_000;
15
+ const DEFAULT_RETRY = {
16
+ maxAttempts: 3,
17
+ baseDelayMs: 500,
18
+ capDelayMs: 5_000,
19
+ };
20
+ /**
21
+ * Parse a `Retry-After` header value into milliseconds.
22
+ * Accepts either a delta-seconds integer (`"30"`) or an HTTP-date.
23
+ * Returns null on parse failure or negative deltas.
24
+ */
25
+ export function parseRetryAfter(value, now = Date.now) {
26
+ if (!value)
27
+ return null;
28
+ const trimmed = value.trim();
29
+ if (trimmed === '')
30
+ return null;
31
+ if (/^[0-9]+$/.test(trimmed)) {
32
+ const seconds = parseInt(trimmed, 10);
33
+ if (Number.isNaN(seconds) || seconds < 0)
34
+ return null;
35
+ return seconds * 1000;
36
+ }
37
+ const target = Date.parse(trimmed);
38
+ if (Number.isNaN(target))
39
+ return null;
40
+ const delta = target - now();
41
+ return delta >= 0 ? delta : 0;
42
+ }
43
+ /** Exported for unit tests. */
44
+ export function classifyOutcome(result, now) {
45
+ if (result.kind === 'error') {
46
+ // Both timer-fired aborts (`AbortSignal.timeout()` → `TimeoutError`)
47
+ // and caller-driven aborts (`AbortController.abort()` → `AbortError`)
48
+ // are terminal: retrying against an already-aborted signal would
49
+ // fail again immediately, and neither outcome reflects backend
50
+ // health. They route through the breaker's neutral path.
51
+ if (result.err instanceof DOMException &&
52
+ (result.err.name === 'TimeoutError' || result.err.name === 'AbortError')) {
53
+ return { kind: 'terminal-network', err: result.err };
54
+ }
55
+ return { kind: 'retryable-network', err: result.err };
56
+ }
57
+ const resp = result.resp;
58
+ if (resp.status >= 200 && resp.status < 400)
59
+ return { kind: 'success', resp };
60
+ if (resp.status === 429) {
61
+ // `resp.headers` is always present on a real `Response`, but tests
62
+ // sometimes stub `fetch` with a plain `{ ok, status }` object. Be
63
+ // defensive — a missing `Retry-After` falls through to exponential
64
+ // backoff, which is the correct behaviour anyway.
65
+ const retryAfterHeader = typeof resp.headers?.get === 'function' ? resp.headers.get('Retry-After') : null;
66
+ const parsed = parseRetryAfter(retryAfterHeader, now);
67
+ return {
68
+ kind: 'retryable-status',
69
+ resp,
70
+ afterMs: parsed !== null ? Math.min(parsed, RETRY_AFTER_CAP_MS) : undefined,
71
+ };
72
+ }
73
+ if (resp.status >= 500)
74
+ return { kind: 'retryable-status', resp, afterMs: undefined };
75
+ return { kind: 'terminal-client', resp };
76
+ }
77
+ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
78
+ function defaultBreakerKey(input) {
79
+ try {
80
+ const url = typeof input === 'string' ? new URL(input) : input;
81
+ return `${url.host}${url.pathname}`;
82
+ }
83
+ catch {
84
+ return String(input);
85
+ }
86
+ }
87
+ /** Final error thrown when retries are exhausted on a 5xx / 429. */
88
+ export class ResilientFetchExhaustedError extends Error {
89
+ response;
90
+ name = 'ResilientFetchExhaustedError';
91
+ constructor(response) {
92
+ super(`Request failed after retries (HTTP ${response.status})`);
93
+ this.response = response;
94
+ }
95
+ }
96
+ /**
97
+ * Wrap `fetch` with bounded retries and a per-process circuit breaker.
98
+ *
99
+ * Semantics:
100
+ * - 5xx and 429 responses are retried; 429 honors `Retry-After` (capped).
101
+ * - Network throws are retried unless they are `TimeoutError` DOMExceptions.
102
+ * - Timeouts and 4xx (other than 429) are returned/thrown without retry
103
+ * AND without incrementing the breaker — they reflect caller config
104
+ * or local network state, not registry health.
105
+ * - Each `fetch` call carries the caller-supplied `signal` (e.g. an
106
+ * `AbortSignal.timeout()`) — that timeout bounds each individual
107
+ * attempt, not the whole retry sequence.
108
+ * - When the breaker is open, throws `CircuitOpenError` synchronously
109
+ * without invoking `fetch`.
110
+ * - When retries are exhausted on a 5xx / 429, throws
111
+ * `ResilientFetchExhaustedError` carrying the last response.
112
+ *
113
+ * Cumulative wall-clock budget:
114
+ * maxAttempts × (per-attempt-timeout + capDelayMs)
115
+ * With defaults (3, 500ms base, 5000ms cap) and a typical 15s per-attempt
116
+ * timeout from the caller's signal, worst case is ~3 × (15s + 5s) = 60s.
117
+ * Callers that want a tighter total bound should reduce `maxAttempts` or
118
+ * wrap `resilientFetch` in their own outer `AbortSignal.timeout()`.
119
+ */
120
+ export async function resilientFetch(input, init, opts = {}) {
121
+ const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
122
+ const now = opts.now ?? (() => Date.now());
123
+ const breaker = opts.breaker ?? getBreaker(opts.breakerKey ?? defaultBreakerKey(input), opts.breakerOptions);
124
+ const retryConfig = {
125
+ maxAttempts: opts.retry?.maxAttempts ?? DEFAULT_RETRY.maxAttempts,
126
+ baseDelayMs: opts.retry?.baseDelayMs ?? DEFAULT_RETRY.baseDelayMs,
127
+ capDelayMs: opts.retry?.capDelayMs ?? DEFAULT_RETRY.capDelayMs,
128
+ };
129
+ const sleep = opts.retry?.sleep ?? defaultSleep;
130
+ const random = opts.retry?.random ?? Math.random;
131
+ // Fail fast on an open breaker, before invoking fetch.
132
+ breaker.check();
133
+ for (let attempt = 0; attempt < retryConfig.maxAttempts; attempt++) {
134
+ let result;
135
+ try {
136
+ // CodeQL js/server-side-request-forgery — flagged because `input`
137
+ // is caller-supplied. Suppressed: every concrete caller passes
138
+ // either a hardcoded URL constant (UNDERSTAND_QUICKLY_DISPATCH_URL,
139
+ // OpenRouter base URL) or a value derived from configuration
140
+ // (env vars, saved settings, the local backend URL). User-input
141
+ // request fields (e.g. PR title, repo name) never flow into
142
+ // `input`. Validating URL shape here would push false-positive
143
+ // rejection onto every caller — wrong layer for the check.
144
+ // lgtm[js/server-side-request-forgery]
145
+ // codeql[js/server-side-request-forgery]
146
+ const resp = await fetchImpl(input, init);
147
+ result = { kind: 'response', resp };
148
+ }
149
+ catch (err) {
150
+ result = { kind: 'error', err };
151
+ }
152
+ const outcome = classifyOutcome(result, now);
153
+ switch (outcome.kind) {
154
+ case 'success':
155
+ breaker.recordSuccess();
156
+ return outcome.resp;
157
+ case 'terminal-client':
158
+ // 4xx: do not count as breaker failure (the server is healthy
159
+ // and rejecting our request — auth, scope, or routing). But
160
+ // also do NOT call recordSuccess: a 401 sandwiched between
161
+ // 5xx responses would otherwise erase the running outage
162
+ // signal. The breaker's neutral path leaves state untouched.
163
+ breaker.recordNeutral();
164
+ return outcome.resp;
165
+ case 'terminal-network':
166
+ // Either `AbortSignal.timeout()` fired locally OR an external
167
+ // caller cancelled the request via AbortController. The server
168
+ // never had a chance to answer; this reflects the user's
169
+ // network or an explicit cancel, not registry health. Don't
170
+ // punish the breaker AND don't reset its outage signal.
171
+ breaker.recordNeutral();
172
+ throw outcome.err;
173
+ case 'retryable-status':
174
+ if (attempt + 1 >= retryConfig.maxAttempts) {
175
+ breaker.recordFailure();
176
+ throw new ResilientFetchExhaustedError(outcome.resp);
177
+ }
178
+ await sleep(computeBackoffMs(attempt, retryConfig.baseDelayMs, retryConfig.capDelayMs, outcome.afterMs, random));
179
+ break;
180
+ case 'retryable-network':
181
+ if (attempt + 1 >= retryConfig.maxAttempts) {
182
+ breaker.recordFailure();
183
+ throw outcome.err;
184
+ }
185
+ await sleep(computeBackoffMs(attempt, retryConfig.baseDelayMs, retryConfig.capDelayMs, undefined, random));
186
+ break;
187
+ default: {
188
+ // Exhaustiveness guard. If a sixth `Outcome` kind is added in
189
+ // future, TypeScript will refuse to assign it to `never` and
190
+ // this line forces the maintainer to add an explicit arm
191
+ // rather than silently fall through to retry/no-retry behaviour.
192
+ const _exhaustive = outcome;
193
+ throw new Error(`resilientFetch: unhandled outcome ${JSON.stringify(_exhaustive)}`);
194
+ }
195
+ }
196
+ }
197
+ // Unreachable: every iteration of the loop either returns (success
198
+ // / terminal-client) or throws (terminal-network / retry exhaustion).
199
+ // The throw is here purely so TypeScript's control-flow analysis sees
200
+ // the function never falls off the end without producing `Promise<Response>`.
201
+ /* c8 ignore next 2 */
202
+ throw new Error('resilientFetch: retry loop terminated unexpectedly');
203
+ }
204
+ //# sourceMappingURL=resilient-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resilient-fetch.js","sourceRoot":"","sources":["../../src/integrations/resilient-fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Bounded retry helper with full-jitter exponential backoff.
3
+ *
4
+ * Runtime-agnostic: depends only on `setTimeout`, `Math.random`, and the
5
+ * Promise machinery — no Node-only imports. Safe to consume from CLI,
6
+ * server, or browser callers.
7
+ *
8
+ * Pattern reference: gitnexus/src/core/embeddings/http-client.ts. This
9
+ * helper is the upgraded form: classification is caller-supplied (so
10
+ * 4xx-vs-5xx-vs-timeout decisions live with the protocol that knows
11
+ * them), backoff is exponential with full jitter, and an optional
12
+ * `afterMs` lets callers honor `Retry-After` headers.
13
+ */
14
+ export interface RetryOptions {
15
+ /** Initial delay before the first retry attempt, in milliseconds. */
16
+ baseDelayMs: number;
17
+ /** Upper bound on any single delay, in milliseconds. */
18
+ capDelayMs: number;
19
+ /** Total attempts including the first call. Must be >= 1. */
20
+ maxAttempts: number;
21
+ /**
22
+ * Decide whether to retry after a thrown error.
23
+ * Return `{retry:false}` to terminate immediately and rethrow.
24
+ * Return `{retry:true}` to retry with exponential-backoff jitter.
25
+ * Return `{retry:true, afterMs}` to wait at least `afterMs` (still
26
+ * subject to `capDelayMs`) — used by callers parsing `Retry-After`.
27
+ */
28
+ isRetryable: (err: unknown, attempt: number) => RetryDecision;
29
+ /** Sleep override — defaults to `setTimeout`. Tests inject fake timers. */
30
+ sleep?: (ms: number) => Promise<void>;
31
+ /** Random override — defaults to `Math.random`. Tests inject seeded values. */
32
+ random?: () => number;
33
+ }
34
+ export type RetryDecision = {
35
+ retry: false;
36
+ } | {
37
+ retry: true;
38
+ afterMs?: number;
39
+ };
40
+ /**
41
+ * Compute the delay before the next retry attempt.
42
+ *
43
+ * - When the caller specifies `afterMs` (e.g., from `Retry-After`), use
44
+ * `min(afterMs, capDelayMs)` so a misbehaving server can't pin the
45
+ * client for an arbitrarily long wait.
46
+ * - Otherwise compute full-jitter exponential backoff:
47
+ * `random() * min(cap, base * 2^attempt)`. Full jitter (rather than
48
+ * "equal jitter") avoids retry-storm thundering herd, per AWS
49
+ * guidance on backoff strategies.
50
+ */
51
+ export declare function computeBackoffMs(attempt: number, baseDelayMs: number, capDelayMs: number, afterMs: number | undefined, random: () => number): number;
52
+ /**
53
+ * Execute `fn` with bounded retries.
54
+ *
55
+ * The classification of "retryable" is the caller's responsibility — see
56
+ * `resilient-fetch.ts` for the GitHub-dispatch-specific rules. This
57
+ * helper is the mechanical retry loop only.
58
+ */
59
+ export declare function withRetry<T>(fn: (attempt: number) => Promise<T>, opts: RetryOptions): Promise<T>;
60
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/integrations/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC;IAC9D,2EAA2E;IAC3E,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAKjF;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,MAAM,GACnB,MAAM,CAOR;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACnC,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,CAAC,CAAC,CA4BZ"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Bounded retry helper with full-jitter exponential backoff.
3
+ *
4
+ * Runtime-agnostic: depends only on `setTimeout`, `Math.random`, and the
5
+ * Promise machinery — no Node-only imports. Safe to consume from CLI,
6
+ * server, or browser callers.
7
+ *
8
+ * Pattern reference: gitnexus/src/core/embeddings/http-client.ts. This
9
+ * helper is the upgraded form: classification is caller-supplied (so
10
+ * 4xx-vs-5xx-vs-timeout decisions live with the protocol that knows
11
+ * them), backoff is exponential with full jitter, and an optional
12
+ * `afterMs` lets callers honor `Retry-After` headers.
13
+ */
14
+ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
15
+ /**
16
+ * Compute the delay before the next retry attempt.
17
+ *
18
+ * - When the caller specifies `afterMs` (e.g., from `Retry-After`), use
19
+ * `min(afterMs, capDelayMs)` so a misbehaving server can't pin the
20
+ * client for an arbitrarily long wait.
21
+ * - Otherwise compute full-jitter exponential backoff:
22
+ * `random() * min(cap, base * 2^attempt)`. Full jitter (rather than
23
+ * "equal jitter") avoids retry-storm thundering herd, per AWS
24
+ * guidance on backoff strategies.
25
+ */
26
+ export function computeBackoffMs(attempt, baseDelayMs, capDelayMs, afterMs, random) {
27
+ if (afterMs !== undefined) {
28
+ return Math.min(Math.max(0, afterMs), capDelayMs);
29
+ }
30
+ const exponential = baseDelayMs * Math.pow(2, attempt);
31
+ const upper = Math.min(capDelayMs, exponential);
32
+ return Math.floor(random() * upper);
33
+ }
34
+ /**
35
+ * Execute `fn` with bounded retries.
36
+ *
37
+ * The classification of "retryable" is the caller's responsibility — see
38
+ * `resilient-fetch.ts` for the GitHub-dispatch-specific rules. This
39
+ * helper is the mechanical retry loop only.
40
+ */
41
+ export async function withRetry(fn, opts) {
42
+ if (opts.maxAttempts < 1) {
43
+ throw new Error(`withRetry: maxAttempts must be >= 1, got ${opts.maxAttempts}`);
44
+ }
45
+ const sleep = opts.sleep ?? defaultSleep;
46
+ const random = opts.random ?? Math.random;
47
+ let lastError;
48
+ for (let attempt = 0; attempt < opts.maxAttempts; attempt++) {
49
+ try {
50
+ return await fn(attempt);
51
+ }
52
+ catch (err) {
53
+ lastError = err;
54
+ const decision = opts.isRetryable(err, attempt);
55
+ if (!decision.retry)
56
+ throw err;
57
+ // Don't sleep after the final attempt.
58
+ if (attempt + 1 >= opts.maxAttempts)
59
+ break;
60
+ const delayMs = computeBackoffMs(attempt, opts.baseDelayMs, opts.capDelayMs, decision.afterMs, random);
61
+ if (delayMs > 0)
62
+ await sleep(delayMs);
63
+ }
64
+ }
65
+ throw lastError;
66
+ }
67
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/integrations/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAyBH,MAAM,YAAY,GAAG,CAAC,EAAU,EAAiB,EAAE,CACjD,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEpD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,WAAmB,EACnB,UAAkB,EAClB,OAA2B,EAC3B,MAAoB;IAEpB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,WAAW,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAAmC,EACnC,IAAkB;IAElB,IAAI,IAAI,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAClF,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IAE1C,IAAI,SAAkB,CAAC;IACvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QAC5D,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,CAAC;YAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC,QAAQ,CAAC,KAAK;gBAAE,MAAM,GAAG,CAAC;YAC/B,uCAAuC;YACvC,IAAI,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,WAAW;gBAAE,MAAM;YAC3C,MAAM,OAAO,GAAG,gBAAgB,CAC9B,OAAO,EACP,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,UAAU,EACf,QAAQ,CAAC,OAAO,EAChB,MAAM,CACP,CAAC;YACF,IAAI,OAAO,GAAG,CAAC;gBAAE,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Understand-Quickly registry integration helpers.
3
+ *
4
+ * Pure, runtime-agnostic logic for opting in to publishing a GitNexus
5
+ * index to the [`looptech-ai/understand-quickly`](https://github.com/looptech-ai/understand-quickly)
6
+ * registry. Lives in `gitnexus-shared` so both the Node CLI and any
7
+ * future browser-side surface can construct identical dispatch payloads.
8
+ *
9
+ * Network I/O lives in the CLI command (`gitnexus/src/cli/publish.ts`)
10
+ * to keep this module free of Node-only imports — see the comment at
11
+ * the top of `gitnexus-shared/src/graph/types.ts`.
12
+ *
13
+ * The protocol contract (single dispatch event, no graph upload) is
14
+ * documented at:
15
+ * https://github.com/looptech-ai/understand-quickly/blob/main/docs/integrations/protocol.md
16
+ */
17
+ /**
18
+ * URL of the registry repo's repository_dispatch endpoint. Hardcoded
19
+ * because the registry is the canonical home for this integration —
20
+ * users who want a private registry can fork and patch.
21
+ */
22
+ export declare const UNDERSTAND_QUICKLY_DISPATCH_URL = "https://api.github.com/repos/looptech-ai/understand-quickly/dispatches";
23
+ /**
24
+ * Event type the registry's sync workflow listens for.
25
+ * See `looptech-ai/understand-quickly/.github/workflows/sync.yml`.
26
+ */
27
+ export declare const UNDERSTAND_QUICKLY_EVENT_TYPE = "sync-entry";
28
+ /** Environment variable that gates the dispatch. */
29
+ export declare const UNDERSTAND_QUICKLY_TOKEN_ENV = "UNDERSTAND_QUICKLY_TOKEN";
30
+ export interface UqDispatchPayload {
31
+ event_type: typeof UNDERSTAND_QUICKLY_EVENT_TYPE;
32
+ client_payload: {
33
+ /** `<owner>/<repo>` shape — must match the registered entry. */
34
+ id: string;
35
+ };
36
+ }
37
+ /**
38
+ * Build the JSON body for the `repository_dispatch` ping. Pure — no
39
+ * env reads, no network. Validates that `id` looks like `owner/repo`
40
+ * (one slash, no whitespace, both halves non-empty) so a misconfigured
41
+ * caller fails loudly before the round-trip.
42
+ */
43
+ export declare function buildUqDispatchPayload(id: string): UqDispatchPayload;
44
+ /**
45
+ * `owner/repo` validation. Conservative on purpose: GitHub's actual
46
+ * naming rules are looser, but we want to catch local paths
47
+ * (`/Users/...`), bare slugs (`my-repo`), and accidental whitespace.
48
+ *
49
+ * Matches GitHub's published slug rules:
50
+ * owner: starts with alnum, then alnum/hyphen only, must end with
51
+ * alnum (no trailing hyphen — GitHub rejects this at account
52
+ * creation, so a `my-org-/repo` input would otherwise pass us
53
+ * and 422 from GitHub). No underscore, no dot. Length cap 39.
54
+ * repo: any of alnum/dot/hyphen/underscore. Length cap 100.
55
+ */
56
+ export declare function isValidOwnerRepo(id: string): boolean;
57
+ /**
58
+ * Strip a single trailing `.git` (case-insensitive) and any trailing
59
+ * slashes from a URL-ish string. Bounded linear: each character is
60
+ * visited at most twice, no backtracking.
61
+ *
62
+ * Replaces `s.replace(/\.git\/*$/i, '').replace(/\/+$/, '')` which
63
+ * CodeQL's polynomial-regex check (codeql/js/polynomial-redos) flags as
64
+ * a worst-case O(n²) on adversarial input like "////.../x".
65
+ */
66
+ export declare function stripGitSuffix(input: string): string;
67
+ /**
68
+ * Parse `owner/repo` out of a git remote URL. Mirrors the heuristic in
69
+ * `gitnexus/src/storage/git.ts:parseRepoNameFromUrl` but keeps both
70
+ * halves so we can build a registry id. Returns `null` on shapes we
71
+ * don't recognise.
72
+ *
73
+ * Examples:
74
+ * git@github.com:looptech-ai/understand-quickly.git
75
+ * https://github.com/looptech-ai/understand-quickly
76
+ * ssh://git@github.com/looptech-ai/understand-quickly.git
77
+ */
78
+ export declare function parseOwnerRepoFromRemote(url: string | null | undefined): string | null;
79
+ //# sourceMappingURL=understand-quickly.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"understand-quickly.d.ts","sourceRoot":"","sources":["../../src/integrations/understand-quickly.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,2EAC8B,CAAC;AAE3E;;;GAGG;AACH,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAE1D,oDAAoD;AACpD,eAAO,MAAM,4BAA4B,6BAA6B,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,OAAO,6BAA6B,CAAC;IACjD,cAAc,EAAE;QACd,gEAAgE;QAChE,EAAE,EAAE,MAAM,CAAC;KACZ,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,CAYpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAsCtF"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Understand-Quickly registry integration helpers.
3
+ *
4
+ * Pure, runtime-agnostic logic for opting in to publishing a GitNexus
5
+ * index to the [`looptech-ai/understand-quickly`](https://github.com/looptech-ai/understand-quickly)
6
+ * registry. Lives in `gitnexus-shared` so both the Node CLI and any
7
+ * future browser-side surface can construct identical dispatch payloads.
8
+ *
9
+ * Network I/O lives in the CLI command (`gitnexus/src/cli/publish.ts`)
10
+ * to keep this module free of Node-only imports — see the comment at
11
+ * the top of `gitnexus-shared/src/graph/types.ts`.
12
+ *
13
+ * The protocol contract (single dispatch event, no graph upload) is
14
+ * documented at:
15
+ * https://github.com/looptech-ai/understand-quickly/blob/main/docs/integrations/protocol.md
16
+ */
17
+ /**
18
+ * URL of the registry repo's repository_dispatch endpoint. Hardcoded
19
+ * because the registry is the canonical home for this integration —
20
+ * users who want a private registry can fork and patch.
21
+ */
22
+ export const UNDERSTAND_QUICKLY_DISPATCH_URL = 'https://api.github.com/repos/looptech-ai/understand-quickly/dispatches';
23
+ /**
24
+ * Event type the registry's sync workflow listens for.
25
+ * See `looptech-ai/understand-quickly/.github/workflows/sync.yml`.
26
+ */
27
+ export const UNDERSTAND_QUICKLY_EVENT_TYPE = 'sync-entry';
28
+ /** Environment variable that gates the dispatch. */
29
+ export const UNDERSTAND_QUICKLY_TOKEN_ENV = 'UNDERSTAND_QUICKLY_TOKEN';
30
+ /**
31
+ * Build the JSON body for the `repository_dispatch` ping. Pure — no
32
+ * env reads, no network. Validates that `id` looks like `owner/repo`
33
+ * (one slash, no whitespace, both halves non-empty) so a misconfigured
34
+ * caller fails loudly before the round-trip.
35
+ */
36
+ export function buildUqDispatchPayload(id) {
37
+ if (!isValidOwnerRepo(id)) {
38
+ throw new Error(`[understand-quickly] expected id of the form "owner/repo", got "${id}". ` +
39
+ `The registry uses this string to look up your entry in registry.json — ` +
40
+ `it must match the GitHub owner/repo of the source code, not a local path.`);
41
+ }
42
+ return {
43
+ event_type: UNDERSTAND_QUICKLY_EVENT_TYPE,
44
+ client_payload: { id },
45
+ };
46
+ }
47
+ /**
48
+ * `owner/repo` validation. Conservative on purpose: GitHub's actual
49
+ * naming rules are looser, but we want to catch local paths
50
+ * (`/Users/...`), bare slugs (`my-repo`), and accidental whitespace.
51
+ *
52
+ * Matches GitHub's published slug rules:
53
+ * owner: starts with alnum, then alnum/hyphen only, must end with
54
+ * alnum (no trailing hyphen — GitHub rejects this at account
55
+ * creation, so a `my-org-/repo` input would otherwise pass us
56
+ * and 422 from GitHub). No underscore, no dot. Length cap 39.
57
+ * repo: any of alnum/dot/hyphen/underscore. Length cap 100.
58
+ */
59
+ export function isValidOwnerRepo(id) {
60
+ return /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9._-]{1,100}$/.test(id);
61
+ }
62
+ /**
63
+ * Strip a single trailing `.git` (case-insensitive) and any trailing
64
+ * slashes from a URL-ish string. Bounded linear: each character is
65
+ * visited at most twice, no backtracking.
66
+ *
67
+ * Replaces `s.replace(/\.git\/*$/i, '').replace(/\/+$/, '')` which
68
+ * CodeQL's polynomial-regex check (codeql/js/polynomial-redos) flags as
69
+ * a worst-case O(n²) on adversarial input like "////.../x".
70
+ */
71
+ export function stripGitSuffix(input) {
72
+ let end = input.length;
73
+ // Trim trailing '/'.
74
+ while (end > 0 && input.charCodeAt(end - 1) === 0x2f)
75
+ end--;
76
+ // Drop one trailing '.git' (case-insensitive).
77
+ if (end >= 4) {
78
+ const tail = input.slice(end - 4, end).toLowerCase();
79
+ if (tail === '.git')
80
+ end -= 4;
81
+ }
82
+ // Trim trailing '/' that may have sat between '.git' and the rest.
83
+ while (end > 0 && input.charCodeAt(end - 1) === 0x2f)
84
+ end--;
85
+ return input.slice(0, end);
86
+ }
87
+ /**
88
+ * Parse `owner/repo` out of a git remote URL. Mirrors the heuristic in
89
+ * `gitnexus/src/storage/git.ts:parseRepoNameFromUrl` but keeps both
90
+ * halves so we can build a registry id. Returns `null` on shapes we
91
+ * don't recognise.
92
+ *
93
+ * Examples:
94
+ * git@github.com:looptech-ai/understand-quickly.git
95
+ * https://github.com/looptech-ai/understand-quickly
96
+ * ssh://git@github.com/looptech-ai/understand-quickly.git
97
+ */
98
+ export function parseOwnerRepoFromRemote(url) {
99
+ if (!url)
100
+ return null;
101
+ const trimmed = url.trim();
102
+ if (!trimmed)
103
+ return null;
104
+ // Strip a trailing `.git` (case-insensitive) and any trailing slashes
105
+ // so https://h/o/r and https://h/o/r.git collapse to the same id.
106
+ // Bounded-linear helper avoids the polynomial-regex CodeQL alert.
107
+ const stripped = stripGitSuffix(trimmed);
108
+ // SCP-form SSH (`git@host:owner/repo`). Capture host so we can reject
109
+ // non-GitHub remotes — a GitLab origin like
110
+ // `https://gitlab.example.com/group/sub/project.git` would otherwise
111
+ // silently dispatch the wrong id (LOW 9).
112
+ const ssh = stripped.match(/^[^@]+@([^:]+):([^/]+)\/([^/]+)$/);
113
+ if (ssh) {
114
+ const host = ssh[1].toLowerCase();
115
+ if (host !== 'github.com' && host !== 'www.github.com')
116
+ return null;
117
+ return `${ssh[2]}/${ssh[3]}`;
118
+ }
119
+ // URL forms (https://, ssh://, git://, file://) — last two path segments.
120
+ const url2 = stripped.match(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/([^/]+)\/(.+)$/);
121
+ if (url2) {
122
+ // Strip optional `userinfo@` (e.g. `ssh://git@github.com/...`).
123
+ const authority = url2[1];
124
+ const atIdx = authority.lastIndexOf('@');
125
+ const hostAndPort = atIdx >= 0 ? authority.slice(atIdx + 1) : authority;
126
+ // Strip `:port` suffix if present.
127
+ const colonIdx = hostAndPort.indexOf(':');
128
+ const host = (colonIdx >= 0 ? hostAndPort.slice(0, colonIdx) : hostAndPort).toLowerCase();
129
+ if (host !== 'github.com' && host !== 'www.github.com')
130
+ return null;
131
+ const segments = url2[2].split('/').filter(Boolean);
132
+ if (segments.length >= 2) {
133
+ const [owner, repo] = segments.slice(-2);
134
+ return `${owner}/${repo}`;
135
+ }
136
+ }
137
+ return null;
138
+ }
139
+ //# sourceMappingURL=understand-quickly.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"understand-quickly.js","sourceRoot":"","sources":["../../src/integrations/understand-quickly.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Test-only helpers.
3
+ *
4
+ * Symbols here are reachable from `gitnexus-shared/test-helpers` so test
5
+ * suites can reset shared registries or exercise internal classifiers,
6
+ * but they are deliberately NOT re-exported from the main `gitnexus-shared`
7
+ * barrel. Production consumers should never import this module — calling
8
+ * `__resetBreakerRegistry__()` from a tool implementation would silently
9
+ * nuke every circuit breaker process-wide.
10
+ */
11
+ export { __resetBreakerRegistry__ } from './integrations/circuit-breaker.js';
12
+ export { classifyOutcome } from './integrations/resilient-fetch.js';
13
+ //# sourceMappingURL=test-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-helpers.d.ts","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Test-only helpers.
3
+ *
4
+ * Symbols here are reachable from `gitnexus-shared/test-helpers` so test
5
+ * suites can reset shared registries or exercise internal classifiers,
6
+ * but they are deliberately NOT re-exported from the main `gitnexus-shared`
7
+ * barrel. Production consumers should never import this module — calling
8
+ * `__resetBreakerRegistry__()` from a tool implementation would silently
9
+ * nuke every circuit breaker process-wide.
10
+ */
11
+ export { __resetBreakerRegistry__ } from './integrations/circuit-breaker.js';
12
+ export { classifyOutcome } from './integrations/resilient-fetch.js';
13
+ //# sourceMappingURL=test-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-helpers.js","sourceRoot":"","sources":["../src/test-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC"}
package/dist/cli/index.js CHANGED
@@ -106,6 +106,15 @@ program
106
106
  .command('augment <pattern>')
107
107
  .description('Augment a search pattern with knowledge graph context (used by hooks)')
108
108
  .action(createLazyAction(() => import('./augment.js'), 'augmentCommand'));
109
+ program
110
+ .command('publish [path]')
111
+ .description('Notify the understand-quickly registry that this repo has a fresh GitNexus index. ' +
112
+ 'Opt-in: requires UNDERSTAND_QUICKLY_TOKEN (fine-grained PAT with ' +
113
+ '`Repository dispatches: write` on looptech-ai/understand-quickly). ' +
114
+ 'No-op without the token. See https://github.com/looptech-ai/understand-quickly.')
115
+ .option('--id <owner/repo>', 'Override the registry id (defaults to the origin remote)')
116
+ .option('--skip-git', 'Treat cwd as the repo root and skip parent git-root discovery')
117
+ .action(createLazyAction(() => import('./publish.js'), 'publishCommand'));
109
118
  // ─── Direct Tool Commands (no MCP overhead) ────────────────────────
110
119
  // These invoke LocalBackend directly for use in eval, scripts, and CI.
111
120
  program