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,29 @@
1
+ /**
2
+ * `gitnexus publish` — opt-in ping to the understand-quickly registry.
3
+ *
4
+ * Fires a single `repository_dispatch` event at
5
+ * `looptech-ai/understand-quickly` so the registry knows to refresh its
6
+ * entry for the current repo. Does NOT upload anything: per the
7
+ * understand-quickly protocol, the registry pulls the graph from a
8
+ * raw-GitHub URL the user controls.
9
+ *
10
+ * https://github.com/looptech-ai/understand-quickly/blob/main/docs/integrations/protocol.md
11
+ *
12
+ * Defaults:
13
+ * - Without `UNDERSTAND_QUICKLY_TOKEN` in the env, this is a no-op
14
+ * (prints one informational line, exit 0). Same shape as the
15
+ * `--publish` patterns in sibling tools.
16
+ * - With the token, fires the dispatch and reports the response code.
17
+ *
18
+ * The `id` is derived from the repo's `origin` remote unless the caller
19
+ * passes `--id <owner/repo>` explicitly. We deliberately do NOT auto-add
20
+ * the repo to the registry — registration is one-time and uses the
21
+ * `npx @understand-quickly/cli add` path documented in the protocol.
22
+ */
23
+ export interface PublishOptions {
24
+ /** Override the auto-derived `owner/repo` id. */
25
+ id?: string;
26
+ /** Treat the cwd as the repo root (skip git-root walk). */
27
+ skipGit?: boolean;
28
+ }
29
+ export declare const publishCommand: (inputPath?: string, options?: PublishOptions) => Promise<void>;
@@ -0,0 +1,174 @@
1
+ /**
2
+ * `gitnexus publish` — opt-in ping to the understand-quickly registry.
3
+ *
4
+ * Fires a single `repository_dispatch` event at
5
+ * `looptech-ai/understand-quickly` so the registry knows to refresh its
6
+ * entry for the current repo. Does NOT upload anything: per the
7
+ * understand-quickly protocol, the registry pulls the graph from a
8
+ * raw-GitHub URL the user controls.
9
+ *
10
+ * https://github.com/looptech-ai/understand-quickly/blob/main/docs/integrations/protocol.md
11
+ *
12
+ * Defaults:
13
+ * - Without `UNDERSTAND_QUICKLY_TOKEN` in the env, this is a no-op
14
+ * (prints one informational line, exit 0). Same shape as the
15
+ * `--publish` patterns in sibling tools.
16
+ * - With the token, fires the dispatch and reports the response code.
17
+ *
18
+ * The `id` is derived from the repo's `origin` remote unless the caller
19
+ * passes `--id <owner/repo>` explicitly. We deliberately do NOT auto-add
20
+ * the repo to the registry — registration is one-time and uses the
21
+ * `npx @understand-quickly/cli add` path documented in the protocol.
22
+ */
23
+ import path from 'path';
24
+ import { UNDERSTAND_QUICKLY_DISPATCH_URL, UNDERSTAND_QUICKLY_TOKEN_ENV, buildUqDispatchPayload, isValidOwnerRepo, parseOwnerRepoFromRemote, } from '../_shared/index.js';
25
+ import { getGitRoot, getRemoteOriginUrl, getCurrentCommit } from '../storage/git.js';
26
+ import { hasIndex } from '../storage/repo-manager.js';
27
+ import { cliInfo, cliError } from './cli-message.js';
28
+ const REGISTER_HINT = 'Register your repo once with: npx @understand-quickly/cli add\n' +
29
+ 'Or use the wizard: https://looptech-ai.github.io/understand-quickly/add.html';
30
+ /**
31
+ * Hard cap on the dispatch fetch to keep CI publish steps from stalling
32
+ * for the OS TCP timeout (~2 min) when api.github.com is unreachable.
33
+ * Matches the pattern used in `src/core/embeddings/http-client.ts`.
34
+ */
35
+ const DISPATCH_TIMEOUT_MS = 15_000;
36
+ export const publishCommand = async (inputPath, options = {}) => {
37
+ // ── 0. Token gate FIRST — guarantees true no-op without the token. ──
38
+ // The README, CLI --help, and PR body all promise "exit 0 without
39
+ // UNDERSTAND_QUICKLY_TOKEN". Doing the index/repo-root checks before
40
+ // the token gate would make those promises false for users who haven't
41
+ // run `gitnexus analyze` yet but want to verify the command is wired.
42
+ const token = process.env[UNDERSTAND_QUICKLY_TOKEN_ENV];
43
+ if (!token) {
44
+ cliInfo(`[understand-quickly] ${UNDERSTAND_QUICKLY_TOKEN_ENV} is not set — skipping dispatch.\n` +
45
+ `Set it to a fine-grained PAT with "Repository dispatches: write" on ` +
46
+ `looptech-ai/understand-quickly to enable instant resync.\n` +
47
+ `(Without the token, the registry's nightly sync still picks up your entry.)`, { skipped: 'no-token' });
48
+ return;
49
+ }
50
+ // ── 1. Resolve the repo root (same precedence as `analyze`) ──────────
51
+ let repoPath;
52
+ if (inputPath) {
53
+ repoPath = path.resolve(inputPath);
54
+ }
55
+ else if (options.skipGit) {
56
+ repoPath = path.resolve(process.cwd());
57
+ }
58
+ else {
59
+ const gitRoot = getGitRoot(process.cwd());
60
+ if (!gitRoot) {
61
+ cliError('[understand-quickly] not inside a git repository.\n' +
62
+ 'Run from a repo, or pass --skip-git to publish from the current directory.');
63
+ process.exitCode = 1;
64
+ return;
65
+ }
66
+ repoPath = gitRoot;
67
+ }
68
+ // ── 2. Confirm a GitNexus index exists ───────────────────────────────
69
+ // Publishing without an index is almost always a mistake — the
70
+ // registry's nightly sync would fetch a stale or missing graph file
71
+ // and mark the entry `missing`. Refuse loudly with a fix-it hint.
72
+ if (!(await hasIndex(repoPath))) {
73
+ cliError(`[understand-quickly] no GitNexus index found at ${repoPath}/.gitnexus.\n` +
74
+ 'Run `gitnexus analyze` first, then re-run `gitnexus publish`.');
75
+ process.exitCode = 1;
76
+ return;
77
+ }
78
+ // ── 3. Derive the registry id ─────────────────────────────────────────
79
+ const id = options.id ?? parseOwnerRepoFromRemote(getRemoteOriginUrl(repoPath) ?? undefined) ?? null;
80
+ if (!id || !isValidOwnerRepo(id)) {
81
+ cliError(`[understand-quickly] could not derive a registry id from this repo.\n` +
82
+ `Pass --id <owner/repo> explicitly (e.g. --id looptech-ai/${path.basename(repoPath)}).\n` +
83
+ REGISTER_HINT);
84
+ process.exitCode = 1;
85
+ return;
86
+ }
87
+ // ── 4. Fire the dispatch ─────────────────────────────────────────────
88
+ const payload = buildUqDispatchPayload(id);
89
+ let response;
90
+ try {
91
+ response = await fetch(UNDERSTAND_QUICKLY_DISPATCH_URL, {
92
+ method: 'POST',
93
+ headers: {
94
+ Accept: 'application/vnd.github+json',
95
+ Authorization: `Bearer ${token}`,
96
+ 'X-GitHub-Api-Version': '2022-11-28',
97
+ 'Content-Type': 'application/json',
98
+ 'User-Agent': 'gitnexus-cli',
99
+ },
100
+ body: JSON.stringify(payload),
101
+ signal: AbortSignal.timeout(DISPATCH_TIMEOUT_MS),
102
+ });
103
+ }
104
+ catch (err) {
105
+ // `AbortSignal.timeout()` throws a `DOMException` with `name ===
106
+ // 'TimeoutError'` on Node 18.14+ (and on browsers/Bun). It is NOT
107
+ // a plain `AbortError`. Match the pattern used in
108
+ // gitnexus/src/core/embeddings/http-client.ts so the user sees the
109
+ // targeted "timed out" message instead of a generic "operation
110
+ // was aborted".
111
+ const isTimeout = err instanceof DOMException && err.name === 'TimeoutError';
112
+ if (isTimeout) {
113
+ cliError(`[understand-quickly] dispatch timed out after ${DISPATCH_TIMEOUT_MS}ms. ` +
114
+ `Check network access to api.github.com and retry.`, { id });
115
+ }
116
+ else {
117
+ const msg = err instanceof Error ? err.message : String(err);
118
+ cliError(`[understand-quickly] dispatch network error: ${msg}`, { id });
119
+ }
120
+ process.exitCode = 1;
121
+ return;
122
+ }
123
+ // GitHub returns 204 on success. Distinct branches for 401/403/404/422
124
+ // so users debug without checking the docs.
125
+ if (response.status === 204) {
126
+ await response.body?.cancel().catch(() => { });
127
+ // `getCurrentCommit` is only meaningful in the success path — moving
128
+ // it inside this branch removes a wasted child-process spawn on every
129
+ // error response (LOW 7).
130
+ const commit = getCurrentCommit(repoPath);
131
+ cliInfo(`[understand-quickly] dispatched sync-entry for ${id}` +
132
+ (commit ? ` @ ${commit.slice(0, 7)}` : '') +
133
+ '.\n' +
134
+ `Note: a 204 only confirms GitHub accepted the dispatch. Whether the ` +
135
+ `registry workflow finds an entry for "${id}" is logged at ` +
136
+ `https://github.com/looptech-ai/understand-quickly/actions/workflows/sync.yml`, { id, commit, status: response.status });
137
+ return;
138
+ }
139
+ if (response.status === 401) {
140
+ cliError(`[understand-quickly] dispatch returned 401 — the ${UNDERSTAND_QUICKLY_TOKEN_ENV} value is invalid or expired.\n` +
141
+ `Regenerate a fine-grained PAT at https://github.com/settings/personal-access-tokens ` +
142
+ `with Repository access scoped to looptech-ai/understand-quickly and the ` +
143
+ `"Repository dispatches: write" permission, then retry.`, { id, status: response.status });
144
+ process.exitCode = 1;
145
+ return;
146
+ }
147
+ if (response.status === 403) {
148
+ cliError(`[understand-quickly] dispatch returned 403 — the token authenticated but ` +
149
+ `lacks the "Repository dispatches: write" permission on ` +
150
+ `looptech-ai/understand-quickly. Edit the PAT scopes and retry.`, { id, status: response.status });
151
+ process.exitCode = 1;
152
+ return;
153
+ }
154
+ if (response.status === 404) {
155
+ cliError(`[understand-quickly] dispatch returned 404 — the token cannot reach ` +
156
+ `looptech-ai/understand-quickly. Verify the PAT has Repository access to ` +
157
+ `that exact repo (not just your own org).`, { id, status: response.status });
158
+ process.exitCode = 1;
159
+ return;
160
+ }
161
+ if (response.status === 422) {
162
+ // Malformed event_type / client_payload — a code bug in this CLI,
163
+ // not a user mistake. Surface so we get bug reports.
164
+ const body422 = await response.text().catch(() => '');
165
+ cliError(`[understand-quickly] dispatch returned 422 (this is a CLI bug; please report).\n` +
166
+ `Body: ${body422 || '(empty)'}`, { id, status: response.status });
167
+ process.exitCode = 1;
168
+ return;
169
+ }
170
+ // 5xx and anything else → bubble the body so the user has something to act on.
171
+ const body = await response.text().catch(() => '');
172
+ cliError(`[understand-quickly] dispatch failed with HTTP ${response.status}: ${body || '(empty body)'}`, { id, status: response.status });
173
+ process.exitCode = 1;
174
+ };
@@ -21,6 +21,8 @@ const DEFAULT_IGNORE_LIST = new Set([
21
21
  'bower_components',
22
22
  'jspm_packages',
23
23
  'vendor', // PHP/Go
24
+ 'third_party', // C/C++ (Google-style vendored dependencies)
25
+ '3rdparty', // C/C++ (alternate spelling, also Qt convention)
24
26
  // 'packages' removed - commonly used for monorepo source code (lerna, pnpm, yarn workspaces)
25
27
  'venv',
26
28
  '.venv',
@@ -1,3 +1,4 @@
1
+ import { CircuitBreaker } from '../../_shared/index.js';
1
2
  /** Per-attempt timeout for the full model download (5 minutes). */
2
3
  export declare const HF_DOWNLOAD_TIMEOUT_MS: number;
3
4
  /** Maximum total download attempts (1 initial + N-1 retries). */
@@ -72,41 +73,15 @@ export declare function applyHfEnvOverrides(env: HfEnvSubset): void;
72
73
  export declare function isNetworkFetchError(message: string): boolean;
73
74
  /** @internal Used by `withHfDownloadRetry` to mark a circuit-open rejection. */
74
75
  export declare const CIRCUIT_OPEN_TAG = "hf-circuit-open";
75
- /** Circuit-breaker states. */
76
- type CircuitState = 'closed' | 'open' | 'half-open';
77
76
  /**
78
- * Circuit breaker for HuggingFace model downloads.
79
- *
80
- * After `failureThreshold` consecutive network failures the circuit opens and
81
- * all subsequent calls to `withHfDownloadRetry` fail immediately without
82
- * issuing any network requests. After `resetTimeoutMs` the circuit enters the
83
- * half-open state and the next call is attempted — if it succeeds the circuit
84
- * closes again; if it fails the circuit re-opens.
85
- *
86
- * Exported for unit-testing; production code should use the module-level
87
- * `hfDownloadCircuit` singleton.
77
+ * Module-level singleton shared by both embedder entry points
78
+ * (`core/embeddings/embedder.ts` + `mcp/core/embedder.ts`). Per-process
79
+ * only not persisted across restarts. Backed by the shared
80
+ * `CircuitBreaker` from `gitnexus-shared` (same state machine, same
81
+ * semantics, plus the single-permit half-open gate that prevents
82
+ * recovery-time stampedes).
88
83
  */
89
- export declare class HfDownloadCircuitBreaker {
90
- readonly failureThreshold: number;
91
- readonly resetTimeoutMs: number;
92
- private _state;
93
- private _failures;
94
- /** Timestamp of the last recorded failure (ms since epoch). */
95
- lastFailureAt: number;
96
- constructor(failureThreshold?: number, resetTimeoutMs?: number);
97
- /** Effective state, factoring in the reset-timeout transition. */
98
- get state(): CircuitState;
99
- /** Returns true when the circuit is open and calls should be rejected. */
100
- isOpen(): boolean;
101
- /** Record a successful call — resets the failure counter and closes the circuit. */
102
- recordSuccess(): void;
103
- /** Record a failed call — increments the counter and opens the circuit when the threshold is reached. */
104
- recordFailure(): void;
105
- /** @internal Reset to initial state (used in tests). */
106
- reset(): void;
107
- }
108
- /** Module-level singleton shared by both embedder entry points. */
109
- export declare const hfDownloadCircuit: HfDownloadCircuitBreaker;
84
+ export declare const hfDownloadCircuit: CircuitBreaker;
110
85
  /** @internal Returns true for errors that should abort without retry (circuit-open). */
111
86
  export declare function isHfCircuitOpenError(message: string): boolean;
112
87
  /**
@@ -119,8 +94,6 @@ export declare function isHfDownloadFailure(message: string): boolean;
119
94
  * `ETIMEDOUT` so that `isNetworkFetchError` classifies it correctly.
120
95
  */
121
96
  export declare function withDownloadTimeout<T>(fn: () => Promise<T>, timeoutMs: number): Promise<T>;
122
- /** @internal Async sleep (exposed for testing). */
123
- export declare function sleep(ms: number): Promise<void>;
124
97
  export interface HfRetryOptions {
125
98
  /** Maximum total attempts including the initial one (default: `HF_MAX_ATTEMPTS`). */
126
99
  maxAttempts?: number;
@@ -132,7 +105,7 @@ export interface HfRetryOptions {
132
105
  * Circuit-breaker instance to use. Defaults to the module-level
133
106
  * `hfDownloadCircuit` singleton. Pass a fresh instance in tests.
134
107
  */
135
- circuit?: HfDownloadCircuitBreaker;
108
+ circuit?: CircuitBreaker;
136
109
  /**
137
110
  * Optional callback invoked before each retry (not the initial attempt).
138
111
  * @param attempt - 1-based retry number
@@ -160,4 +133,3 @@ export interface HfRetryOptions {
160
133
  * the calling code still fire.
161
134
  */
162
135
  export declare function withHfDownloadRetry<T>(fn: () => Promise<T>, options?: HfRetryOptions): Promise<T>;
163
- export {};
@@ -1,5 +1,6 @@
1
1
  import os from 'node:os';
2
2
  import { join } from 'node:path';
3
+ import { CircuitBreaker, withRetry } from '../../_shared/index.js';
3
4
  // ---------------------------------------------------------------------------
4
5
  // Download resilience defaults
5
6
  // ---------------------------------------------------------------------------
@@ -85,61 +86,18 @@ export function isNetworkFetchError(message) {
85
86
  /** @internal Used by `withHfDownloadRetry` to mark a circuit-open rejection. */
86
87
  export const CIRCUIT_OPEN_TAG = 'hf-circuit-open';
87
88
  /**
88
- * Circuit breaker for HuggingFace model downloads.
89
- *
90
- * After `failureThreshold` consecutive network failures the circuit opens and
91
- * all subsequent calls to `withHfDownloadRetry` fail immediately without
92
- * issuing any network requests. After `resetTimeoutMs` the circuit enters the
93
- * half-open state and the next call is attempted — if it succeeds the circuit
94
- * closes again; if it fails the circuit re-opens.
95
- *
96
- * Exported for unit-testing; production code should use the module-level
97
- * `hfDownloadCircuit` singleton.
89
+ * Module-level singleton shared by both embedder entry points
90
+ * (`core/embeddings/embedder.ts` + `mcp/core/embedder.ts`). Per-process
91
+ * only not persisted across restarts. Backed by the shared
92
+ * `CircuitBreaker` from `gitnexus-shared` (same state machine, same
93
+ * semantics, plus the single-permit half-open gate that prevents
94
+ * recovery-time stampedes).
98
95
  */
99
- export class HfDownloadCircuitBreaker {
100
- failureThreshold;
101
- resetTimeoutMs;
102
- _state = 'closed';
103
- _failures = 0;
104
- /** Timestamp of the last recorded failure (ms since epoch). */
105
- lastFailureAt = 0;
106
- constructor(failureThreshold = CB_FAILURE_THRESHOLD, resetTimeoutMs = CB_RESET_TIMEOUT_MS) {
107
- this.failureThreshold = failureThreshold;
108
- this.resetTimeoutMs = resetTimeoutMs;
109
- }
110
- /** Effective state, factoring in the reset-timeout transition. */
111
- get state() {
112
- if (this._state === 'open' && Date.now() - this.lastFailureAt > this.resetTimeoutMs) {
113
- this._state = 'half-open';
114
- }
115
- return this._state;
116
- }
117
- /** Returns true when the circuit is open and calls should be rejected. */
118
- isOpen() {
119
- return this.state === 'open';
120
- }
121
- /** Record a successful call — resets the failure counter and closes the circuit. */
122
- recordSuccess() {
123
- this._failures = 0;
124
- this._state = 'closed';
125
- }
126
- /** Record a failed call — increments the counter and opens the circuit when the threshold is reached. */
127
- recordFailure() {
128
- this._failures++;
129
- this.lastFailureAt = Date.now();
130
- if (this._failures >= this.failureThreshold) {
131
- this._state = 'open';
132
- }
133
- }
134
- /** @internal Reset to initial state (used in tests). */
135
- reset() {
136
- this._failures = 0;
137
- this._state = 'closed';
138
- this.lastFailureAt = 0;
139
- }
140
- }
141
- /** Module-level singleton shared by both embedder entry points. */
142
- export const hfDownloadCircuit = new HfDownloadCircuitBreaker();
96
+ export const hfDownloadCircuit = new CircuitBreaker({
97
+ failureThreshold: CB_FAILURE_THRESHOLD,
98
+ cooldownMs: CB_RESET_TIMEOUT_MS,
99
+ key: 'hf-download',
100
+ });
143
101
  // ---------------------------------------------------------------------------
144
102
  // Retry + timeout wrapper
145
103
  // ---------------------------------------------------------------------------
@@ -171,10 +129,6 @@ export function withDownloadTimeout(fn, timeoutMs) {
171
129
  });
172
130
  });
173
131
  }
174
- /** @internal Async sleep (exposed for testing). */
175
- export function sleep(ms) {
176
- return new Promise((resolve) => setTimeout(resolve, ms));
177
- }
178
132
  /**
179
133
  * Retry wrapper for HuggingFace model downloads with per-attempt timeout and
180
134
  * circuit-breaker protection.
@@ -214,37 +168,65 @@ export async function withHfDownloadRetry(fn, options = {}) {
214
168
  ? Math.min(Math.floor(envMaxAttempts), HF_MAX_ATTEMPTS_CAP)
215
169
  : HF_MAX_ATTEMPTS;
216
170
  const { maxAttempts = resolvedMaxAttempts, baseDelayMs = HF_BASE_DELAY_MS, timeoutMs = resolvedTimeout, circuit = hfDownloadCircuit, onRetry, } = options;
217
- if (circuit.isOpen()) {
218
- const secsUntilReset = Math.ceil((circuit.resetTimeoutMs - (Date.now() - circuit.lastFailureAt)) / 1000);
171
+ if (circuit.getState() === 'open') {
172
+ // Compute remaining cooldown without consuming a probe permit.
173
+ const openedAt = circuit.getOpenedAt();
174
+ const secsUntilReset = openedAt !== null ? Math.ceil((circuit.getCooldownMs() - (Date.now() - openedAt)) / 1000) : 0;
219
175
  throw new Error(`${CIRCUIT_OPEN_TAG}: HuggingFace download circuit is open after repeated network failures` +
220
176
  (secsUntilReset > 0 ? ` — will reset in ~${secsUntilReset}s` : ''));
221
177
  }
222
- let lastError = new Error('unknown error');
223
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
224
- try {
178
+ // Retry budget delegated to `withRetry` from gitnexus-shared. The
179
+ // HF-specific bits per-attempt timeout, network-vs-non-network
180
+ // classification, circuit-breaker recording, onRetry callback — wire
181
+ // through the `isRetryable` callback. `circuitTripped` is the
182
+ // sentinel that lets us replace the final thrown error with a
183
+ // CIRCUIT_OPEN_TAG message when the breaker tripped mid-loop.
184
+ let circuitTripped = false;
185
+ try {
186
+ return await withRetry(async () => {
225
187
  const result = await withDownloadTimeout(fn, timeoutMs);
226
188
  circuit.recordSuccess();
227
189
  return result;
190
+ }, {
191
+ maxAttempts,
192
+ baseDelayMs,
193
+ // Disable the cap to match the bespoke pure-exponential
194
+ // progression. With the default `HF_MAX_ATTEMPTS_CAP = 10` and
195
+ // `baseDelayMs = 2000`, the largest possible delay is
196
+ // `2000 * 2^9 = ~17 minutes` — bounded enough not to need a cap.
197
+ capDelayMs: Number.MAX_SAFE_INTEGER,
198
+ isRetryable: (err, attempt) => {
199
+ const error = err instanceof Error ? err : new Error(String(err));
200
+ if (!isNetworkFetchError(error.message)) {
201
+ // Non-network error (e.g. CUDA unavailable) — propagate
202
+ // without retry. Use recordNeutral so the breaker's existing
203
+ // failure-count progress isn't reset by a non-network failure
204
+ // that says nothing about the CDN's health.
205
+ circuit.recordNeutral();
206
+ return { retry: false };
207
+ }
208
+ circuit.recordFailure();
209
+ if (circuit.getState() === 'open') {
210
+ // Circuit just tripped — fail fast, no more retries.
211
+ circuitTripped = true;
212
+ return { retry: false };
213
+ }
214
+ // Mirror the bespoke onRetry contract: fire only when there's
215
+ // actually a next attempt.
216
+ if (attempt + 1 < maxAttempts) {
217
+ onRetry?.(attempt + 1, maxAttempts, error);
218
+ }
219
+ return { retry: true };
220
+ },
221
+ });
222
+ }
223
+ catch (err) {
224
+ if (circuitTripped) {
225
+ throw new Error(`${CIRCUIT_OPEN_TAG}: HuggingFace download circuit opened after ${CB_FAILURE_THRESHOLD} consecutive failures`);
228
226
  }
229
- catch (err) {
230
- lastError = err instanceof Error ? err : new Error(String(err));
231
- if (!isNetworkFetchError(lastError.message)) {
232
- // Non-network error (e.g. CUDA unavailable) — propagate without retry
233
- throw lastError;
234
- }
235
- circuit.recordFailure();
236
- if (circuit.isOpen()) {
237
- // Circuit just tripped — fail fast, no more retries
238
- throw new Error(`${CIRCUIT_OPEN_TAG}: HuggingFace download circuit opened after ${circuit.failureThreshold} consecutive failures`);
239
- }
240
- if (attempt < maxAttempts - 1) {
241
- const delay = baseDelayMs * Math.pow(2, attempt);
242
- onRetry?.(attempt + 1, maxAttempts, lastError);
243
- await sleep(delay);
244
- }
245
- }
227
+ // All retries exhausted — rethrow the last network error so
228
+ // isNetworkFetchError patterns in the calling code still match and
229
+ // surface HF_ENDPOINT guidance.
230
+ throw err;
246
231
  }
247
- // All retries exhausted — throw the last network error so isNetworkFetchError
248
- // patterns in the calling code still match and surface HF_ENDPOINT guidance.
249
- throw lastError;
250
232
  }
@@ -3,6 +3,12 @@
3
3
  *
4
4
  * Shared fetch+retry logic for OpenAI-compatible /v1/embeddings endpoints.
5
5
  * Imported by both the core embedder (batch) and MCP embedder (query).
6
+ *
7
+ * Network resilience is delegated to `resilientFetch` from
8
+ * `gitnexus-shared` — bounded retries with exponential-backoff jitter,
9
+ * `Retry-After` honored on 429, and an in-process circuit breaker that
10
+ * fails fast on a flapping endpoint. Per-attempt timeout is enforced
11
+ * via `AbortSignal.timeout` on the underlying fetch.
6
12
  */
7
13
  /**
8
14
  * Check whether HTTP embedding mode is active (env vars are set).
@@ -3,12 +3,20 @@
3
3
  *
4
4
  * Shared fetch+retry logic for OpenAI-compatible /v1/embeddings endpoints.
5
5
  * Imported by both the core embedder (batch) and MCP embedder (query).
6
+ *
7
+ * Network resilience is delegated to `resilientFetch` from
8
+ * `gitnexus-shared` — bounded retries with exponential-backoff jitter,
9
+ * `Retry-After` honored on 429, and an in-process circuit breaker that
10
+ * fails fast on a flapping endpoint. Per-attempt timeout is enforced
11
+ * via `AbortSignal.timeout` on the underlying fetch.
6
12
  */
13
+ import { CircuitOpenError, ResilientFetchExhaustedError, resilientFetch } from '../../_shared/index.js';
7
14
  const HTTP_TIMEOUT_MS = 30_000;
8
15
  const HTTP_MAX_RETRIES = 2;
9
16
  const HTTP_RETRY_BACKOFF_MS = 1_000;
10
17
  const HTTP_BATCH_SIZE = 64;
11
18
  const DEFAULT_DIMS = 384;
19
+ const HTTP_BREAKER_KEY = 'embeddings-http';
12
20
  /**
13
21
  * Build config from the current process.env snapshot.
14
22
  * Returns null when GITNEXUS_EMBEDDING_URL + GITNEXUS_EMBEDDING_MODEL are unset.
@@ -67,10 +75,10 @@ const safeUrl = (url) => {
67
75
  * @param batchIndex - Logical batch number (for error context)
68
76
  * @param attempt - Current retry attempt (internal)
69
77
  */
70
- const httpEmbedBatch = async (url, batch, model, apiKey, batchIndex = 0, attempt = 0) => {
78
+ const httpEmbedBatch = async (url, batch, model, apiKey, batchIndex = 0) => {
71
79
  let resp;
72
80
  try {
73
- resp = await fetch(url, {
81
+ resp = await resilientFetch(url, {
74
82
  method: 'POST',
75
83
  signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
76
84
  headers: {
@@ -78,32 +86,28 @@ const httpEmbedBatch = async (url, batch, model, apiKey, batchIndex = 0, attempt
78
86
  Authorization: `Bearer ${apiKey}`,
79
87
  },
80
88
  body: JSON.stringify({ input: batch, model }),
89
+ }, {
90
+ breakerKey: HTTP_BREAKER_KEY,
91
+ retry: { maxAttempts: HTTP_MAX_RETRIES + 1, baseDelayMs: HTTP_RETRY_BACKOFF_MS },
81
92
  });
82
93
  }
83
94
  catch (err) {
84
- // Timeouts should not be retried — the server is unresponsive.
85
- // AbortSignal.timeout() throws DOMException with name 'TimeoutError'.
86
- const isTimeout = err instanceof DOMException && err.name === 'TimeoutError';
87
- if (isTimeout) {
95
+ if (err instanceof CircuitOpenError) {
96
+ throw new Error(`Embedding endpoint circuit open (${safeUrl(url)}, batch ${batchIndex}): retry in ${Math.ceil(err.retryAfterMs / 1000)}s`);
97
+ }
98
+ if (err instanceof DOMException && err.name === 'TimeoutError') {
88
99
  throw new Error(`Embedding request timed out after ${HTTP_TIMEOUT_MS}ms (${safeUrl(url)}, batch ${batchIndex})`);
89
100
  }
90
- // DNS, connection errors — retry with backoff
91
- if (attempt < HTTP_MAX_RETRIES) {
92
- const delay = HTTP_RETRY_BACKOFF_MS * (attempt + 1);
93
- await new Promise((r) => setTimeout(r, delay));
94
- return httpEmbedBatch(url, batch, model, apiKey, batchIndex, attempt + 1);
101
+ if (err instanceof ResilientFetchExhaustedError) {
102
+ throw new Error(`Embedding endpoint returned ${err.response.status} (${safeUrl(url)}, batch ${batchIndex})`);
95
103
  }
96
104
  const reason = err instanceof Error ? err.message : String(err);
97
105
  throw new Error(`Embedding request failed (${safeUrl(url)}, batch ${batchIndex}): ${reason}`);
98
106
  }
99
107
  if (!resp.ok) {
100
- const status = resp.status;
101
- if ((status === 429 || status >= 500) && attempt < HTTP_MAX_RETRIES) {
102
- const delay = HTTP_RETRY_BACKOFF_MS * (attempt + 1);
103
- await new Promise((r) => setTimeout(r, delay));
104
- return httpEmbedBatch(url, batch, model, apiKey, batchIndex, attempt + 1);
105
- }
106
- throw new Error(`Embedding endpoint returned ${status} (${safeUrl(url)}, batch ${batchIndex})`);
108
+ // resilientFetch already retried 5xx/429; any non-OK response here is
109
+ // a terminal client error (4xx other than 429).
110
+ throw new Error(`Embedding endpoint returned ${resp.status} (${safeUrl(url)}, batch ${batchIndex})`);
107
111
  }
108
112
  const data = (await resp.json());
109
113
  return data.data;
@@ -589,12 +589,13 @@ export async function openBridgeDbReadOnly(groupDir) {
589
589
  await new Promise((r) => setTimeout(r, delay));
590
590
  }
591
591
  }
592
- // Pino's NDJSON serialization is structurally injection-resistant
593
- // (CodeQL js/log-injection): groupDir and err.message are JSON-escaped
594
- // by the serializer, so no manual CRLF / U+2028 / ANSI sanitization is
595
- // needed. Demoted to debug only fires when the bridge truly gave up
596
- // after retries, and operators only need it at debug verbosity.
597
- bridgeLogger.debug({ groupDir, err: lastErr, attempts: LBUG_OPEN_RETRY_ATTEMPTS }, 'openBridgeDbReadOnly gave up');
592
+ // Strip CRLF from user-controlled strings before logging to close
593
+ // CodeQL js/log-injection. Pino's NDJSON serialization already
594
+ // JSON-escapes all values, but we sanitize here as a defence-in-depth
595
+ // measure so CodeQL can see the taint flow is broken.
596
+ const safeGroupDir = String(groupDir).replace(/[\r\n]/g, ' ');
597
+ const safeErrMsg = lastErr instanceof Error ? String(lastErr.message).replace(/[\r\n]/g, ' ') : undefined;
598
+ bridgeLogger.debug({ groupDir: safeGroupDir, errMsg: safeErrMsg, attempts: LBUG_OPEN_RETRY_ATTEMPTS }, 'openBridgeDbReadOnly gave up');
598
599
  return null;
599
600
  }
600
601
  /* ------------------------------------------------------------------ */
@@ -1,8 +1,25 @@
1
1
  import { createRequire } from 'node:module';
2
2
  const _require = createRequire(import.meta.url);
3
3
  const yaml = _require('js-yaml');
4
- const VALID_CONTRACT_TYPES = ['http', 'grpc', 'thrift', 'topic', 'lib', 'custom'];
4
+ const VALID_CONTRACT_TYPES = [
5
+ 'http',
6
+ 'grpc',
7
+ 'thrift',
8
+ 'topic',
9
+ 'lib',
10
+ 'custom',
11
+ 'include',
12
+ ];
5
13
  const VALID_ROLES = ['provider', 'consumer'];
14
+ // Defaults matter for backward compatibility: any group.yaml that omits a
15
+ // `detect.<type>` key inherits its value from this constant. Adding a new
16
+ // extractor that defaults to `true` silently changes the behavior of every
17
+ // existing group on the next sync. New extractors must default to `false`
18
+ // (opt-in) so operators consciously enable them via group.yaml.
19
+ //
20
+ // `includes`: opt-in. The C/C++ IncludeExtractor (PR #1156) ships disabled by
21
+ // default; enable with `detect.includes: true` for groups containing C/C++
22
+ // repos that need cross-repo header tracking.
6
23
  const DEFAULT_DETECT = {
7
24
  http: true,
8
25
  grpc: true,
@@ -10,6 +27,7 @@ const DEFAULT_DETECT = {
10
27
  topics: true,
11
28
  shared_libs: true,
12
29
  embedding_fallback: true,
30
+ includes: false,
13
31
  workspace_deps: false,
14
32
  };
15
33
  const DEFAULT_MATCHING = {