homegraph 1.5.3 → 1.5.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 (132) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +51 -5
  3. package/dist/addons/dynamic-import.d.ts +18 -0
  4. package/dist/addons/dynamic-import.d.ts.map +1 -0
  5. package/dist/addons/dynamic-import.js +25 -0
  6. package/dist/addons/dynamic-import.js.map +1 -0
  7. package/dist/addons/init-template.d.ts +15 -0
  8. package/dist/addons/init-template.d.ts.map +1 -0
  9. package/dist/addons/init-template.js +290 -0
  10. package/dist/addons/init-template.js.map +1 -0
  11. package/dist/addons/loader.d.ts +18 -0
  12. package/dist/addons/loader.d.ts.map +1 -0
  13. package/dist/addons/loader.js +88 -0
  14. package/dist/addons/loader.js.map +1 -0
  15. package/dist/addons/manager.d.ts +106 -0
  16. package/dist/addons/manager.d.ts.map +1 -0
  17. package/dist/addons/manager.js +437 -0
  18. package/dist/addons/manager.js.map +1 -0
  19. package/dist/addons/paths.d.ts +22 -0
  20. package/dist/addons/paths.d.ts.map +1 -0
  21. package/dist/addons/paths.js +65 -0
  22. package/dist/addons/paths.js.map +1 -0
  23. package/dist/addons/registry.d.ts +38 -0
  24. package/dist/addons/registry.d.ts.map +1 -0
  25. package/dist/addons/registry.js +160 -0
  26. package/dist/addons/registry.js.map +1 -0
  27. package/dist/addons/semver.d.ts +21 -0
  28. package/dist/addons/semver.d.ts.map +1 -0
  29. package/dist/addons/semver.js +79 -0
  30. package/dist/addons/semver.js.map +1 -0
  31. package/dist/addons/types.d.ts +51 -0
  32. package/dist/addons/types.d.ts.map +1 -0
  33. package/dist/addons/types.js +14 -0
  34. package/dist/addons/types.js.map +1 -0
  35. package/dist/addons/validate.d.ts +38 -0
  36. package/dist/addons/validate.d.ts.map +1 -0
  37. package/dist/addons/validate.js +165 -0
  38. package/dist/addons/validate.js.map +1 -0
  39. package/dist/arkui/migrate-snapshot.d.ts.map +1 -1
  40. package/dist/arkui/migrate-snapshot.js +58 -34
  41. package/dist/arkui/migrate-snapshot.js.map +1 -1
  42. package/dist/bin/addon-commands.d.ts +27 -0
  43. package/dist/bin/addon-commands.d.ts.map +1 -0
  44. package/dist/bin/addon-commands.js +254 -0
  45. package/dist/bin/addon-commands.js.map +1 -0
  46. package/dist/bin/homegraph.js +6 -1
  47. package/dist/bin/homegraph.js.map +1 -1
  48. package/dist/bin/node-version-check.d.ts +1 -1
  49. package/dist/bin/node-version-check.js +1 -1
  50. package/dist/db/sqlite-adapter.d.ts +2 -3
  51. package/dist/db/sqlite-adapter.d.ts.map +1 -1
  52. package/dist/db/sqlite-adapter.js +2 -3
  53. package/dist/db/sqlite-adapter.js.map +1 -1
  54. package/dist/extraction/languages/arkts.d.ts +14 -0
  55. package/dist/extraction/languages/arkts.d.ts.map +1 -1
  56. package/dist/extraction/languages/arkts.js +78 -32
  57. package/dist/extraction/languages/arkts.js.map +1 -1
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/mcp/explore-repeat-guard.d.ts +123 -0
  62. package/dist/mcp/explore-repeat-guard.d.ts.map +1 -0
  63. package/dist/mcp/explore-repeat-guard.js +503 -0
  64. package/dist/mcp/explore-repeat-guard.js.map +1 -0
  65. package/dist/mcp/explore-session-state.d.ts +20 -0
  66. package/dist/mcp/explore-session-state.d.ts.map +1 -1
  67. package/dist/mcp/explore-session-state.js +31 -0
  68. package/dist/mcp/explore-session-state.js.map +1 -1
  69. package/dist/mcp/proxy.d.ts.map +1 -1
  70. package/dist/mcp/proxy.js +15 -4
  71. package/dist/mcp/proxy.js.map +1 -1
  72. package/dist/mcp/query-pool.d.ts.map +1 -1
  73. package/dist/mcp/query-pool.js +10 -13
  74. package/dist/mcp/query-pool.js.map +1 -1
  75. package/dist/mcp/server-instructions.d.ts +5 -7
  76. package/dist/mcp/server-instructions.d.ts.map +1 -1
  77. package/dist/mcp/server-instructions.js +36 -101
  78. package/dist/mcp/server-instructions.js.map +1 -1
  79. package/dist/mcp/tools.d.ts +33 -2
  80. package/dist/mcp/tools.d.ts.map +1 -1
  81. package/dist/mcp/tools.js +1403 -266
  82. package/dist/mcp/tools.js.map +1 -1
  83. package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
  84. package/dist/resolution/callback-synthesizer.js +166 -0
  85. package/dist/resolution/callback-synthesizer.js.map +1 -1
  86. package/dist/search/query-utils.d.ts +47 -7
  87. package/dist/search/query-utils.d.ts.map +1 -1
  88. package/dist/search/query-utils.js +335 -109
  89. package/dist/search/query-utils.js.map +1 -1
  90. package/dist/spec/build/scan.js +1 -1
  91. package/dist/spec/build/scan.js.map +1 -1
  92. package/dist/spec/build/scope-resolver.d.ts +24 -5
  93. package/dist/spec/build/scope-resolver.d.ts.map +1 -1
  94. package/dist/spec/build/scope-resolver.js +76 -10
  95. package/dist/spec/build/scope-resolver.js.map +1 -1
  96. package/dist/spec/config.d.ts +8 -0
  97. package/dist/spec/config.d.ts.map +1 -1
  98. package/dist/spec/config.js +3 -0
  99. package/dist/spec/config.js.map +1 -1
  100. package/dist/spec/evolve/pipeline.d.ts.map +1 -1
  101. package/dist/spec/evolve/pipeline.js +2 -1
  102. package/dist/spec/evolve/pipeline.js.map +1 -1
  103. package/dist/spec/git/commits.d.ts +8 -3
  104. package/dist/spec/git/commits.d.ts.map +1 -1
  105. package/dist/spec/git/commits.js +12 -11
  106. package/dist/spec/git/commits.js.map +1 -1
  107. package/dist/spec/mine/addon/adapter.d.ts +45 -0
  108. package/dist/spec/mine/addon/adapter.d.ts.map +1 -0
  109. package/dist/spec/mine/addon/adapter.js +118 -0
  110. package/dist/spec/mine/addon/adapter.js.map +1 -0
  111. package/dist/spec/mine/addon/render.d.ts +20 -0
  112. package/dist/spec/mine/addon/render.d.ts.map +1 -0
  113. package/dist/spec/mine/addon/render.js +41 -0
  114. package/dist/spec/mine/addon/render.js.map +1 -0
  115. package/dist/spec/mine/addon/types.d.ts +70 -0
  116. package/dist/spec/mine/addon/types.d.ts.map +1 -0
  117. package/dist/spec/mine/addon/types.js +14 -0
  118. package/dist/spec/mine/addon/types.js.map +1 -0
  119. package/dist/spec/mine/generator.d.ts +5 -1
  120. package/dist/spec/mine/generator.d.ts.map +1 -1
  121. package/dist/spec/mine/generator.js +69 -7
  122. package/dist/spec/mine/generator.js.map +1 -1
  123. package/dist/spec/mine/pipeline.d.ts.map +1 -1
  124. package/dist/spec/mine/pipeline.js +5 -1
  125. package/dist/spec/mine/pipeline.js.map +1 -1
  126. package/dist/spec/mine/scanner.d.ts +1 -0
  127. package/dist/spec/mine/scanner.d.ts.map +1 -1
  128. package/dist/spec/mine/scanner.js +1 -1
  129. package/dist/spec/mine/scanner.js.map +1 -1
  130. package/dist/spec/types.d.ts +1 -1
  131. package/dist/spec/types.d.ts.map +1 -1
  132. package/package.json +6 -6
package/CHANGELOG.md CHANGED
@@ -9,6 +9,30 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+
13
+ ## [1.5.4] - 2026-08-19
14
+
15
+ ### Breaking Changes
16
+
17
+ - Require Node.js **22+** (`engines: >=22`). Node 18–21 are no longer supported; the CLI hard-exits below major 22. Dependencies that were pinned for Node 18 are restored: `commander@^14`, `@clack/prompts@^1.7`, optional `better-sqlite3@^12`. Use `npm run test:node-matrix` (nvm) to exercise majors 22–25 locally.
18
+
19
+ ### New Features
20
+
21
+ - Docs hub at `docs/README.md` plus a short `docs/RELEASE.md` for publishing [`homegraph`](https://www.npmjs.com/package/homegraph) (Actions), GitCode releases, and mirror checks — without duplicating DEVELOPMENT SDD.
22
+ - Explore better matches common survey shapes (data-source, Event→handler, multi-Type deps, resource/preview, NAPI) with honest **Partial** vs **ANSWER NOW**, so agents get anchors without fake-complete dumps that trigger Grep storms.
23
+ - Startup MCP instructions are a shorter locator playbook: how to write `query`, when to stop after explore, and when to Skip — without long low-salience essays.
24
+ - Repeated or near-duplicate `homegraph_explore` bags are guarded so agents do not spin on the same keyword paraphrase.
25
+ - New `homegraph addon` command group installs and manages pluggable addons (`init`, `install`, `list`, `remove`, `enable`, `disable`, `update`) from npm packages or local paths. Addons extend `homegraph spec mine` with external requirement context — e.g. map commit messages to Jira tickets and inject the fetched details into the spec-generation prompt — without HomeGraph knowing any ticket format. Enrichment runs per commit cluster with per-addon timeouts and failure isolation, so a broken addon never blocks spec generation.
26
+ - `homegraph addon` version policy: re-installing a registered addon refuses **downgrades** (`remove` first), upgrades preserve the previous version and **restore it when the new one fails validation**, and `update --latest` forces the newest published version (registry packages only — local-path installs have no dist-tag). Applies to npm packages and local-path installs alike.
27
+
28
+ ### Fixes
29
+
30
+ - `homegraph_arkui_migrate` no longer materializes every `class`/`struct` to find `@Observed` types: it walks scope state-var `observed-ref` edges and scoped files only, so large Harmony repos stop timing out on migrate snapshots.
31
+ - Explore default is **universal mid-lean**: **Anchors** (`symbol` → `file:line`) first, ≤2 spine digests (~12K ceiling, ~3K/file), Relationships off. Partial **Next anchor** ranks domain Manager inventory by query-token overlap + CJK→ASCII domain stems (digests prefer that file) — e.g. 通知订阅 → SubscribeManager over screenlock StateManager, 备份 → Extension over EventManager; when inventory + digest are enough, explore **soft-closes** with matching **Coarse locate — ANSWER** header+footer (no Partial/ANSWER contradiction). Session fuse: ≤2 explores; after Partial, follow-ups must name the Next anchor (no novel-Type bypass); callers/callees refused; ≤1 `homegraph_node`. A second Partial appends a hard stop footer. Qualified `Type.member` callers no longer fall back to every bare `member`. ArkTS: synthesize `commonEventManager` publish↔subscribe (literal event) and `taskpool.execute`→named worker. Skip: chained layout literals (`.width('…')`) and pure C-API “which callback” catalogs. Set `HOMEGRAPH_EXPLORE_FULL_SOURCE=1` for the prior body-heavy dump. Cross-Type “如何驱动 / 状态变化” questions no longer collapse into compact hard-ANSWER-NOW; `homegraph_callers` steers Type-wide inventory instead of per-method fan-out.
32
+ - ArkTS indexing no longer `realpathSync`s every ArkAnalyzer path on the hot path: the project root is canonicalized once per batch, then relative paths are string-stripped (with a cached realpath fallback when prefixes disagree) — restoring Storing speed on large Harmony repos while keeping macOS `/var`↔`/private/var` and symlink-root correctness.
33
+ - Multi-term locator bags (e.g. `item` + `service`) are no longer misrouted into light-mechanism just because `service` is a generic noun, so corroboration / full explore ranking can still surface the matching backend file.
34
+ - Renamed the language-verification guide (was `SEARCH_QUALITY_LOOP.md`) and archived settled design notes under `docs/design/archive/` so the docs tree is easier to navigate.
35
+
12
36
  ## [1.5.3] - 2026-08-11
13
37
 
14
38
  ### New Features
@@ -52,7 +76,6 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
52
76
  - Explore **route mutual-exclusion** hardened so inventory/member surveys cannot steal Type×method interaction, Export-fail wiring, or Release↔destructor compares; Manager **状态来源** goes to data-source inventory; repo-wide literal/`text` import hunts defer to Grep (never light); light-mechanism output capped leaner; system-language / hover / kit-deps prefer explore-first. Regression corpus lives under `test/explore-routing/` (`npm run test:explore-routing`; set `HOMEGRAPH_PROBE_ROOT` for live size checks).
53
77
  - Light-mechanism **XML howto** seeds `convertxml` / `XmlParseUtil` and caps import/source size so explore stays lean enough that agents need not stack Grep/Read.
54
78
 
55
-
56
79
  ## [1.5.2] - 2026-08-05
57
80
 
58
81
  ### New Features
@@ -125,6 +148,8 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
125
148
  - Incremental sync now picks up cross-file relationships that only become resolvable after an edit — for example, when a file gains an export that another, unchanged file was already importing or calling. Previously the reference in the unchanged file was never revisited, so callers, impact, and flow results silently omitted the new edge (while status reported a clean index) until a full re-index. References that can't be resolved yet are now remembered and automatically retried whenever a change introduces a symbol that could satisfy them — this also covers a class gaining a new method that other files already call. Thanks @loadcosmos for the report with a minimal reproduction. (#1240)
126
149
  - The reverse case is fixed too: when an edit removes or moves a symbol (or deletes its file), callers in unchanged files now re-resolve during the same sync — rebinding to the symbol's new home when it moved, or waiting to reconnect automatically when it comes back — instead of silently losing their relationship until a full re-index. (#1240)
127
150
 
151
+ [1.5.4]: https://gitcode.com/ProgramAnalysis/homegraph/tags/v1.5.4
152
+ [1.5.3]: https://gitcode.com/ProgramAnalysis/homegraph/tags/v1.5.3
128
153
  [1.5.2]: https://gitcode.com/ProgramAnalysis/homegraph/tags/v1.5.2
129
154
  [1.5.1]: https://gitcode.com/ProgramAnalysis/homegraph/tags/v1.5.1
130
155
  [1.5.0]: https://gitcode.com/ProgramAnalysis/homegraph/tags/v1.5.0
package/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  ### 1. 安装 CLI
17
17
 
18
- 需要 **Node.js 18+**(推荐 22.5+ 或 24)。SQLite 优先用内置 `node:sqlite`(≥22.5),否则 `better-sqlite3`(11.x,含 Node 18),再不行回退 `node-sqlite3-wasm`。
18
+ 需要 **Node.js 22+**(推荐 22.5+ 或 24)。SQLite 优先用内置 `node:sqlite`(≥22.5),否则 `better-sqlite3`(12.x),再不行回退 `node-sqlite3-wasm`。
19
19
 
20
20
  ```bash
21
21
  npm install -g homegraph
@@ -149,6 +149,7 @@ homegraph impact <符号> # 修改该符号的影响范围
149
149
  homegraph affected [files...] # 根据变更文件查找受影响的测试(支持 --stdin)
150
150
  homegraph spec build # 从已有 .spec 目录(或用户指定目录)构建Spec知识图谱(Commit4Spec)
151
151
  homegraph spec mine # 从 Git 历史挖掘设计Spec文档(AST 分析 + LLM 聚类生成)
152
+ homegraph addon ... # 管理 用户自定义插件(init/install/list/remove/enable/disable/update)
152
153
  homegraph spec match <文本> # 全文搜索相似历史Spec
153
154
  homegraph spec find <文件> # 查找与指定文件关联的Spec
154
155
  homegraph spec trace <符号> # 追溯代码符号关联的Spec
@@ -197,7 +198,31 @@ Commit4Spec 提供两条互补路径将设计Spec与 Git 历史关联,存入 `
197
198
 
198
199
  **路径 1:`spec build`(已有Spec导入)**
199
200
 
200
- 从项目已有的 `.spec` 目录(或用户指定目录)读取Spec文档,解析其中的 Git 引用和变更片段,直接构建知识图谱节点与关系。
201
+ 从项目已有的 `.spec` 目录(或用户指定目录)读取 Spec 文档,通过内置的启发式规则将 Git 提交与 Spec 文档配对,解析配对提交的变更片段,直接构建知识图谱节点与关系。
202
+
203
+ 默认规则与文档发现逻辑可通过 `.homegraph/commit4spec/configs.json` 的 `discovery` 与 `commitScope` 段进行用户自定义
204
+
205
+ ```json
206
+ {
207
+ //Spec 文档发现:
208
+ // {specId}.md 平铺文件,或 {specId}/ 目录下按 `primaryDocCandidates` 顺序取第一个存在的主文档;
209
+ // 目录内匹配 `supplementaryGlobs` 的补充 `.md`文档标题并入 Spec 子标题。
210
+ "discovery": {
211
+ "primaryDocCandidates": ["plan.md", "README.md", "spec.md", "design.md", "{spec_dir_name}.md", "spec-{spec_dir_name}.md",],
212
+ "supplementaryGlobs": ["logic/**/*.md", "design/**/*.md"]
213
+ },
214
+ //commit-spec匹配:
215
+ // 优先从 commit message 的第一行提取 conventional-commit scope(如 `feat(spec03): ...`),归一化后若能在磁盘上找到对应 spec 则直接采用、不回退正文;
216
+ // 若第一行 scope 对应的 spec 在磁盘上不存在(或第一行完全未命中 scope)且配置了 `bodyRegex`,则回退从正文/footer 提取 spec 引用(如 `Spec: spec03` trailer);
217
+ // `bodyRegex` 为可选配置,未配置时仅用标题通道;
218
+ // 默认提取后归一化(`normalize`):剥 `review/` 前缀 → 转小写 → `spec3` 补零为 `spec03`。
219
+ "commitScope": {
220
+ "scopeRegex": "^(?:feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)\\((?:review\\/)?(spec\\d+)\\)",
221
+ "bodyRegex": "^Spec:\\s*(spec\\d+)\\s*$",
222
+ "normalize": { "stripPrefixes": ["review/"], "lowercase": true, "padSpecNumber": true }
223
+ }
224
+ }
225
+ ```
201
226
 
202
227
  **路径 2:`spec mine`(Spec逆向挖掘)**
203
228
 
@@ -205,7 +230,9 @@ Commit4Spec 提供两条互补路径将设计Spec与 Git 历史关联,存入 `
205
230
 
206
231
  支持增量模式(`meta.json` 记录已处理范围)、commit过滤和聚类输出模式(`--skip-llm`)。
207
232
 
208
- `spec mine`逆向挖掘和`spec evolve process`演化更新涉及到模型访问,优先选择用户本地已有的Agent平台(Claude Code >> Codex >> 用户配置LLM),若未安装Claude Code或Codex,则需用户自主配置模型服务,编辑配置文件`.homegraph/commit4spec/configs.json`
233
+ `spec mine`逆向挖掘和`spec evolve process`演化更新涉及到模型访问,优先选择用户本地已有的Agent平台(Claude Code >> Codex >> DevEco Code >> 用户配置LLM),可用环境变量 `HOMEGRAPH_SPEC_AGENT` 强制指定(取值 `claude-code`、`codex`、`deveco-code`,`none` 表示禁用)。
234
+
235
+ 若未安装Claude Code、Codex或DevEco Code,则需用户自主配置模型服务,编辑配置文件`.homegraph/commit4spec/configs.json`
209
236
 
210
237
  ```json
211
238
  // All available options (fields marked * are required):
@@ -230,6 +257,25 @@ homegraph spec find src/auth.ts # 哪些Spec涉及该文件
230
257
  homegraph spec trace UserService # 追溯符号关联的设计Spec
231
258
  ```
232
259
 
260
+ **Addons(插件扩展):**
261
+
262
+ `homegraph addon` 管理可插拔扩展包,为 `spec mine` 注入外部需求上下文(如 Jira 工单详情),无需 HomeGraph 认识任何工单格式:
263
+
264
+ ```bash
265
+ homegraph addon init my-jira # 生成 addon 脚手架(内置示例)
266
+ homegraph addon install ./my-jira # 安装并登记(记录具体版本号)
267
+ homegraph addon list # 查看已登记的 addon 及状态
268
+ homegraph addon update my-jira # 更新(默认按记录 range 内最新;--latest 强制 @latest,仅 registry 包)
269
+ homegraph addon disable my-jira # 停用(保留安装)
270
+ homegraph addon remove my-jira # 注销(--purge 同时删除文件)
271
+ ```
272
+
273
+ Addon 实现 `enrich` 钩子:HomeGraph 按 commit 簇传入其已有的 commit 数据(hash / 消息 / 作者 / 时间戳),addon 返回带去重键的需求补充文本,HomeGraph 去重后渲染进生成 prompt 的 `## Supplement` 段。
274
+
275
+ (可选) `buildPrompt` 钩子:用于整体接管 prompt 组装——HomeGraph 传入簇数据、已去重的补充文本、输出模板与字符预算(软约定),由 addon 自行组装完整 prompt,第一个提供该钩子的 addon 生效,调用失败自动回退到默认组装。
276
+
277
+ 每个 addon 独立超时(15s)且失败不影响生成;包需在 package.json 声明 `"homegraph": { "addon": true, "api": 1 }`;登记表存于 `.homegraph/addons.json`,仅显式登记且启用的 addon 才会被加载。
278
+
233
279
  ---
234
280
 
235
281
  ## 支持的语言
@@ -248,7 +294,7 @@ TypeScript / JavaScript、Python、Go、Rust、Java、C#、PHP、Ruby、C / C++
248
294
 
249
295
  ## 从源码构建
250
296
 
251
- 适用于开发或二次修改。协作约定(代码拉取、Commit、PR 清单)见 [DEVELOPMENT.md](./DEVELOPMENT.md)。
297
+ 适用于开发或二次修改。协作约定(Spec、Commit、PR)见 [DEVELOPMENT.md](./DEVELOPMENT.md);文档目录见 [docs/README.md](./docs/README.md);发包见 [docs/RELEASE.md](./docs/RELEASE.md);本地 CLI/MCP 见 [docs/local-dev-guide.md](./docs/local-dev-guide.md)
252
298
 
253
299
  ```bash
254
300
  git clone <your-repo-url>
@@ -301,7 +347,7 @@ Cursor 等项目级配置写入 `./.cursor/mcp.json`,格式相同。推荐使
301
347
 
302
348
  ## 环境要求
303
349
 
304
- - Node.js **18+**(推荐 22.5+ 或 24;SQLite:`node:sqlite` → better-sqlite3 11.x → wasm)
350
+ - Node.js **22+**(推荐 22.5+ 或 24;SQLite:`node:sqlite` → better-sqlite3 12.x → wasm)
305
351
  - 首次索引时会对项目源码做 AST 解析,大型仓库首次 `init -i` 可能需要数分钟
306
352
  - WSL2 下若项目位于 Windows 盘符(`/mnt/c` 等)且 MCP 连接不稳定,可设置 `HOMEGRAPH_NO_DAEMON=1` 跳过共享后台服务,每个会话独立运行
307
353
 
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Dynamic ESM import for CJS builds.
3
+ *
4
+ * tsc compiles `import()` to `require()` under `module: commonjs`, which
5
+ * fails for ESM-only addon packages. `new Function` bypasses the transform
6
+ * and works in the production CLI (plain CJS Node). Some host contexts
7
+ * (e.g. vitest worker realms) have no dynamic-import callback on
8
+ * Function-created globals — tests mock this module with a plain
9
+ * module-scope `import()`, which works there.
10
+ *
11
+ * @module addons/dynamic-import
12
+ */
13
+ /**
14
+ * Import an ESM module by specifier. For addons the specifier is an
15
+ * absolute `file://` URL of the resolved entry point.
16
+ */
17
+ export declare function importESM(specifier: string): Promise<Record<string, unknown>>;
18
+ //# sourceMappingURL=dynamic-import.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-import.d.ts","sourceRoot":"","sources":["../../src/addons/dynamic-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAOH;;;GAGG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAE7E"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * Dynamic ESM import for CJS builds.
4
+ *
5
+ * tsc compiles `import()` to `require()` under `module: commonjs`, which
6
+ * fails for ESM-only addon packages. `new Function` bypasses the transform
7
+ * and works in the production CLI (plain CJS Node). Some host contexts
8
+ * (e.g. vitest worker realms) have no dynamic-import callback on
9
+ * Function-created globals — tests mock this module with a plain
10
+ * module-scope `import()`, which works there.
11
+ *
12
+ * @module addons/dynamic-import
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.importESM = importESM;
16
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
17
+ const importViaFunction = new Function('specifier', 'return import(specifier)');
18
+ /**
19
+ * Import an ESM module by specifier. For addons the specifier is an
20
+ * absolute `file://` URL of the resolved entry point.
21
+ */
22
+ function importESM(specifier) {
23
+ return importViaFunction(specifier);
24
+ }
25
+ //# sourceMappingURL=dynamic-import.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-import.js","sourceRoot":"","sources":["../../src/addons/dynamic-import.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAWH,8BAEC;AAXD,8DAA8D;AAC9D,MAAM,iBAAiB,GAAG,IAAI,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAEzC,CAAC;AAEtC;;;GAGG;AACH,SAAgB,SAAS,CAAC,SAAiB;IACzC,OAAO,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Addon scaffold generator (`homegraph addon init`).
3
+ *
4
+ * Produces a publishable npm package skeleton with the `homegraph` marker,
5
+ * an `enrich` stub, and a worked Jira example (parse ticket refs → fetch
6
+ * details → return supplements).
7
+ *
8
+ * @module addons/init-template
9
+ */
10
+ /**
11
+ * Create the addon scaffold in `parentDir/<name>`. Returns the paths of the
12
+ * files created (absolute). Throws when the target directory already exists.
13
+ */
14
+ export declare function createAddonScaffold(name: string, parentDir: string, lang: 'js' | 'ts'): string[];
15
+ //# sourceMappingURL=init-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-template.d.ts","sourceRoot":"","sources":["../../src/addons/init-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4OH;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,IAAI,GAAG,IAAI,GAChB,MAAM,EAAE,CA2BV"}
@@ -0,0 +1,290 @@
1
+ "use strict";
2
+ /**
3
+ * Addon scaffold generator (`homegraph addon init`).
4
+ *
5
+ * Produces a publishable npm package skeleton with the `homegraph` marker,
6
+ * an `enrich` stub, and a worked Jira example (parse ticket refs → fetch
7
+ * details → return supplements).
8
+ *
9
+ * @module addons/init-template
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.createAddonScaffold = createAddonScaffold;
46
+ const fs = __importStar(require("fs"));
47
+ const path = __importStar(require("path"));
48
+ function packageJson(name, lang) {
49
+ const entry = lang === 'ts' ? './dist/index.js' : './index.mjs';
50
+ return JSON.stringify({
51
+ name,
52
+ version: '0.1.0',
53
+ description: 'HomeGraph addon: enrich spec-mine prompts with external requirement data',
54
+ type: 'module',
55
+ exports: { '.': entry },
56
+ files: lang === 'ts' ? ['dist', 'examples'] : ['index.mjs', 'examples'],
57
+ scripts: lang === 'ts' ? { build: 'tsc' } : undefined,
58
+ // TypeScript needs the homegraph type declarations to compile.
59
+ devDependencies: lang === 'ts' ? { homegraph: 'latest' } : undefined,
60
+ homegraph: { addon: true, api: 1 },
61
+ }, null, 2) + '\n';
62
+ }
63
+ function tsconfig() {
64
+ return JSON.stringify({
65
+ compilerOptions: {
66
+ target: 'ES2022',
67
+ module: 'NodeNext',
68
+ moduleResolution: 'NodeNext',
69
+ outDir: 'dist',
70
+ declaration: true,
71
+ strict: true,
72
+ esModuleInterop: true,
73
+ skipLibCheck: true,
74
+ },
75
+ include: ['index.ts'],
76
+ }, null, 2) + '\n';
77
+ }
78
+ /** Shared body of the enrich stub — identical for JS and TS. */
79
+ function enrichBody() {
80
+ return ` const supplements = [];
81
+ for (const commit of input.commits) {
82
+ const match = /([A-Z]+-\\d+)/.exec(commit.commitMessage);
83
+ if (match) {
84
+ supplements.push({
85
+ key: match[1],
86
+ text: \`Requirement \${match[1]}: replace this with fetched details.\`,
87
+ commitHash: commit.commitHash,
88
+ });
89
+ }
90
+ }
91
+ return supplements;
92
+ `;
93
+ }
94
+ function indexSource(name, lang) {
95
+ const head = `/**
96
+ * ${name} — HomeGraph spec-mine addon.
97
+ *
98
+ * Contract (api 1) — implement at least one hook:
99
+ * - enrich(input): add external requirement context (e.g. Jira) as
100
+ * supplements; HomeGraph dedupes by \`key\` and renders
101
+ * them into a "## Supplement" prompt section.
102
+ * - buildPrompt(ctx): optional escape hatch that takes over the whole
103
+ * prompt assembly (see ctx.limits — a soft contract).
104
+ *
105
+ * Data shapes (all passed in — you never run git):
106
+ *
107
+ * input { clusterId, commits }
108
+ * input.commits[i] { commitHash, commitMessage, author, timestamp }
109
+ * - commitMessage first line of the commit message
110
+ * - timestamp unix seconds
111
+ *
112
+ * Supplement { key?, text, commitHash? }
113
+ * - key opaque dedupe id (e.g. the ticket key); without it,
114
+ * exact-text dedup is used
115
+ * - text required — the requirement description you assembled
116
+ * - commitHash optional — surfaced for traceability
117
+ *
118
+ * buildPrompt ctx { cluster, supplements, template, limits }
119
+ * - cluster { id, commits, primaryFiles, primarySymbols }
120
+ * - supplements already deduplicated by HomeGraph
121
+ * - template output template (default or user --template)
122
+ * - limits { maxContextChars, maxSupplementChars } — soft
123
+ * contract you are expected to honor
124
+ *
125
+ * Return one Supplement per requirement. Both hooks are optional but at
126
+ * least one must be implemented.
127
+ `;
128
+ if (lang === 'ts') {
129
+ return (`import type { EnrichInput, Supplement } from 'homegraph';\n\n` +
130
+ head +
131
+ ` */\n\n` +
132
+ `export async function enrich(input: EnrichInput): Promise<Supplement[]> {\n` +
133
+ enrichBody() +
134
+ `}\n`);
135
+ }
136
+ return (head +
137
+ ` * @param {import('homegraph').EnrichInput} input - one commit cluster.
138
+ * @returns {Promise<import('homegraph').Supplement[]>}
139
+ */
140
+
141
+ export async function enrich(input) {
142
+ ` +
143
+ enrichBody() +
144
+ `}
145
+ `);
146
+ }
147
+ const JIRA_EXAMPLE = `// Worked example: fetch requirement details from Jira and render them as
148
+ // supplements. Standalone — drop this file into any addon's examples/ dir.
149
+ //
150
+ // Data shapes (enrich input / Supplement / buildPrompt ctx): see the
151
+ // contract comment at the top of index.mjs.
152
+ //
153
+ // const JIRA_BASE = process.env.JIRA_BASE_URL ?? 'https://your-domain.atlassian.net';
154
+ // const JIRA_EMAIL = process.env.JIRA_EMAIL ?? '';
155
+ // const JIRA_TOKEN = process.env.JIRA_API_TOKEN ?? '';
156
+ // const TICKET_RE = /([A-Z]+-\\d+)/g;
157
+
158
+ export async function enrich(input) {
159
+ const seen = new Set();
160
+ const supplements = [];
161
+ for (const commit of input.commits) {
162
+ for (const match of commit.commitMessage.matchAll(TICKET_RE)) {
163
+ const key = match[1];
164
+ if (seen.has(key)) continue; // dedupe across commits in the cluster
165
+ seen.add(key);
166
+ supplements.push({ key, text: await fetchTicket(key), commitHash: commit.commitHash });
167
+ }
168
+ }
169
+ return supplements;
170
+ }
171
+
172
+ async function fetchTicket(key) {
173
+ if (!JIRA_TOKEN) return \`\${key}: set JIRA_API_TOKEN to fetch details.\`;
174
+ const res = await fetch(\`\${JIRA_BASE}/rest/api/3/issue/\${key}\`, {
175
+ headers: { Authorization: 'Basic ' + btoa(JIRA_EMAIL + ':' + JIRA_TOKEN) },
176
+ });
177
+ if (!res.ok) return \`\${key}: fetch failed (\${res.status}).\`;
178
+ const issue = await res.json();
179
+ const summary = issue.fields?.summary ?? '(no summary)';
180
+ const description = issue.fields?.description ?? '';
181
+ return \`\${key} — \${summary}\\n\${description}\`;
182
+ }
183
+ `;
184
+ const BUILD_PROMPT_EXAMPLE = `// buildPrompt example — the optional escape hatch that takes over the whole
185
+ // prompt assembly. Copy it into index.mjs (or re-export it). When an addon
186
+ // implements both hooks, buildPrompt wins for assembly while enrich still
187
+ // runs first — its supplements reach you via ctx.supplements.
188
+ //
189
+ // Input ctx: { cluster, supplements, template, limits }
190
+ // - cluster: { id, commits, primaryFiles, primarySymbols }
191
+ // - supplements: already deduplicated by HomeGraph
192
+ // - template: output template (default or user --template)
193
+ // - limits: { maxContextChars, maxSupplementChars } — soft contract
194
+ // Output: the full user prompt string. Throwing falls back to the default
195
+ // assembler, so you can experiment freely.
196
+ export async function buildPrompt(ctx) {
197
+ const { cluster, supplements, template } = ctx;
198
+
199
+ // Your own section first, then the built-in structure.
200
+ const supplementLines = supplements.map(
201
+ (s) => \`- \${s.text}\${s.commitHash ? \` (commit \${s.commitHash.slice(0, 7)})\` : ''}\`,
202
+ );
203
+
204
+ return [
205
+ supplements.length > 0 ? '## Supplement' : null,
206
+ ...(supplements.length > 0 ? supplementLines : []),
207
+ '',
208
+ \`## Cluster Context — \${cluster.commits.length} commits\`,
209
+ \`Primary files: \${cluster.primaryFiles.join(', ') || '(none)'}\`,
210
+ \`Primary symbols: \${cluster.primarySymbols.join(', ') || '(none)'}\`,
211
+ '',
212
+ '---',
213
+ '',
214
+ 'Fill in this template:',
215
+ '',
216
+ template,
217
+ ]
218
+ .filter(Boolean)
219
+ .join('\\n');
220
+ }
221
+ `;
222
+ const README = `# HomeGraph Addon
223
+
224
+ A pluggable extension for \`homegraph spec mine\`: enrich cluster prompts with
225
+ external requirement context (Jira, internal trackers, APIs) without
226
+ HomeGraph knowing any ticket format.
227
+
228
+ ## Contract (api 1)
229
+
230
+ - \`enrich(input)\` → \`Supplement[]\`
231
+ - \`input\`: \`{ clusterId, commits }\` — HomeGraph passes every commit it
232
+ already knows, never re-runs git.
233
+ - \`input.commits[i]\`: \`{ commitHash, commitMessage, author, timestamp }\`
234
+ (commitMessage is the first line; timestamp is unix seconds).
235
+ - Return one \`Supplement\` per requirement:
236
+ - \`key\` (optional): opaque dedupe id (e.g. the ticket key); without it,
237
+ exact-text dedup is used.
238
+ - \`text\` (required): the requirement description you assembled
239
+ (title / URL / body all fine).
240
+ - \`commitHash\` (optional): surfaced for traceability.
241
+ - HomeGraph renders supplements into a \`## Supplement\` prompt section.
242
+ - \`buildPrompt(ctx)\` (optional escape hatch) — takes over prompt assembly
243
+ entirely. \`ctx\`: \`{ cluster, supplements, template, limits }\`
244
+ - \`cluster\`: \`{ id, commits, primaryFiles, primarySymbols }\`
245
+ - \`supplements\`: already deduplicated by HomeGraph.
246
+ - \`template\`: the output template (default or user \`--template\`).
247
+ - \`limits\`: \`{ maxContextChars, maxSupplementChars }\` — a soft contract
248
+ you are expected to honor.
249
+
250
+ Implement at least one hook. The \`homegraph\` field in package.json must
251
+ declare \`{ "addon": true, "api": 1 }\`.
252
+
253
+ ## Local usage
254
+
255
+ homegraph addon install ./path/to/this/addon
256
+
257
+ See \`examples/jira.mjs\` for enrich and \`examples/build-prompt.mjs\` for prompt assembly.
258
+ `;
259
+ /**
260
+ * Create the addon scaffold in `parentDir/<name>`. Returns the paths of the
261
+ * files created (absolute). Throws when the target directory already exists.
262
+ */
263
+ function createAddonScaffold(name, parentDir, lang) {
264
+ const addonDir = path.join(parentDir, name);
265
+ if (fs.existsSync(addonDir)) {
266
+ throw new Error(`Directory already exists: ${addonDir}`);
267
+ }
268
+ const files = {
269
+ 'package.json': packageJson(name, lang),
270
+ README: README,
271
+ 'examples/jira.mjs': JIRA_EXAMPLE,
272
+ 'examples/build-prompt.mjs': BUILD_PROMPT_EXAMPLE,
273
+ };
274
+ if (lang === 'ts') {
275
+ files['tsconfig.json'] = tsconfig();
276
+ files['index.ts'] = indexSource(name, lang);
277
+ }
278
+ else {
279
+ files['index.mjs'] = indexSource(name, lang);
280
+ }
281
+ const created = [];
282
+ for (const [rel, content] of Object.entries(files)) {
283
+ const filePath = path.join(addonDir, rel);
284
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
285
+ fs.writeFileSync(filePath, content, 'utf-8');
286
+ created.push(filePath);
287
+ }
288
+ return created;
289
+ }
290
+ //# sourceMappingURL=init-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-template.js","sourceRoot":"","sources":["../../src/addons/init-template.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Runtime addon loading.
3
+ *
4
+ * Deterministic lookup, never a directory scan: read the registry
5
+ * (`addons.json`), then for each enabled entry resolve its install directory
6
+ * by name and validate + import it. Missing / invalid / disabled entries are
7
+ * skipped with a warning — a broken addon must never break the host flow.
8
+ *
9
+ * @module addons/loader
10
+ */
11
+ import { LoadedAddon } from './types';
12
+ /**
13
+ * Load all enabled, registered addons for a repository, in registry order.
14
+ * Registry order is the deterministic priority order for consumers that need
15
+ * one winner (e.g. `buildPrompt`).
16
+ */
17
+ export declare function loadAddons(repoRoot: string): Promise<LoadedAddon[]>;
18
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/addons/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAiCzE"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * Runtime addon loading.
4
+ *
5
+ * Deterministic lookup, never a directory scan: read the registry
6
+ * (`addons.json`), then for each enabled entry resolve its install directory
7
+ * by name and validate + import it. Missing / invalid / disabled entries are
8
+ * skipped with a warning — a broken addon must never break the host flow.
9
+ *
10
+ * @module addons/loader
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.loadAddons = loadAddons;
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ const errors_1 = require("../errors");
50
+ const registry_1 = require("./registry");
51
+ const paths_1 = require("./paths");
52
+ const validate_1 = require("./validate");
53
+ /**
54
+ * Load all enabled, registered addons for a repository, in registry order.
55
+ * Registry order is the deterministic priority order for consumers that need
56
+ * one winner (e.g. `buildPrompt`).
57
+ */
58
+ async function loadAddons(repoRoot) {
59
+ const registry = (0, registry_1.readRegistry)(repoRoot);
60
+ const out = [];
61
+ for (const entry of registry.addons) {
62
+ if (!entry.enabled)
63
+ continue;
64
+ const pkgDir = (0, paths_1.addonPkgDir)(repoRoot, entry.name);
65
+ if (!fs.existsSync(path.join(pkgDir, 'package.json'))) {
66
+ (0, errors_1.logWarn)(`Addon ${entry.name} is registered but not installed — skipping`, {
67
+ repoRoot,
68
+ name: entry.name,
69
+ });
70
+ continue;
71
+ }
72
+ const validation = await (0, validate_1.validateAddonPackage)(pkgDir);
73
+ if (!validation.ok) {
74
+ (0, errors_1.logWarn)(`Addon ${entry.name} failed validation — skipping`, {
75
+ name: entry.name,
76
+ reason: validation.reason,
77
+ });
78
+ continue;
79
+ }
80
+ out.push({
81
+ name: entry.name,
82
+ version: validation.version ?? entry.version,
83
+ module: validation.module,
84
+ });
85
+ }
86
+ return out;
87
+ }
88
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/addons/loader.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeH,gCAiCC;AA9CD,uCAAyB;AACzB,2CAA6B;AAC7B,sCAAoC;AACpC,yCAA0C;AAC1C,mCAAsC;AACtC,yCAAkD;AAGlD;;;;GAIG;AACI,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,MAAM,QAAQ,GAAG,IAAA,uBAAY,EAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,GAAG,GAAkB,EAAE,CAAC;IAE9B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC,KAAK,CAAC,OAAO;YAAE,SAAS;QAE7B,MAAM,MAAM,GAAG,IAAA,mBAAW,EAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;YACtD,IAAA,gBAAO,EAAC,SAAS,KAAK,CAAC,IAAI,6CAA6C,EAAE;gBACxE,QAAQ;gBACR,IAAI,EAAE,KAAK,CAAC,IAAI;aACjB,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,IAAA,+BAAoB,EAAC,MAAM,CAAC,CAAC;QACtD,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;YACnB,IAAA,gBAAO,EAAC,SAAS,KAAK,CAAC,IAAI,+BAA+B,EAAE;gBAC1D,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO;YAC5C,MAAM,EAAE,UAAU,CAAC,MAAO;SAC3B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}