@oriro/orirocli 0.1.8 → 0.1.9

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 (350) hide show
  1. package/ATTRIBUTION.md +8 -0
  2. package/LICENSE +21 -0
  3. package/package.json +1 -1
  4. package/skills/21stdev/SKILL.md +64 -0
  5. package/skills/graphify/SKILL.md +619 -0
  6. package/skills/graphify/__init__.py +28 -0
  7. package/skills/graphify/__main__.py +4582 -0
  8. package/skills/graphify/affected.py +154 -0
  9. package/skills/graphify/always_on/agents-md.md +12 -0
  10. package/skills/graphify/always_on/antigravity-rules.md +14 -0
  11. package/skills/graphify/always_on/claude-md.md +9 -0
  12. package/skills/graphify/always_on/gemini-md.md +9 -0
  13. package/skills/graphify/always_on/kiro-steering.md +5 -0
  14. package/skills/graphify/always_on/vscode-instructions.md +17 -0
  15. package/skills/graphify/analyze.py +724 -0
  16. package/skills/graphify/benchmark.py +155 -0
  17. package/skills/graphify/build.py +487 -0
  18. package/skills/graphify/cache.py +417 -0
  19. package/skills/graphify/callflow_html.py +2020 -0
  20. package/skills/graphify/cluster.py +272 -0
  21. package/skills/graphify/command-kilo.md +15 -0
  22. package/skills/graphify/dedup.py +429 -0
  23. package/skills/graphify/detect.py +1379 -0
  24. package/skills/graphify/diagnostics.py +390 -0
  25. package/skills/graphify/export.py +1408 -0
  26. package/skills/graphify/extract.py +11570 -0
  27. package/skills/graphify/global_graph.py +159 -0
  28. package/skills/graphify/google_workspace.py +223 -0
  29. package/skills/graphify/hooks.py +457 -0
  30. package/skills/graphify/ingest.py +331 -0
  31. package/skills/graphify/llm.py +1896 -0
  32. package/skills/graphify/manifest.py +4 -0
  33. package/skills/graphify/mcp_ingest.py +392 -0
  34. package/skills/graphify/multigraph_compat.py +212 -0
  35. package/skills/graphify/pg_introspect.py +142 -0
  36. package/skills/graphify/prs.py +748 -0
  37. package/skills/graphify/querylog.py +70 -0
  38. package/skills/graphify/report.py +218 -0
  39. package/skills/graphify/scip_ingest.py +363 -0
  40. package/skills/graphify/security.py +336 -0
  41. package/skills/graphify/semantic_cleanup.py +319 -0
  42. package/skills/graphify/serve.py +1309 -0
  43. package/skills/graphify/skill-aider.md +1246 -0
  44. package/skills/graphify/skill-amp.md +613 -0
  45. package/skills/graphify/skill-claw.md +616 -0
  46. package/skills/graphify/skill-codex.md +613 -0
  47. package/skills/graphify/skill-copilot.md +616 -0
  48. package/skills/graphify/skill-devin.md +1372 -0
  49. package/skills/graphify/skill-droid.md +613 -0
  50. package/skills/graphify/skill-kilo.md +625 -0
  51. package/skills/graphify/skill-kiro.md +615 -0
  52. package/skills/graphify/skill-opencode.md +608 -0
  53. package/skills/graphify/skill-pi.md +615 -0
  54. package/skills/graphify/skill-trae.md +614 -0
  55. package/skills/graphify/skill-vscode.md +612 -0
  56. package/skills/graphify/skill-windows.md +651 -0
  57. package/skills/graphify/skills/amp/references/add-watch.md +56 -0
  58. package/skills/graphify/skills/amp/references/exports.md +71 -0
  59. package/skills/graphify/skills/amp/references/extraction-spec.md +68 -0
  60. package/skills/graphify/skills/amp/references/github-and-merge.md +46 -0
  61. package/skills/graphify/skills/amp/references/hooks.md +33 -0
  62. package/skills/graphify/skills/amp/references/query.md +249 -0
  63. package/skills/graphify/skills/amp/references/transcribe.md +48 -0
  64. package/skills/graphify/skills/amp/references/update.md +179 -0
  65. package/skills/graphify/skills/claude/references/add-watch.md +56 -0
  66. package/skills/graphify/skills/claude/references/exports.md +71 -0
  67. package/skills/graphify/skills/claude/references/extraction-spec.md +68 -0
  68. package/skills/graphify/skills/claude/references/github-and-merge.md +46 -0
  69. package/skills/graphify/skills/claude/references/hooks.md +33 -0
  70. package/skills/graphify/skills/claude/references/query.md +103 -0
  71. package/skills/graphify/skills/claude/references/transcribe.md +48 -0
  72. package/skills/graphify/skills/claude/references/update.md +179 -0
  73. package/skills/graphify/skills/claw/references/add-watch.md +56 -0
  74. package/skills/graphify/skills/claw/references/exports.md +71 -0
  75. package/skills/graphify/skills/claw/references/extraction-spec.md +29 -0
  76. package/skills/graphify/skills/claw/references/github-and-merge.md +46 -0
  77. package/skills/graphify/skills/claw/references/hooks.md +33 -0
  78. package/skills/graphify/skills/claw/references/query.md +249 -0
  79. package/skills/graphify/skills/claw/references/transcribe.md +48 -0
  80. package/skills/graphify/skills/claw/references/update.md +179 -0
  81. package/skills/graphify/skills/codex/references/add-watch.md +56 -0
  82. package/skills/graphify/skills/codex/references/exports.md +71 -0
  83. package/skills/graphify/skills/codex/references/extraction-spec.md +29 -0
  84. package/skills/graphify/skills/codex/references/github-and-merge.md +46 -0
  85. package/skills/graphify/skills/codex/references/hooks.md +33 -0
  86. package/skills/graphify/skills/codex/references/query.md +249 -0
  87. package/skills/graphify/skills/codex/references/transcribe.md +48 -0
  88. package/skills/graphify/skills/codex/references/update.md +179 -0
  89. package/skills/graphify/skills/copilot/references/add-watch.md +56 -0
  90. package/skills/graphify/skills/copilot/references/exports.md +71 -0
  91. package/skills/graphify/skills/copilot/references/extraction-spec.md +68 -0
  92. package/skills/graphify/skills/copilot/references/github-and-merge.md +46 -0
  93. package/skills/graphify/skills/copilot/references/hooks.md +33 -0
  94. package/skills/graphify/skills/copilot/references/query.md +249 -0
  95. package/skills/graphify/skills/copilot/references/transcribe.md +48 -0
  96. package/skills/graphify/skills/copilot/references/update.md +179 -0
  97. package/skills/graphify/skills/droid/references/add-watch.md +56 -0
  98. package/skills/graphify/skills/droid/references/exports.md +71 -0
  99. package/skills/graphify/skills/droid/references/extraction-spec.md +68 -0
  100. package/skills/graphify/skills/droid/references/github-and-merge.md +46 -0
  101. package/skills/graphify/skills/droid/references/hooks.md +33 -0
  102. package/skills/graphify/skills/droid/references/query.md +249 -0
  103. package/skills/graphify/skills/droid/references/transcribe.md +48 -0
  104. package/skills/graphify/skills/droid/references/update.md +179 -0
  105. package/skills/graphify/skills/kilo/references/add-watch.md +56 -0
  106. package/skills/graphify/skills/kilo/references/exports.md +71 -0
  107. package/skills/graphify/skills/kilo/references/extraction-spec.md +68 -0
  108. package/skills/graphify/skills/kilo/references/github-and-merge.md +46 -0
  109. package/skills/graphify/skills/kilo/references/hooks.md +33 -0
  110. package/skills/graphify/skills/kilo/references/query.md +249 -0
  111. package/skills/graphify/skills/kilo/references/transcribe.md +48 -0
  112. package/skills/graphify/skills/kilo/references/update.md +179 -0
  113. package/skills/graphify/skills/kiro/references/add-watch.md +56 -0
  114. package/skills/graphify/skills/kiro/references/exports.md +71 -0
  115. package/skills/graphify/skills/kiro/references/extraction-spec.md +29 -0
  116. package/skills/graphify/skills/kiro/references/github-and-merge.md +46 -0
  117. package/skills/graphify/skills/kiro/references/hooks.md +33 -0
  118. package/skills/graphify/skills/kiro/references/query.md +249 -0
  119. package/skills/graphify/skills/kiro/references/transcribe.md +48 -0
  120. package/skills/graphify/skills/kiro/references/update.md +179 -0
  121. package/skills/graphify/skills/opencode/references/add-watch.md +56 -0
  122. package/skills/graphify/skills/opencode/references/exports.md +71 -0
  123. package/skills/graphify/skills/opencode/references/extraction-spec.md +68 -0
  124. package/skills/graphify/skills/opencode/references/github-and-merge.md +46 -0
  125. package/skills/graphify/skills/opencode/references/hooks.md +33 -0
  126. package/skills/graphify/skills/opencode/references/query.md +249 -0
  127. package/skills/graphify/skills/opencode/references/transcribe.md +48 -0
  128. package/skills/graphify/skills/opencode/references/update.md +179 -0
  129. package/skills/graphify/skills/pi/references/add-watch.md +56 -0
  130. package/skills/graphify/skills/pi/references/exports.md +71 -0
  131. package/skills/graphify/skills/pi/references/extraction-spec.md +29 -0
  132. package/skills/graphify/skills/pi/references/github-and-merge.md +46 -0
  133. package/skills/graphify/skills/pi/references/hooks.md +33 -0
  134. package/skills/graphify/skills/pi/references/query.md +249 -0
  135. package/skills/graphify/skills/pi/references/transcribe.md +48 -0
  136. package/skills/graphify/skills/pi/references/update.md +179 -0
  137. package/skills/graphify/skills/trae/references/add-watch.md +56 -0
  138. package/skills/graphify/skills/trae/references/exports.md +71 -0
  139. package/skills/graphify/skills/trae/references/extraction-spec.md +68 -0
  140. package/skills/graphify/skills/trae/references/github-and-merge.md +46 -0
  141. package/skills/graphify/skills/trae/references/hooks.md +35 -0
  142. package/skills/graphify/skills/trae/references/query.md +249 -0
  143. package/skills/graphify/skills/trae/references/transcribe.md +48 -0
  144. package/skills/graphify/skills/trae/references/update.md +179 -0
  145. package/skills/graphify/skills/vscode/references/add-watch.md +56 -0
  146. package/skills/graphify/skills/vscode/references/exports.md +71 -0
  147. package/skills/graphify/skills/vscode/references/extraction-spec.md +68 -0
  148. package/skills/graphify/skills/vscode/references/github-and-merge.md +46 -0
  149. package/skills/graphify/skills/vscode/references/hooks.md +33 -0
  150. package/skills/graphify/skills/vscode/references/query.md +249 -0
  151. package/skills/graphify/skills/vscode/references/transcribe.md +48 -0
  152. package/skills/graphify/skills/vscode/references/update.md +179 -0
  153. package/skills/graphify/skills/windows/references/add-watch.md +56 -0
  154. package/skills/graphify/skills/windows/references/exports.md +71 -0
  155. package/skills/graphify/skills/windows/references/extraction-spec.md +68 -0
  156. package/skills/graphify/skills/windows/references/github-and-merge.md +46 -0
  157. package/skills/graphify/skills/windows/references/hooks.md +33 -0
  158. package/skills/graphify/skills/windows/references/query.md +249 -0
  159. package/skills/graphify/skills/windows/references/transcribe.md +48 -0
  160. package/skills/graphify/skills/windows/references/update.md +179 -0
  161. package/skills/graphify/symbol_resolution.py +538 -0
  162. package/skills/graphify/transcribe.py +184 -0
  163. package/skills/graphify/tree_html.py +582 -0
  164. package/skills/graphify/validate.py +72 -0
  165. package/skills/graphify/watch.py +898 -0
  166. package/skills/graphify/wiki.py +282 -0
  167. package/skills/impeccable/SKILL.md +186 -0
  168. package/skills/impeccable/agents/impeccable_asset_producer.toml +92 -0
  169. package/skills/impeccable/agents/impeccable_manual_edit_applier.toml +95 -0
  170. package/skills/impeccable/agents/openai.yaml +4 -0
  171. package/skills/impeccable/reference/adapt.md +311 -0
  172. package/skills/impeccable/reference/animate.md +201 -0
  173. package/skills/impeccable/reference/audit.md +133 -0
  174. package/skills/impeccable/reference/bolder.md +113 -0
  175. package/skills/impeccable/reference/brand.md +108 -0
  176. package/skills/impeccable/reference/clarify.md +288 -0
  177. package/skills/impeccable/reference/codex.md +105 -0
  178. package/skills/impeccable/reference/colorize.md +257 -0
  179. package/skills/impeccable/reference/craft.md +123 -0
  180. package/skills/impeccable/reference/critique.md +790 -0
  181. package/skills/impeccable/reference/delight.md +302 -0
  182. package/skills/impeccable/reference/distill.md +111 -0
  183. package/skills/impeccable/reference/document.md +429 -0
  184. package/skills/impeccable/reference/extract.md +69 -0
  185. package/skills/impeccable/reference/harden.md +347 -0
  186. package/skills/impeccable/reference/init.md +172 -0
  187. package/skills/impeccable/reference/interaction-design.md +189 -0
  188. package/skills/impeccable/reference/layout.md +161 -0
  189. package/skills/impeccable/reference/live.md +720 -0
  190. package/skills/impeccable/reference/onboard.md +234 -0
  191. package/skills/impeccable/reference/optimize.md +258 -0
  192. package/skills/impeccable/reference/overdrive.md +130 -0
  193. package/skills/impeccable/reference/polish.md +241 -0
  194. package/skills/impeccable/reference/product.md +60 -0
  195. package/skills/impeccable/reference/quieter.md +99 -0
  196. package/skills/impeccable/reference/shape.md +165 -0
  197. package/skills/impeccable/reference/typeset.md +279 -0
  198. package/skills/impeccable/scripts/cleanup-deprecated.mjs +284 -0
  199. package/skills/impeccable/scripts/command-metadata.json +94 -0
  200. package/skills/impeccable/scripts/context-signals.mjs +225 -0
  201. package/skills/impeccable/scripts/context.mjs +266 -0
  202. package/skills/impeccable/scripts/critique-storage.mjs +242 -0
  203. package/skills/impeccable/scripts/design-parser.mjs +835 -0
  204. package/skills/impeccable/scripts/detect-csp.mjs +198 -0
  205. package/skills/impeccable/scripts/detect.mjs +21 -0
  206. package/skills/impeccable/scripts/detector/browser/injected/index.mjs +1733 -0
  207. package/skills/impeccable/scripts/detector/cli/main.mjs +244 -0
  208. package/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +4618 -0
  209. package/skills/impeccable/scripts/detector/detect-antipatterns.mjs +43 -0
  210. package/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +252 -0
  211. package/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +535 -0
  212. package/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +986 -0
  213. package/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +208 -0
  214. package/skills/impeccable/scripts/detector/engines/visual/screenshot-contrast.mjs +189 -0
  215. package/skills/impeccable/scripts/detector/findings.mjs +12 -0
  216. package/skills/impeccable/scripts/detector/node/file-system.mjs +198 -0
  217. package/skills/impeccable/scripts/detector/profile/profiler.mjs +166 -0
  218. package/skills/impeccable/scripts/detector/registry/antipatterns.mjs +419 -0
  219. package/skills/impeccable/scripts/detector/rules/checks.mjs +2384 -0
  220. package/skills/impeccable/scripts/detector/shared/color.mjs +124 -0
  221. package/skills/impeccable/scripts/detector/shared/constants.mjs +101 -0
  222. package/skills/impeccable/scripts/detector/shared/page.mjs +7 -0
  223. package/skills/impeccable/scripts/impeccable-paths.mjs +126 -0
  224. package/skills/impeccable/scripts/is-generated.mjs +69 -0
  225. package/skills/impeccable/scripts/live-accept.mjs +812 -0
  226. package/skills/impeccable/scripts/live-browser-session.js +123 -0
  227. package/skills/impeccable/scripts/live-browser.js +10295 -0
  228. package/skills/impeccable/scripts/live-commit-manual-edits.mjs +1241 -0
  229. package/skills/impeccable/scripts/live-complete.mjs +75 -0
  230. package/skills/impeccable/scripts/live-completion.mjs +19 -0
  231. package/skills/impeccable/scripts/live-copy-edit-agent.mjs +683 -0
  232. package/skills/impeccable/scripts/live-discard-manual-edits.mjs +51 -0
  233. package/skills/impeccable/scripts/live-event-validation.mjs +137 -0
  234. package/skills/impeccable/scripts/live-inject.mjs +557 -0
  235. package/skills/impeccable/scripts/live-insert-ui.mjs +458 -0
  236. package/skills/impeccable/scripts/live-insert.mjs +272 -0
  237. package/skills/impeccable/scripts/live-manual-edit-evidence.mjs +363 -0
  238. package/skills/impeccable/scripts/live-manual-edits-buffer.mjs +152 -0
  239. package/skills/impeccable/scripts/live-poll.mjs +379 -0
  240. package/skills/impeccable/scripts/live-resume.mjs +94 -0
  241. package/skills/impeccable/scripts/live-server.mjs +2326 -0
  242. package/skills/impeccable/scripts/live-session-store.mjs +289 -0
  243. package/skills/impeccable/scripts/live-status.mjs +61 -0
  244. package/skills/impeccable/scripts/live-svelte-component.mjs +826 -0
  245. package/skills/impeccable/scripts/live-sveltekit-adapter.mjs +274 -0
  246. package/skills/impeccable/scripts/live-ui-core.mjs +179 -0
  247. package/skills/impeccable/scripts/live-vocabulary.mjs +36 -0
  248. package/skills/impeccable/scripts/live-wrap.mjs +894 -0
  249. package/skills/impeccable/scripts/live.mjs +246 -0
  250. package/skills/impeccable/scripts/modern-screenshot.umd.js +14 -0
  251. package/skills/impeccable/scripts/palette.mjs +633 -0
  252. package/skills/impeccable/scripts/pin.mjs +214 -0
  253. package/skills/uipm-ui-styling/LICENSE.txt +202 -0
  254. package/skills/uipm-ui-styling/SKILL.md +328 -0
  255. package/skills/uipm-ui-styling/canvas-fonts/ArsenalSC-OFL.txt +93 -0
  256. package/skills/uipm-ui-styling/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  257. package/skills/uipm-ui-styling/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  258. package/skills/uipm-ui-styling/canvas-fonts/BigShoulders-OFL.txt +93 -0
  259. package/skills/uipm-ui-styling/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  260. package/skills/uipm-ui-styling/canvas-fonts/Boldonse-OFL.txt +93 -0
  261. package/skills/uipm-ui-styling/canvas-fonts/Boldonse-Regular.ttf +0 -0
  262. package/skills/uipm-ui-styling/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  263. package/skills/uipm-ui-styling/canvas-fonts/BricolageGrotesque-OFL.txt +93 -0
  264. package/skills/uipm-ui-styling/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  265. package/skills/uipm-ui-styling/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  266. package/skills/uipm-ui-styling/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  267. package/skills/uipm-ui-styling/canvas-fonts/CrimsonPro-OFL.txt +93 -0
  268. package/skills/uipm-ui-styling/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  269. package/skills/uipm-ui-styling/canvas-fonts/DMMono-OFL.txt +93 -0
  270. package/skills/uipm-ui-styling/canvas-fonts/DMMono-Regular.ttf +0 -0
  271. package/skills/uipm-ui-styling/canvas-fonts/EricaOne-OFL.txt +94 -0
  272. package/skills/uipm-ui-styling/canvas-fonts/EricaOne-Regular.ttf +0 -0
  273. package/skills/uipm-ui-styling/canvas-fonts/GeistMono-Bold.ttf +0 -0
  274. package/skills/uipm-ui-styling/canvas-fonts/GeistMono-OFL.txt +93 -0
  275. package/skills/uipm-ui-styling/canvas-fonts/GeistMono-Regular.ttf +0 -0
  276. package/skills/uipm-ui-styling/canvas-fonts/Gloock-OFL.txt +93 -0
  277. package/skills/uipm-ui-styling/canvas-fonts/Gloock-Regular.ttf +0 -0
  278. package/skills/uipm-ui-styling/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  279. package/skills/uipm-ui-styling/canvas-fonts/IBMPlexMono-OFL.txt +93 -0
  280. package/skills/uipm-ui-styling/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  281. package/skills/uipm-ui-styling/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  282. package/skills/uipm-ui-styling/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  283. package/skills/uipm-ui-styling/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  284. package/skills/uipm-ui-styling/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  285. package/skills/uipm-ui-styling/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  286. package/skills/uipm-ui-styling/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  287. package/skills/uipm-ui-styling/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  288. package/skills/uipm-ui-styling/canvas-fonts/InstrumentSans-OFL.txt +93 -0
  289. package/skills/uipm-ui-styling/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  290. package/skills/uipm-ui-styling/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  291. package/skills/uipm-ui-styling/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  292. package/skills/uipm-ui-styling/canvas-fonts/Italiana-OFL.txt +93 -0
  293. package/skills/uipm-ui-styling/canvas-fonts/Italiana-Regular.ttf +0 -0
  294. package/skills/uipm-ui-styling/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  295. package/skills/uipm-ui-styling/canvas-fonts/JetBrainsMono-OFL.txt +93 -0
  296. package/skills/uipm-ui-styling/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  297. package/skills/uipm-ui-styling/canvas-fonts/Jura-Light.ttf +0 -0
  298. package/skills/uipm-ui-styling/canvas-fonts/Jura-Medium.ttf +0 -0
  299. package/skills/uipm-ui-styling/canvas-fonts/Jura-OFL.txt +93 -0
  300. package/skills/uipm-ui-styling/canvas-fonts/LibreBaskerville-OFL.txt +93 -0
  301. package/skills/uipm-ui-styling/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  302. package/skills/uipm-ui-styling/canvas-fonts/Lora-Bold.ttf +0 -0
  303. package/skills/uipm-ui-styling/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  304. package/skills/uipm-ui-styling/canvas-fonts/Lora-Italic.ttf +0 -0
  305. package/skills/uipm-ui-styling/canvas-fonts/Lora-OFL.txt +93 -0
  306. package/skills/uipm-ui-styling/canvas-fonts/Lora-Regular.ttf +0 -0
  307. package/skills/uipm-ui-styling/canvas-fonts/NationalPark-Bold.ttf +0 -0
  308. package/skills/uipm-ui-styling/canvas-fonts/NationalPark-OFL.txt +93 -0
  309. package/skills/uipm-ui-styling/canvas-fonts/NationalPark-Regular.ttf +0 -0
  310. package/skills/uipm-ui-styling/canvas-fonts/NothingYouCouldDo-OFL.txt +93 -0
  311. package/skills/uipm-ui-styling/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  312. package/skills/uipm-ui-styling/canvas-fonts/Outfit-Bold.ttf +0 -0
  313. package/skills/uipm-ui-styling/canvas-fonts/Outfit-OFL.txt +93 -0
  314. package/skills/uipm-ui-styling/canvas-fonts/Outfit-Regular.ttf +0 -0
  315. package/skills/uipm-ui-styling/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  316. package/skills/uipm-ui-styling/canvas-fonts/PixelifySans-OFL.txt +93 -0
  317. package/skills/uipm-ui-styling/canvas-fonts/PoiretOne-OFL.txt +93 -0
  318. package/skills/uipm-ui-styling/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  319. package/skills/uipm-ui-styling/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  320. package/skills/uipm-ui-styling/canvas-fonts/RedHatMono-OFL.txt +93 -0
  321. package/skills/uipm-ui-styling/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  322. package/skills/uipm-ui-styling/canvas-fonts/Silkscreen-OFL.txt +93 -0
  323. package/skills/uipm-ui-styling/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  324. package/skills/uipm-ui-styling/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  325. package/skills/uipm-ui-styling/canvas-fonts/SmoochSans-OFL.txt +93 -0
  326. package/skills/uipm-ui-styling/canvas-fonts/Tektur-Medium.ttf +0 -0
  327. package/skills/uipm-ui-styling/canvas-fonts/Tektur-OFL.txt +93 -0
  328. package/skills/uipm-ui-styling/canvas-fonts/Tektur-Regular.ttf +0 -0
  329. package/skills/uipm-ui-styling/canvas-fonts/WorkSans-Bold.ttf +0 -0
  330. package/skills/uipm-ui-styling/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  331. package/skills/uipm-ui-styling/canvas-fonts/WorkSans-Italic.ttf +0 -0
  332. package/skills/uipm-ui-styling/canvas-fonts/WorkSans-OFL.txt +93 -0
  333. package/skills/uipm-ui-styling/canvas-fonts/WorkSans-Regular.ttf +0 -0
  334. package/skills/uipm-ui-styling/canvas-fonts/YoungSerif-OFL.txt +93 -0
  335. package/skills/uipm-ui-styling/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  336. package/skills/uipm-ui-styling/references/canvas-design-system.md +320 -0
  337. package/skills/uipm-ui-styling/references/shadcn-accessibility.md +471 -0
  338. package/skills/uipm-ui-styling/references/shadcn-components.md +424 -0
  339. package/skills/uipm-ui-styling/references/shadcn-theming.md +373 -0
  340. package/skills/uipm-ui-styling/references/tailwind-customization.md +483 -0
  341. package/skills/uipm-ui-styling/references/tailwind-responsive.md +382 -0
  342. package/skills/uipm-ui-styling/references/tailwind-utilities.md +455 -0
  343. package/skills/uipm-ui-styling/scripts/.coverage +0 -0
  344. package/skills/uipm-ui-styling/scripts/requirements.txt +17 -0
  345. package/skills/uipm-ui-styling/scripts/shadcn_add.py +292 -0
  346. package/skills/uipm-ui-styling/scripts/tailwind_config_gen.py +456 -0
  347. package/skills/uipm-ui-styling/scripts/tests/coverage-ui.json +1 -0
  348. package/skills/uipm-ui-styling/scripts/tests/requirements.txt +3 -0
  349. package/skills/uipm-ui-styling/scripts/tests/test_shadcn_add.py +266 -0
  350. package/skills/uipm-ui-styling/scripts/tests/test_tailwind_config_gen.py +336 -0
@@ -0,0 +1,538 @@
1
+ """Deterministic symbol indexing and conservative cross-file resolution helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ import re
7
+ import unicodedata
8
+ from dataclasses import dataclass
9
+ from pathlib import Path
10
+ from collections.abc import Sequence
11
+ from typing import Any
12
+
13
+ from graphify.security import sanitize_metadata
14
+
15
+
16
+
17
+ @dataclass(frozen=True)
18
+ class ImportedSymbol:
19
+ """A Python imported name that can be used as deterministic resolution evidence."""
20
+
21
+ local_name: str
22
+ imported_name: str
23
+ module_stem: str
24
+ source_file: str
25
+ source_location: str
26
+
27
+
28
+ def normalise_callable_label(label: str) -> str:
29
+ """Normalize a node label into the key used for call resolution."""
30
+
31
+ return label.strip().strip("()").lstrip(".").lower()
32
+
33
+
34
+ def node_is_resolvable_symbol(node: dict[str, Any]) -> bool:
35
+ """Return True when a node is suitable for deterministic symbol lookup.
36
+
37
+ Requires ``file_type == "code"`` as the positive gate — only code-class
38
+ nodes participate as call targets. ``_EXCLUDED_FILE_TYPES`` is kept as
39
+ defensive-in-depth against legacy data, but the primary guard is the
40
+ positive code check. Document/paper/image/concept nodes (e.g. a Markdown
41
+ heading whose label happens to match a code identifier) MUST NOT become
42
+ callees for a raw code call.
43
+ """
44
+
45
+ if node.get("file_type") != "code":
46
+ return False
47
+ label = str(node.get("label", "")).strip()
48
+ if not label:
49
+ return False
50
+ if label.endswith((".py", ".js", ".ts", ".tsx", ".java", ".go", ".rs")):
51
+ return False
52
+ return bool(normalise_callable_label(label))
53
+
54
+
55
+ def build_label_index(nodes: list[dict[str, Any]]) -> dict[str, list[str]]:
56
+ """Build label -> node id list for conservative cross-file resolution."""
57
+
58
+ index: dict[str, list[str]] = {}
59
+ for node in nodes:
60
+ if not node_is_resolvable_symbol(node):
61
+ continue
62
+ node_id = node.get("id")
63
+ if not node_id:
64
+ continue
65
+ key = normalise_callable_label(str(node.get("label", "")))
66
+ if not key:
67
+ continue
68
+ index.setdefault(key, []).append(str(node_id))
69
+ return index
70
+
71
+
72
+ def existing_edge_pairs(edges: list[dict[str, Any]]) -> set[tuple[str, str, str]]:
73
+ """Return all existing source/target/relation edge triples.
74
+
75
+ Includes relation so that a prior "contains" or "method" edge does not
76
+ suppress a semantically distinct "calls" edge between the same endpoints (#F5).
77
+ """
78
+
79
+ triples: set[tuple[str, str, str]] = set()
80
+ for edge in edges:
81
+ source = edge.get("source")
82
+ target = edge.get("target")
83
+ relation = edge.get("relation", "")
84
+ if source and target:
85
+ triples.add((str(source), str(target), str(relation)))
86
+ return triples
87
+
88
+
89
+ def iter_raw_calls(per_file: Sequence[object]) -> list[dict[str, Any]]:
90
+ """Return raw calls from all per-file extraction fragments.
91
+
92
+ Parameter is ``Sequence[object]`` (not ``Sequence[dict[str, Any] | None]``)
93
+ because external extraction output may contain arbitrary deserialized
94
+ JSON. Defensive against malformed fragments: non-dict per-file entries
95
+ are skipped, non-list ``raw_calls`` are treated as empty, and non-dict
96
+ items inside the list are silently dropped. The downstream resolvers
97
+ assume every returned item is a dict and they expect this guarantee.
98
+ """
99
+
100
+ calls: list[dict[str, Any]] = []
101
+ for result in per_file:
102
+ if not isinstance(result, dict):
103
+ continue
104
+ raw_calls = result.get("raw_calls", [])
105
+ if not isinstance(raw_calls, list):
106
+ continue
107
+ for raw_call in raw_calls:
108
+ if isinstance(raw_call, dict):
109
+ calls.append(raw_call)
110
+ return calls
111
+
112
+
113
+ def _module_stem(module_name: str | None) -> str:
114
+ """Return the final module component used to match Graphify source stems."""
115
+
116
+ if not module_name:
117
+ return ""
118
+ return module_name.strip(".").split(".")[-1]
119
+
120
+
121
+ def parse_python_import_aliases(path: Path) -> dict[str, ImportedSymbol]:
122
+ """Parse deterministic Python import aliases from one source file.
123
+
124
+ Supported forms:
125
+ from helper import transform
126
+ from helper import transform as tx
127
+ from .helper import transform
128
+
129
+ The function deliberately does not resolve plain ``import helper`` member
130
+ calls because current raw call records do not preserve the receiver name from
131
+ ``helper.transform()``. That can be added later only after raw call facts are
132
+ extended to include the receiver expression.
133
+ """
134
+
135
+ try:
136
+ source = path.read_text(encoding="utf-8", errors="replace")
137
+ tree = ast.parse(source)
138
+ except (OSError, SyntaxError):
139
+ return {}
140
+
141
+ aliases: dict[str, ImportedSymbol] = {}
142
+ source_file = str(path)
143
+
144
+ # Only top-level `from ... import ...` statements count as file-wide
145
+ # evidence. Nested/function-local imports do NOT — they're only valid
146
+ # inside their lexical scope, and our raw-call records don't currently
147
+ # carry enough scope info to match the import site safely. Walking
148
+ # ast.walk(tree) would incorrectly justify calls in other scopes.
149
+ for node in tree.body:
150
+ if not isinstance(node, ast.ImportFrom):
151
+ continue
152
+ module_stem = _module_stem(node.module)
153
+ if not module_stem:
154
+ continue
155
+ for alias in node.names:
156
+ if alias.name == "*":
157
+ continue
158
+ local_name = alias.asname or alias.name
159
+ aliases[local_name] = ImportedSymbol(
160
+ local_name=local_name,
161
+ imported_name=alias.name,
162
+ module_stem=module_stem,
163
+ source_file=source_file,
164
+ source_location=f"L{getattr(node, 'lineno', 1)}",
165
+ )
166
+
167
+ return aliases
168
+
169
+
170
+ def _node_source_stem(node: dict[str, Any]) -> str:
171
+ """Return the stem of a node's source file."""
172
+
173
+ source_file = str(node.get("source_file", ""))
174
+ if not source_file:
175
+ return ""
176
+ return Path(source_file).stem
177
+
178
+
179
+ def build_python_symbol_index(nodes: list[dict[str, Any]]) -> dict[tuple[str, str], list[str]]:
180
+ """Build ``(module_stem, normalized_symbol_name) -> node_ids``.
181
+
182
+ This index is stricter than the global label index. It uses both the module
183
+ stem and the symbol label, which allows import evidence to resolve calls that
184
+ global label uniqueness alone cannot safely resolve.
185
+ """
186
+
187
+ index: dict[tuple[str, str], list[str]] = {}
188
+ for node in nodes:
189
+ if not node_is_resolvable_symbol(node):
190
+ continue
191
+ source_stem = _node_source_stem(node)
192
+ if not source_stem:
193
+ continue
194
+ label = normalise_callable_label(str(node.get("label", "")))
195
+ if not label:
196
+ continue
197
+ node_id = node.get("id")
198
+ if not node_id:
199
+ continue
200
+ index.setdefault((source_stem, label), []).append(str(node_id))
201
+ return index
202
+
203
+
204
+ def find_unique_python_symbol(
205
+ symbol_index: dict[tuple[str, str], list[str]],
206
+ imported: ImportedSymbol,
207
+ ) -> str | None:
208
+ """Resolve one imported symbol to exactly one Graphify node id."""
209
+
210
+ candidates = symbol_index.get((imported.module_stem, imported.imported_name.lower()), [])
211
+ if len(candidates) == 1:
212
+ return candidates[0]
213
+ return None
214
+
215
+
216
+ def resolve_python_import_guided_calls(
217
+ per_file: Sequence[object],
218
+ paths: Sequence[Path],
219
+ all_nodes: list[dict[str, Any]],
220
+ all_edges: list[dict[str, Any]],
221
+ ) -> list[dict[str, Any]]:
222
+ """Resolve raw Python calls using explicit import evidence.
223
+
224
+ Only ``from module import symbol [as alias]`` forms are handled. Member calls
225
+ remain skipped because the current raw call fact does not carry receiver
226
+ information.
227
+
228
+ Parameter ``per_file`` is ``Sequence[object]`` because external extraction
229
+ output may contain arbitrary deserialized JSON. Non-dict slots are
230
+ treated as empty fragments, and indices past ``len(per_file)`` are also
231
+ treated as empty (paths longer than per_file is tolerated).
232
+ """
233
+
234
+ symbol_index = build_python_symbol_index(all_nodes)
235
+ known_pairs = existing_edge_pairs(all_edges)
236
+ # Build result_by_file defensively:
237
+ # - skip indices past the end of per_file (paths shorter than per_file
238
+ # also OK; the zip-like behavior is what callers expect)
239
+ # - non-dict per_file slots fall back to the empty fragment so the
240
+ # downstream `.get("raw_calls", [])` lookup never raises
241
+ result_by_file: dict[str, dict[str, Any]] = {}
242
+ for index, path in enumerate(paths):
243
+ if path.suffix != ".py":
244
+ continue
245
+ slot: Any = per_file[index] if index < len(per_file) else None
246
+ result_by_file[str(path)] = slot if isinstance(slot, dict) else {"nodes": [], "edges": []}
247
+ resolved_edges: list[dict[str, Any]] = []
248
+
249
+ for path in paths:
250
+ if path.suffix != ".py":
251
+ continue
252
+ source_file = str(path)
253
+ aliases = parse_python_import_aliases(path)
254
+ if not aliases:
255
+ continue
256
+ file_result = result_by_file.get(source_file, {"raw_calls": []})
257
+ raw_calls = file_result.get("raw_calls", [])
258
+ if not isinstance(raw_calls, list):
259
+ continue
260
+ for raw_call in raw_calls:
261
+ if not isinstance(raw_call, dict):
262
+ continue
263
+ if raw_call.get("is_member_call"):
264
+ continue
265
+ callee = str(raw_call.get("callee", "")).strip()
266
+ if not callee:
267
+ continue
268
+ imported = aliases.get(callee)
269
+ if imported is None:
270
+ continue
271
+ target = find_unique_python_symbol(symbol_index, imported)
272
+ if target is None:
273
+ continue
274
+ caller = str(raw_call.get("caller_nid", ""))
275
+ if not caller or caller == target:
276
+ continue
277
+ pair = (caller, target, "calls")
278
+ if pair in known_pairs:
279
+ continue
280
+ known_pairs.add(pair)
281
+ resolved_edges.append(
282
+ {
283
+ "source": caller,
284
+ "target": target,
285
+ "relation": "calls",
286
+ "context": "import_guided_call",
287
+ "confidence": "EXTRACTED",
288
+ "confidence_score": 1.0,
289
+ "source_file": raw_call.get("source_file", source_file),
290
+ "source_location": raw_call.get("source_location") or imported.source_location,
291
+ "weight": 1.0,
292
+ "metadata": sanitize_metadata({
293
+ "resolver": "python_import_guided",
294
+ "local_name": imported.local_name,
295
+ "imported_name": imported.imported_name,
296
+ "module_stem": imported.module_stem,
297
+ "import_source_location": imported.source_location,
298
+ }),
299
+ }
300
+ )
301
+
302
+ return resolved_edges
303
+
304
+
305
+ def resolve_cross_file_raw_calls(
306
+ per_file: Sequence[dict[str, Any] | None],
307
+ all_nodes: list[dict[str, Any]],
308
+ all_edges: list[dict[str, Any]],
309
+ ) -> list[dict[str, Any]]:
310
+ """Resolve unqualified raw calls conservatively after all files are known.
311
+
312
+ This intentionally preserves Graphify's existing behavior:
313
+ - member calls are skipped;
314
+ - ambiguous labels are skipped;
315
+ - only a single unique candidate is emitted;
316
+ - emitted edges are INFERRED because the raw call alone is not import proof.
317
+ """
318
+
319
+ label_index = build_label_index(all_nodes)
320
+ known_pairs = existing_edge_pairs(all_edges)
321
+ resolved: list[dict[str, Any]] = []
322
+
323
+ for raw_call in iter_raw_calls(per_file):
324
+ callee = str(raw_call.get("callee", "")).strip()
325
+ if not callee:
326
+ continue
327
+ if raw_call.get("is_member_call"):
328
+ continue
329
+ candidates = label_index.get(callee.lower(), [])
330
+ if len(candidates) != 1:
331
+ continue
332
+ target = candidates[0]
333
+ caller = str(raw_call.get("caller_nid", ""))
334
+ if not caller:
335
+ continue
336
+ if target == caller:
337
+ continue
338
+ pair = (caller, target, "calls")
339
+ if pair in known_pairs:
340
+ continue
341
+ known_pairs.add(pair)
342
+ resolved.append(
343
+ {
344
+ "source": caller,
345
+ "target": target,
346
+ "relation": "calls",
347
+ "context": "call",
348
+ "confidence": "INFERRED",
349
+ "confidence_score": 0.8,
350
+ "source_file": raw_call.get("source_file", ""),
351
+ "source_location": raw_call.get("source_location"),
352
+ "weight": 1.0,
353
+ }
354
+ )
355
+
356
+ return resolved
357
+
358
+
359
+ def _bash_make_id(*parts: str) -> str:
360
+ """Exact copy of extract._make_id — kept here to avoid an import cycle."""
361
+ combined = "_".join(p.strip("_.") for p in parts if p)
362
+ combined = unicodedata.normalize("NFKC", combined)
363
+ cleaned = re.sub(r"[^\w]+", "_", combined, flags=re.UNICODE)
364
+ cleaned = re.sub(r"_+", "_", cleaned)
365
+ return cleaned.strip("_").casefold()
366
+
367
+
368
+ def _bash_file_stem(rel_path: Path) -> str:
369
+ """Exact copy of extract._file_stem — kept here to avoid an import cycle."""
370
+ parent = rel_path.parent.name
371
+ if parent and parent not in (".", ""):
372
+ return f"{parent}.{rel_path.stem}"
373
+ return rel_path.stem
374
+
375
+
376
+ def _file_node_id_for_path(path: Path, root: Path) -> str:
377
+ # Produce the canonical {parent_dir}_{stem} file-node ID that extract()'s
378
+ # id_remap generates (#1033), so bash `source` edges land on the real file
379
+ # node instead of an orphan. _bash_make_id / _bash_file_stem are exact copies
380
+ # of extract._make_id / extract._file_stem, so IDs match.
381
+ try:
382
+ rel = path.resolve().relative_to(root.resolve())
383
+ except ValueError:
384
+ return _bash_make_id(str(path)) # path outside root: hash absolute path as fallback
385
+ return _bash_make_id(_bash_file_stem(rel))
386
+
387
+
388
+ def resolve_bash_source_edges(
389
+ per_file: Sequence[dict | None],
390
+ paths: Sequence[Path],
391
+ root: Path,
392
+ existing_edges: list[dict] | None = None,
393
+ ) -> list[dict]:
394
+ """Resolve Bash source/import edges and source-backed function calls.
395
+
396
+ Defensive against malformed extraction fragments: non-dict ``per_file``
397
+ entries, missing ``bash_sources``/``raw_calls`` keys, non-dict items in
398
+ those lists, and missing/empty ``id`` / ``target_path`` / ``caller_nid``
399
+ fields all yield silent skips rather than ``KeyError``.
400
+
401
+ ``bash_sources[].target_path`` contract (Graphify static-analysis policy):
402
+ - Absolute paths: resolved as-is.
403
+ - Relative paths: resolved against the *source file's* directory
404
+ (i.e. ``Path(path).parent / target_path``).
405
+ NOTE: this is a deterministic static-analysis policy chosen by
406
+ Graphify, NOT bash runtime semantics. At runtime, ``source ./X``
407
+ is resolved against the shell's current working directory. We
408
+ prefer source-file-relative because static analysis cannot know
409
+ the future CWD; resolving against the file being analyzed gives
410
+ deterministic, reproducible edges across runs.
411
+ - Inputs of type ``str`` and ``pathlib.Path`` are processed.
412
+ Anything else is silently skipped.
413
+ """
414
+ path_by_index = [Path(p).resolve() for p in paths]
415
+ file_nid_by_path = {p: _file_node_id_for_path(p, root) for p in path_by_index} # resolved paths only
416
+
417
+ functions_by_file: dict[str, dict[str, str]] = {}
418
+ for result, path in zip(per_file, path_by_index):
419
+ if not isinstance(result, dict):
420
+ continue
421
+ file_nid = file_nid_by_path[path]
422
+ nodes = result.get("nodes", [])
423
+ if not isinstance(nodes, list):
424
+ continue
425
+ for node in nodes:
426
+ if not isinstance(node, dict):
427
+ continue
428
+ metadata = node.get("metadata", {})
429
+ if not isinstance(metadata, dict):
430
+ continue
431
+ if metadata.get("kind") != "bash_function":
432
+ continue
433
+ name = str(node.get("label", "")).removesuffix("()").strip()
434
+ node_id = node.get("id")
435
+ if not name or not node_id:
436
+ continue
437
+ functions_by_file.setdefault(file_nid, {})[name] = str(node_id)
438
+
439
+ sourced_files: dict[str, set[str]] = {}
440
+ resolved_edges: list[dict] = []
441
+ existing = existing_edge_pairs(existing_edges or [])
442
+
443
+ for result, path in zip(per_file, path_by_index):
444
+ if not isinstance(result, dict):
445
+ continue
446
+ src_file_nid = file_nid_by_path[path]
447
+ bash_sources = result.get("bash_sources", [])
448
+ if not isinstance(bash_sources, list):
449
+ continue
450
+ for source in bash_sources:
451
+ if not isinstance(source, dict):
452
+ continue
453
+ raw_target = source.get("target_path")
454
+ if not isinstance(raw_target, (str, Path)) or not str(raw_target).strip():
455
+ continue
456
+ # Relative paths resolve against the source file's directory —
457
+ # Graphify static-analysis policy (NOT bash runtime semantics;
458
+ # at runtime `source ./X` is CWD-relative, but static analysis
459
+ # can't know the future CWD, so we resolve relative to the
460
+ # file being analyzed for deterministic, reproducible edges).
461
+ candidate = Path(raw_target)
462
+ if not candidate.is_absolute():
463
+ candidate = path.parent / candidate
464
+ try:
465
+ target_path = candidate.resolve()
466
+ except (OSError, RuntimeError):
467
+ continue
468
+ target_file_nid = file_nid_by_path.get(target_path)
469
+ if target_file_nid is None:
470
+ continue
471
+ sourced_files.setdefault(src_file_nid, set()).add(target_file_nid)
472
+ key = (src_file_nid, target_file_nid, "imports_from")
473
+ if key in existing:
474
+ continue
475
+ existing.add(key)
476
+ resolved_edges.append(
477
+ {
478
+ "source": src_file_nid,
479
+ "target": target_file_nid,
480
+ "relation": "imports_from",
481
+ "context": "import",
482
+ "confidence": "EXTRACTED",
483
+ "confidence_score": 1.0,
484
+ "source_file": source.get("source_file", str(path)),
485
+ "source_location": source.get("source_location", ""),
486
+ "weight": 1.0,
487
+ }
488
+ )
489
+
490
+ for result, path in zip(per_file, path_by_index):
491
+ if not isinstance(result, dict):
492
+ continue
493
+ caller_file_nid = file_nid_by_path[path]
494
+ imported_file_ids = sourced_files.get(caller_file_nid, set())
495
+ if not imported_file_ids:
496
+ continue
497
+ raw_calls = result.get("raw_calls", [])
498
+ if not isinstance(raw_calls, list):
499
+ continue
500
+ for raw_call in raw_calls:
501
+ if not isinstance(raw_call, dict):
502
+ continue
503
+ if raw_call.get("language") != "bash":
504
+ continue
505
+ callee = raw_call.get("callee")
506
+ caller_nid = raw_call.get("caller_nid")
507
+ # callee must be a non-empty string — anything else (list, dict,
508
+ # int, None, …) is silently skipped to avoid TypeError on the
509
+ # `in functions_by_file[...]` membership check below.
510
+ if not isinstance(callee, str) or not callee or not caller_nid:
511
+ continue
512
+ matches = [
513
+ functions_by_file[file_nid][callee]
514
+ for file_nid in imported_file_ids
515
+ if callee in functions_by_file.get(file_nid, {})
516
+ ]
517
+ if len(matches) != 1:
518
+ continue
519
+ target = matches[0]
520
+ key = (str(caller_nid), target, "calls")
521
+ if key in existing:
522
+ continue
523
+ existing.add(key)
524
+ resolved_edges.append(
525
+ {
526
+ "source": str(caller_nid),
527
+ "target": target,
528
+ "relation": "calls",
529
+ "context": "call",
530
+ "confidence": "EXTRACTED",
531
+ "confidence_score": 1.0,
532
+ "source_file": raw_call.get("source_file", str(path)),
533
+ "source_location": raw_call.get("source_location", ""),
534
+ "weight": 1.0,
535
+ }
536
+ )
537
+
538
+ return resolved_edges