@kortix/sandbox 0.4.1

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 (246) hide show
  1. package/config/customize.sh +143 -0
  2. package/config/kortix-env-setup.sh +25 -0
  3. package/kortix-master/package.json +22 -0
  4. package/kortix-master/src/config.ts +22 -0
  5. package/kortix-master/src/index.ts +44 -0
  6. package/kortix-master/src/routes/env.ts +65 -0
  7. package/kortix-master/src/routes/proxy.ts +108 -0
  8. package/kortix-master/src/routes/update.ts +185 -0
  9. package/kortix-master/src/services/proxy.ts +43 -0
  10. package/kortix-master/src/services/secret-store.ts +156 -0
  11. package/kortix-master/tsconfig.json +14 -0
  12. package/opencode/agents/kortix-browser.md +142 -0
  13. package/opencode/agents/kortix-build.md +62 -0
  14. package/opencode/agents/kortix-explore.md +66 -0
  15. package/opencode/agents/kortix-image-gen.md +33 -0
  16. package/opencode/agents/kortix-main.md +450 -0
  17. package/opencode/agents/kortix-plan.md +100 -0
  18. package/opencode/agents/kortix-research.md +84 -0
  19. package/opencode/agents/kortix-sheets.md +61 -0
  20. package/opencode/agents/kortix-slides.md +64 -0
  21. package/opencode/agents/kortix-web-dev.md +572 -0
  22. package/opencode/commands/email.md +36 -0
  23. package/opencode/commands/init.md +43 -0
  24. package/opencode/commands/journal.md +44 -0
  25. package/opencode/commands/memory-init.md +81 -0
  26. package/opencode/commands/memory-search.md +50 -0
  27. package/opencode/commands/memory-status.md +56 -0
  28. package/opencode/commands/research.md +36 -0
  29. package/opencode/commands/search.md +38 -0
  30. package/opencode/commands/slides.md +32 -0
  31. package/opencode/commands/spreadsheet.md +30 -0
  32. package/opencode/memory.json +37 -0
  33. package/opencode/ocx.jsonc +10 -0
  34. package/opencode/opencode.jsonc +103 -0
  35. package/opencode/package.json +25 -0
  36. package/opencode/patches/apply.sh +19 -0
  37. package/opencode/patches/opencode-pty-spawn.txt +49 -0
  38. package/opencode/plugin/background-agents.ts.disabled +483 -0
  39. package/opencode/plugin/kdco-primitives/get-project-id.ts +172 -0
  40. package/opencode/plugin/kdco-primitives/index.ts +26 -0
  41. package/opencode/plugin/kdco-primitives/log-warn.ts +51 -0
  42. package/opencode/plugin/kdco-primitives/mutex.ts +122 -0
  43. package/opencode/plugin/kdco-primitives/shell.ts +138 -0
  44. package/opencode/plugin/kdco-primitives/temp.ts +36 -0
  45. package/opencode/plugin/kdco-primitives/terminal-detect.ts +34 -0
  46. package/opencode/plugin/kdco-primitives/types.ts +13 -0
  47. package/opencode/plugin/kdco-primitives/with-timeout.ts +84 -0
  48. package/opencode/plugin/memory.ts +306 -0
  49. package/opencode/plugin/worktree/state.ts +412 -0
  50. package/opencode/plugin/worktree/terminal.ts +1002 -0
  51. package/opencode/plugin/worktree.ts +861 -0
  52. package/opencode/skills/KORTIX-browser/SKILL.md +478 -0
  53. package/opencode/skills/KORTIX-cron-triggers/SKILL.md +173 -0
  54. package/opencode/skills/KORTIX-deep-research/SKILL.md +278 -0
  55. package/opencode/skills/KORTIX-docx/SKILL.md +398 -0
  56. package/opencode/skills/KORTIX-docx/scripts/__init__.py +1 -0
  57. package/opencode/skills/KORTIX-docx/scripts/accept_changes.py +104 -0
  58. package/opencode/skills/KORTIX-docx/scripts/comment.py +244 -0
  59. package/opencode/skills/KORTIX-docx/scripts/office/helpers/__init__.py +0 -0
  60. package/opencode/skills/KORTIX-docx/scripts/office/helpers/merge_runs.py +199 -0
  61. package/opencode/skills/KORTIX-docx/scripts/office/helpers/simplify_redlines.py +197 -0
  62. package/opencode/skills/KORTIX-docx/scripts/office/pack.py +159 -0
  63. package/opencode/skills/KORTIX-docx/scripts/office/soffice.py +183 -0
  64. package/opencode/skills/KORTIX-docx/scripts/office/unpack.py +132 -0
  65. package/opencode/skills/KORTIX-docx/scripts/office/validate.py +111 -0
  66. package/opencode/skills/KORTIX-docx/scripts/office/validators/__init__.py +15 -0
  67. package/opencode/skills/KORTIX-docx/scripts/office/validators/base.py +847 -0
  68. package/opencode/skills/KORTIX-docx/scripts/office/validators/docx.py +446 -0
  69. package/opencode/skills/KORTIX-docx/scripts/office/validators/pptx.py +275 -0
  70. package/opencode/skills/KORTIX-docx/scripts/office/validators/redlining.py +247 -0
  71. package/opencode/skills/KORTIX-docx/scripts/render_docx.py +179 -0
  72. package/opencode/skills/KORTIX-docx/scripts/templates/comments.xml +3 -0
  73. package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtended.xml +3 -0
  74. package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtensible.xml +3 -0
  75. package/opencode/skills/KORTIX-docx/scripts/templates/commentsIds.xml +3 -0
  76. package/opencode/skills/KORTIX-docx/scripts/templates/people.xml +3 -0
  77. package/opencode/skills/KORTIX-domain-research/SKILL.md +96 -0
  78. package/opencode/skills/KORTIX-domain-research/scripts/domain-lookup.py +810 -0
  79. package/opencode/skills/KORTIX-elevenlabs/SKILL.md +230 -0
  80. package/opencode/skills/KORTIX-elevenlabs/scripts/tts.py +389 -0
  81. package/opencode/skills/KORTIX-email/SKILL.md +145 -0
  82. package/opencode/skills/KORTIX-legal-writer/SKILL.md +409 -0
  83. package/opencode/skills/KORTIX-legal-writer/references/bluebook.md +152 -0
  84. package/opencode/skills/KORTIX-legal-writer/references/document-types.md +416 -0
  85. package/opencode/skills/KORTIX-legal-writer/scripts/courtlistener.py +291 -0
  86. package/opencode/skills/KORTIX-legal-writer/scripts/ecfr_lookup.py +299 -0
  87. package/opencode/skills/KORTIX-legal-writer/scripts/verify-legal.py +507 -0
  88. package/opencode/skills/KORTIX-logo-creator/SKILL.md +293 -0
  89. package/opencode/skills/KORTIX-logo-creator/references/prompt-patterns.md +134 -0
  90. package/opencode/skills/KORTIX-logo-creator/scripts/compose_logo.py +406 -0
  91. package/opencode/skills/KORTIX-logo-creator/scripts/create_logo_sheet.py +258 -0
  92. package/opencode/skills/KORTIX-logo-creator/scripts/remove_bg.py +96 -0
  93. package/opencode/skills/KORTIX-memory/SKILL.md +261 -0
  94. package/opencode/skills/KORTIX-memory/scripts/export-sessions.py +409 -0
  95. package/opencode/skills/KORTIX-paper-creator/SKILL.md +549 -0
  96. package/opencode/skills/KORTIX-paper-creator/assets/template.tex +101 -0
  97. package/opencode/skills/KORTIX-paper-creator/scripts/compile.sh +177 -0
  98. package/opencode/skills/KORTIX-paper-creator/scripts/openalex_to_bibtex.py +220 -0
  99. package/opencode/skills/KORTIX-paper-creator/scripts/verify.sh +354 -0
  100. package/opencode/skills/KORTIX-paper-search/SKILL.md +418 -0
  101. package/opencode/skills/KORTIX-pdf/SKILL.md +232 -0
  102. package/opencode/skills/KORTIX-pdf/forms.md +36 -0
  103. package/opencode/skills/KORTIX-pdf/reference.md +105 -0
  104. package/opencode/skills/KORTIX-pdf/scripts/check_bounding_boxes.py +65 -0
  105. package/opencode/skills/KORTIX-pdf/scripts/check_fillable_fields.py +11 -0
  106. package/opencode/skills/KORTIX-pdf/scripts/convert_pdf_to_images.py +33 -0
  107. package/opencode/skills/KORTIX-pdf/scripts/create_validation_image.py +37 -0
  108. package/opencode/skills/KORTIX-pdf/scripts/extract_form_field_info.py +122 -0
  109. package/opencode/skills/KORTIX-pdf/scripts/extract_form_structure.py +115 -0
  110. package/opencode/skills/KORTIX-pdf/scripts/fill_fillable_fields.py +98 -0
  111. package/opencode/skills/KORTIX-pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  112. package/opencode/skills/KORTIX-plan/SKILL.md +228 -0
  113. package/opencode/skills/KORTIX-presentation-viewer/SKILL.md +87 -0
  114. package/opencode/skills/KORTIX-presentation-viewer/serve.ts +136 -0
  115. package/opencode/skills/KORTIX-presentation-viewer/viewer.html +559 -0
  116. package/opencode/skills/KORTIX-presentations/SKILL.md +344 -0
  117. package/opencode/skills/KORTIX-remotion/SKILL.md +56 -0
  118. package/opencode/skills/KORTIX-remotion/rules/3d.md +86 -0
  119. package/opencode/skills/KORTIX-remotion/rules/animations.md +29 -0
  120. package/opencode/skills/KORTIX-remotion/rules/assets.md +78 -0
  121. package/opencode/skills/KORTIX-remotion/rules/audio-visualization.md +198 -0
  122. package/opencode/skills/KORTIX-remotion/rules/audio.md +169 -0
  123. package/opencode/skills/KORTIX-remotion/rules/calculate-metadata.md +104 -0
  124. package/opencode/skills/KORTIX-remotion/rules/can-decode.md +75 -0
  125. package/opencode/skills/KORTIX-remotion/rules/charts.md +120 -0
  126. package/opencode/skills/KORTIX-remotion/rules/compositions.md +141 -0
  127. package/opencode/skills/KORTIX-remotion/rules/display-captions.md +184 -0
  128. package/opencode/skills/KORTIX-remotion/rules/extract-frames.md +229 -0
  129. package/opencode/skills/KORTIX-remotion/rules/ffmpeg.md +38 -0
  130. package/opencode/skills/KORTIX-remotion/rules/fonts.md +152 -0
  131. package/opencode/skills/KORTIX-remotion/rules/get-audio-duration.md +58 -0
  132. package/opencode/skills/KORTIX-remotion/rules/get-video-dimensions.md +68 -0
  133. package/opencode/skills/KORTIX-remotion/rules/get-video-duration.md +58 -0
  134. package/opencode/skills/KORTIX-remotion/rules/gifs.md +141 -0
  135. package/opencode/skills/KORTIX-remotion/rules/images.md +130 -0
  136. package/opencode/skills/KORTIX-remotion/rules/import-srt-captions.md +69 -0
  137. package/opencode/skills/KORTIX-remotion/rules/light-leaks.md +73 -0
  138. package/opencode/skills/KORTIX-remotion/rules/lottie.md +68 -0
  139. package/opencode/skills/KORTIX-remotion/rules/maps.md +401 -0
  140. package/opencode/skills/KORTIX-remotion/rules/measuring-dom-nodes.md +35 -0
  141. package/opencode/skills/KORTIX-remotion/rules/measuring-text.md +143 -0
  142. package/opencode/skills/KORTIX-remotion/rules/parameters.md +98 -0
  143. package/opencode/skills/KORTIX-remotion/rules/sequencing.md +118 -0
  144. package/opencode/skills/KORTIX-remotion/rules/subtitles.md +36 -0
  145. package/opencode/skills/KORTIX-remotion/rules/tailwind.md +11 -0
  146. package/opencode/skills/KORTIX-remotion/rules/text-animations.md +20 -0
  147. package/opencode/skills/KORTIX-remotion/rules/timing.md +179 -0
  148. package/opencode/skills/KORTIX-remotion/rules/transcribe-captions.md +70 -0
  149. package/opencode/skills/KORTIX-remotion/rules/transitions.md +197 -0
  150. package/opencode/skills/KORTIX-remotion/rules/transparent-videos.md +106 -0
  151. package/opencode/skills/KORTIX-remotion/rules/trimming.md +53 -0
  152. package/opencode/skills/KORTIX-remotion/rules/videos.md +171 -0
  153. package/opencode/skills/KORTIX-secrets/SKILL.md +280 -0
  154. package/opencode/skills/KORTIX-semantic-search/SKILL.md +213 -0
  155. package/opencode/skills/KORTIX-session-search/SKILL.md +807 -0
  156. package/opencode/skills/KORTIX-session-search/Untitled +1 -0
  157. package/opencode/skills/KORTIX-skill-creator/SKILL.md +163 -0
  158. package/opencode/skills/KORTIX-web-research/SKILL.md +69 -0
  159. package/opencode/skills/KORTIX-xlsx/LICENSE.txt +30 -0
  160. package/opencode/skills/KORTIX-xlsx/SKILL.md +549 -0
  161. package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/__init__.py +0 -0
  162. package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/merge_runs.py +199 -0
  163. package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  164. package/opencode/skills/KORTIX-xlsx/scripts/office/pack.py +159 -0
  165. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  166. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  167. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  168. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  169. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  170. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  171. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  172. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  173. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  174. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  175. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  176. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  177. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  178. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  179. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  180. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  181. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  182. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  183. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  184. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  185. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  186. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  187. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  188. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  189. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  190. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  191. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  192. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  193. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  194. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  195. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  196. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  197. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  198. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  199. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  200. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  201. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  202. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  203. package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  204. package/opencode/skills/KORTIX-xlsx/scripts/office/soffice.py +183 -0
  205. package/opencode/skills/KORTIX-xlsx/scripts/office/unpack.py +132 -0
  206. package/opencode/skills/KORTIX-xlsx/scripts/office/validate.py +111 -0
  207. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/__init__.py +15 -0
  208. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/base.py +847 -0
  209. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/docx.py +446 -0
  210. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/pptx.py +275 -0
  211. package/opencode/skills/KORTIX-xlsx/scripts/office/validators/redlining.py +247 -0
  212. package/opencode/skills/KORTIX-xlsx/scripts/recalc.py +184 -0
  213. package/opencode/tools/image-gen.ts +342 -0
  214. package/opencode/tools/image-search.ts +190 -0
  215. package/opencode/tools/memory-get.ts +168 -0
  216. package/opencode/tools/memory-search.ts +247 -0
  217. package/opencode/tools/presentation-gen.ts +723 -0
  218. package/opencode/tools/scrape-webpage.ts +115 -0
  219. package/opencode/tools/scripts/.python-version +1 -0
  220. package/opencode/tools/scripts/convert_pdf.py +184 -0
  221. package/opencode/tools/scripts/convert_pptx.py +562 -0
  222. package/opencode/tools/scripts/pyproject.toml +11 -0
  223. package/opencode/tools/scripts/uv.lock +287 -0
  224. package/opencode/tools/scripts/validate_slide.py +74 -0
  225. package/opencode/tools/show-user.ts +217 -0
  226. package/opencode/tools/tests/e2e-presentation-fix.ts +277 -0
  227. package/opencode/tools/tests/image-gen.test.ts +215 -0
  228. package/opencode/tools/tests/image-search.test.ts +125 -0
  229. package/opencode/tools/tests/memory-system-benchmark.ts +1076 -0
  230. package/opencode/tools/tests/presentation-gen.test.ts +389 -0
  231. package/opencode/tools/tests/scrape-webpage.test.ts +74 -0
  232. package/opencode/tools/tests/show-user.test.ts +241 -0
  233. package/opencode/tools/tests/video-gen.test.ts +110 -0
  234. package/opencode/tools/tests/web-search.test.ts +106 -0
  235. package/opencode/tools/video-gen.ts +200 -0
  236. package/opencode/tools/web-search.ts +153 -0
  237. package/opencode/tsconfig.json +29 -0
  238. package/package.json +36 -0
  239. package/patch-agent-browser.js +100 -0
  240. package/postinstall.sh +88 -0
  241. package/services/KORTIX-presentation-viewer/run +37 -0
  242. package/services/agent-browser-viewer/run +48 -0
  243. package/services/kortix-master/run +16 -0
  244. package/services/lss-sync/run +22 -0
  245. package/services/opencode-serve/run +25 -0
  246. package/services/opencode-web/run +21 -0
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env bash
2
+ # LaTeX compilation pipeline with error recovery.
3
+ #
4
+ # Usage:
5
+ # bash compile.sh <path-to-main.tex>
6
+ # bash compile.sh paper/my-paper/main.tex
7
+ # bash compile.sh paper/my-paper/main.tex --clean # remove aux files after
8
+ #
9
+ # Runs: pdflatex -> bibtex -> pdflatex -> pdflatex
10
+ # Exit codes: 0 = success, 1 = error
11
+
12
+ set -euo pipefail
13
+
14
+ # --- Args ---
15
+ if [ $# -lt 1 ]; then
16
+ echo "Usage: compile.sh <path-to-main.tex> [--clean]" >&2
17
+ exit 1
18
+ fi
19
+
20
+ TEX_FILE="$1"
21
+ CLEAN=false
22
+ if [[ "${2:-}" == "--clean" ]]; then
23
+ CLEAN=true
24
+ fi
25
+
26
+ if [ ! -f "$TEX_FILE" ]; then
27
+ echo "ERROR: File not found: $TEX_FILE" >&2
28
+ exit 1
29
+ fi
30
+
31
+ # --- Paths ---
32
+ TEX_DIR="$(cd "$(dirname "$TEX_FILE")" && pwd)"
33
+ TEX_NAME="$(basename "$TEX_FILE" .tex)"
34
+ BUILD_DIR="$TEX_DIR/build"
35
+ mkdir -p "$BUILD_DIR"
36
+
37
+ # --- Helper: run pdflatex ---
38
+ run_pdflatex() {
39
+ local pass_name="$1"
40
+ echo "=== pdflatex pass: $pass_name ==="
41
+ # TEXINPUTS adds TEX_DIR as a search path so \input{sections/...} resolves.
42
+ # Trailing colon means "append default search paths".
43
+ if TEXINPUTS="$TEX_DIR:" pdflatex \
44
+ -interaction=nonstopmode \
45
+ -halt-on-error \
46
+ -output-directory="$BUILD_DIR" \
47
+ "$TEX_FILE" > "$BUILD_DIR/pdflatex_${pass_name}.stdout" 2>&1; then
48
+ echo " OK"
49
+ return 0
50
+ else
51
+ echo " FAILED (exit $?)" >&2
52
+ # Extract error lines from log
53
+ local LOG="$BUILD_DIR/${TEX_NAME}.log"
54
+ if [ -f "$LOG" ]; then
55
+ echo "--- Errors from $LOG ---" >&2
56
+ grep -n -A2 "^!" "$LOG" | head -40 >&2
57
+ echo "---" >&2
58
+ fi
59
+ return 1
60
+ fi
61
+ }
62
+
63
+ # --- Helper: run bibtex ---
64
+ run_bibtex() {
65
+ echo "=== bibtex ==="
66
+ # bibtex needs to run in the build dir where the .aux file is
67
+ pushd "$BUILD_DIR" > /dev/null
68
+ if bibtex "$TEX_NAME" > bibtex.stdout 2>&1; then
69
+ echo " OK"
70
+ popd > /dev/null
71
+ return 0
72
+ else
73
+ local exit_code=$?
74
+ # bibtex exit code 1 = warnings (ok), 2 = errors
75
+ if [ $exit_code -le 1 ]; then
76
+ echo " OK (with warnings)"
77
+ popd > /dev/null
78
+ return 0
79
+ else
80
+ echo " FAILED (exit $exit_code)" >&2
81
+ if [ -f "$TEX_NAME.blg" ]; then
82
+ echo "--- Errors from ${TEX_NAME}.blg ---" >&2
83
+ grep -i "error" "$TEX_NAME.blg" | head -20 >&2
84
+ echo "---" >&2
85
+ fi
86
+ popd > /dev/null
87
+ return 1
88
+ fi
89
+ fi
90
+ }
91
+
92
+ # --- Copy .bib file to build dir so bibtex can find it ---
93
+ if [ -f "$TEX_DIR/references.bib" ]; then
94
+ cp "$TEX_DIR/references.bib" "$BUILD_DIR/"
95
+ fi
96
+
97
+ # --- Copy figures dir symlink ---
98
+ if [ -d "$TEX_DIR/figures" ] && [ ! -e "$BUILD_DIR/figures" ]; then
99
+ ln -s "$TEX_DIR/figures" "$BUILD_DIR/figures"
100
+ fi
101
+
102
+ # --- Copy sections dir symlink ---
103
+ if [ -d "$TEX_DIR/sections" ] && [ ! -e "$BUILD_DIR/sections" ]; then
104
+ ln -s "$TEX_DIR/sections" "$BUILD_DIR/sections"
105
+ fi
106
+
107
+ # --- Compilation Pipeline ---
108
+ echo "Compiling: $TEX_FILE"
109
+ echo "Build dir: $BUILD_DIR"
110
+ echo ""
111
+
112
+ # Pass 1: initial compilation (generates .aux with citation keys)
113
+ if ! run_pdflatex "1-initial"; then
114
+ echo "COMPILE FAILED on pass 1." >&2
115
+ exit 1
116
+ fi
117
+
118
+ # BibTeX: resolve citations (only if there are \citation commands in .aux)
119
+ if [ -f "$BUILD_DIR/references.bib" ] && grep -q '\\citation{' "$BUILD_DIR/${TEX_NAME}.aux" 2>/dev/null; then
120
+ if ! run_bibtex; then
121
+ echo "WARNING: bibtex had errors, continuing anyway..." >&2
122
+ fi
123
+ elif [ -f "$BUILD_DIR/references.bib" ]; then
124
+ echo "=== bibtex: skipped (no \\citation commands in .aux yet) ==="
125
+ else
126
+ echo "=== bibtex: skipped (no references.bib) ==="
127
+ fi
128
+
129
+ # Pass 2: incorporate bibliography
130
+ if ! run_pdflatex "2-bib"; then
131
+ echo "COMPILE FAILED on pass 2." >&2
132
+ exit 1
133
+ fi
134
+
135
+ # Pass 3: resolve cross-references
136
+ if ! run_pdflatex "3-final"; then
137
+ echo "COMPILE FAILED on pass 3." >&2
138
+ exit 1
139
+ fi
140
+
141
+ # --- Check for warnings ---
142
+ LOG="$BUILD_DIR/${TEX_NAME}.log"
143
+ if [ -f "$LOG" ]; then
144
+ # Match both standard LaTeX and package (natbib, cleveref, etc.) warnings
145
+ UNDEF_REFS=$(grep -c "Warning: Reference .* undefined" "$LOG" 2>/dev/null || true)
146
+ UNDEF_CITES=$(grep "Warning: Citation .* undefined" "$LOG" 2>/dev/null | grep -vc "There were" 2>/dev/null || true)
147
+ OVERFULL=$(grep -c "Overfull" "$LOG" 2>/dev/null || true)
148
+ UNDERFULL=$(grep -c "Underfull" "$LOG" 2>/dev/null || true)
149
+
150
+ echo ""
151
+ echo "=== Compilation Summary ==="
152
+ echo " PDF: $BUILD_DIR/${TEX_NAME}.pdf"
153
+ echo " Undefined references: $UNDEF_REFS"
154
+ echo " Undefined citations: $UNDEF_CITES"
155
+ echo " Overfull boxes: $OVERFULL"
156
+ echo " Underfull boxes: $UNDERFULL"
157
+
158
+ if [ "$UNDEF_REFS" -gt 0 ] || [ "$UNDEF_CITES" -gt 0 ]; then
159
+ echo ""
160
+ echo " WARNING: Unresolved references/citations detected."
161
+ grep "Warning: \(Reference\|Citation\)" "$LOG" | grep -v "There were" | head -20
162
+ fi
163
+ fi
164
+
165
+ # --- Clean aux files if requested ---
166
+ if $CLEAN; then
167
+ echo ""
168
+ echo "Cleaning aux files..."
169
+ rm -f "$BUILD_DIR"/*.aux "$BUILD_DIR"/*.log "$BUILD_DIR"/*.bbl \
170
+ "$BUILD_DIR"/*.blg "$BUILD_DIR"/*.out "$BUILD_DIR"/*.toc \
171
+ "$BUILD_DIR"/*.lof "$BUILD_DIR"/*.lot "$BUILD_DIR"/*.stdout
172
+ echo " Done. Only PDF remains."
173
+ fi
174
+
175
+ echo ""
176
+ echo "BUILD SUCCESSFUL: $BUILD_DIR/${TEX_NAME}.pdf"
177
+ exit 0
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env python3
2
+ """Convert OpenAlex API JSON response to BibTeX entries.
3
+
4
+ Usage:
5
+ # Pipe from curl
6
+ curl -s "https://api.openalex.org/works?search=topic&per_page=10&mailto=agent@kortix.ai" | python3 openalex_to_bibtex.py
7
+
8
+ # From file
9
+ python3 openalex_to_bibtex.py < results.json
10
+
11
+ # Single work object (not a search response)
12
+ python3 openalex_to_bibtex.py --single < work.json
13
+
14
+ # Append to existing .bib file
15
+ curl -s "..." | python3 openalex_to_bibtex.py >> references.bib
16
+
17
+ Output: BibTeX entries to stdout, one per work. Diagnostics to stderr.
18
+ """
19
+
20
+ import json
21
+ import sys
22
+ import re
23
+ import unicodedata
24
+
25
+ def normalize_ascii(text):
26
+ """Normalize unicode to ASCII-safe LaTeX-compatible text."""
27
+ if not text:
28
+ return ""
29
+ # NFKD decomposition, strip combining marks
30
+ text = unicodedata.normalize("NFKD", text)
31
+ text = "".join(c for c in text if not unicodedata.combining(c))
32
+ # Replace common special chars
33
+ replacements = {
34
+ "\u2013": "--", "\u2014": "---", "\u2018": "`", "\u2019": "'",
35
+ "\u201c": "``", "\u201d": "''", "\u00e9": "\\'e", "\u00e8": "\\`e",
36
+ "\u00f6": '\\"o', "\u00fc": '\\"u', "\u00e4": '\\"a', "\u00f1": "\\~n",
37
+ "\u00e7": "\\c{c}", "&": "\\&", "%": "\\%", "#": "\\#", "_": "\\_",
38
+ }
39
+ for k, v in replacements.items():
40
+ text = text.replace(k, v)
41
+ return text
42
+
43
+ def make_cite_key(work):
44
+ """Generate a citation key: firstauthorlastname + year + first_content_word."""
45
+ year = work.get("publication_year", "XXXX") or "XXXX"
46
+ # Get first author's last name
47
+ authorships = work.get("authorships", [])
48
+ if authorships:
49
+ name = authorships[0].get("author", {}).get("display_name", "unknown")
50
+ # Take last word as surname (handles "First Last" and "First M. Last")
51
+ lastname = name.strip().split()[-1].lower() if name else "unknown"
52
+ else:
53
+ lastname = "unknown"
54
+ # Clean surname to alphanumeric
55
+ lastname = re.sub(r"[^a-z]", "", lastname)
56
+ # Get first meaningful word from title
57
+ title = work.get("display_name", "") or ""
58
+ stop_words = {"a", "an", "the", "on", "in", "of", "for", "and", "to", "with", "from", "by"}
59
+ words = re.findall(r"[a-z]+", title.lower())
60
+ content_word = ""
61
+ for w in words:
62
+ if w not in stop_words and len(w) > 2:
63
+ content_word = w
64
+ break
65
+ if not content_word and words:
66
+ content_word = words[0]
67
+ return f"{lastname}{year}{content_word}"
68
+
69
+ def format_authors(authorships):
70
+ """Format OpenAlex authorships to BibTeX author string."""
71
+ if not authorships:
72
+ return "Unknown"
73
+ names = []
74
+ for a in authorships:
75
+ name = a.get("author", {}).get("display_name", "")
76
+ if name:
77
+ names.append(name)
78
+ if not names:
79
+ return "Unknown"
80
+ return " and ".join(names)
81
+
82
+ def get_journal_or_venue(work):
83
+ """Extract journal name or conference venue."""
84
+ loc = work.get("primary_location", {}) or {}
85
+ source = loc.get("source", {}) or {}
86
+ return source.get("display_name", "")
87
+
88
+ def get_pages(work):
89
+ """Extract page numbers from biblio field."""
90
+ biblio = work.get("biblio", {}) or {}
91
+ first = biblio.get("first_page", "")
92
+ last = biblio.get("last_page", "")
93
+ if first and last:
94
+ return f"{first}--{last}"
95
+ elif first:
96
+ return first
97
+ return ""
98
+
99
+ def get_volume_issue(work):
100
+ """Extract volume and number from biblio."""
101
+ biblio = work.get("biblio", {}) or {}
102
+ return biblio.get("volume", ""), biblio.get("issue", "")
103
+
104
+ def work_to_bibtex(work):
105
+ """Convert a single OpenAlex work object to a BibTeX entry string."""
106
+ cite_key = make_cite_key(work)
107
+ title = normalize_ascii(work.get("display_name", "Untitled"))
108
+ authors = format_authors(work.get("authorships", []))
109
+ year = work.get("publication_year", "")
110
+ doi = work.get("doi", "") or ""
111
+ if doi.startswith("https://doi.org/"):
112
+ doi = doi[len("https://doi.org/"):]
113
+ work_type = work.get("type", "article") or "article"
114
+ journal = normalize_ascii(get_journal_or_venue(work))
115
+ pages = get_pages(work)
116
+ volume, number = get_volume_issue(work)
117
+ oa_url = (work.get("open_access", {}) or {}).get("oa_url", "")
118
+
119
+ # Determine BibTeX entry type
120
+ type_map = {
121
+ "article": "article",
122
+ "review": "article",
123
+ "preprint": "article",
124
+ "book": "book",
125
+ "book-chapter": "incollection",
126
+ "proceedings-article": "inproceedings",
127
+ "dissertation": "phdthesis",
128
+ "dataset": "misc",
129
+ }
130
+ bib_type = type_map.get(work_type, "article")
131
+
132
+ # Build fields
133
+ fields = []
134
+ fields.append(f" title = {{{title}}}")
135
+ fields.append(f" author = {{{authors}}}")
136
+ if year:
137
+ fields.append(f" year = {{{year}}}")
138
+ if journal:
139
+ if bib_type == "inproceedings":
140
+ fields.append(f" booktitle = {{{journal}}}")
141
+ elif bib_type in ("article",):
142
+ fields.append(f" journal = {{{journal}}}")
143
+ else:
144
+ fields.append(f" publisher = {{{journal}}}")
145
+ if volume:
146
+ fields.append(f" volume = {{{volume}}}")
147
+ if number:
148
+ fields.append(f" number = {{{number}}}")
149
+ if pages:
150
+ fields.append(f" pages = {{{pages}}}")
151
+ if doi:
152
+ fields.append(f" doi = {{{doi}}}")
153
+ if oa_url:
154
+ fields.append(f" url = {{{oa_url}}}")
155
+ if work_type == "preprint":
156
+ fields.append(f" note = {{Preprint}}")
157
+
158
+ entry = f"@{bib_type}{{{cite_key},\n"
159
+ entry += ",\n".join(fields)
160
+ entry += "\n}\n"
161
+ return cite_key, entry
162
+
163
+ def reconstruct_abstract(inverted_index):
164
+ """Reconstruct plaintext abstract from OpenAlex inverted index."""
165
+ if not inverted_index:
166
+ return ""
167
+ max_pos = max(max(positions) for positions in inverted_index.values())
168
+ words = [""] * (max_pos + 1)
169
+ for word, positions in inverted_index.items():
170
+ for pos in positions:
171
+ words[pos] = word
172
+ return " ".join(w for w in words if w)
173
+
174
+ def main():
175
+ single_mode = "--single" in sys.argv
176
+
177
+ try:
178
+ data = json.load(sys.stdin)
179
+ except json.JSONDecodeError as e:
180
+ print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
181
+ sys.exit(1)
182
+
183
+ # Handle both search responses (with "results" array) and single work objects
184
+ if single_mode or "results" not in data:
185
+ works = [data]
186
+ else:
187
+ works = data.get("results", [])
188
+
189
+ if not works:
190
+ print("Warning: No works found in input.", file=sys.stderr)
191
+ sys.exit(0)
192
+
193
+ seen_keys = set()
194
+ count = 0
195
+ for work in works:
196
+ cite_key, entry = work_to_bibtex(work)
197
+ # Deduplicate keys
198
+ original_key = cite_key
199
+ suffix = 1
200
+ while cite_key in seen_keys:
201
+ suffix += 1
202
+ cite_key = f"{original_key}{chr(96 + suffix)}" # a, b, c...
203
+ entry = entry.replace(f"{{{original_key},", f"{{{cite_key},", 1)
204
+ seen_keys.add(cite_key)
205
+ print(entry)
206
+ count += 1
207
+
208
+ # Print abstract as comment if available
209
+ abstract_idx = work.get("abstract_inverted_index")
210
+ if abstract_idx:
211
+ abstract = reconstruct_abstract(abstract_idx)
212
+ if abstract:
213
+ # Print as BibTeX comment for reference
214
+ print(f"% Abstract [{cite_key}]: {abstract[:300]}{'...' if len(abstract) > 300 else ''}")
215
+ print()
216
+
217
+ print(f"% Generated {count} BibTeX entries from OpenAlex", file=sys.stderr)
218
+
219
+ if __name__ == "__main__":
220
+ main()