@memorax/memorax-code 0.1.2 → 0.1.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 (242) hide show
  1. package/README.md +16 -12
  2. package/bin/memorax-code-npm-preinstall.mjs +7 -2
  3. package/bin/memorax-code-opencode.mjs +4 -0
  4. package/bin/memorax-code-plugin-postinstall.mjs +342 -93
  5. package/docs/configuration.md +148 -35
  6. package/docs/troubleshooting.md +73 -13
  7. package/lib/dsh-plugin-install.mjs +22 -0
  8. package/lib/memorax-code-adapter-common/src/hooks/capture-cwd-hook.mjs +21 -14
  9. package/lib/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +26 -7
  10. package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +86 -95
  11. package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-policy.mjs +58 -0
  12. package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs +2 -2
  13. package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-job-supervisor.mjs +1 -1
  14. package/lib/memorax-code-adapter-common/src/windows-cli-invocation.mjs +13 -1
  15. package/lib/memorax-code-backend/dist/app/backend-server.js +210 -0
  16. package/lib/memorax-code-backend/dist/{server-observability.js → app/memory-observability.js} +4 -4
  17. package/lib/memorax-code-backend/dist/{backend-state.js → app/state.js} +1 -1
  18. package/lib/memorax-code-backend/dist/{claude-adapter-lifecycle.js → clients/claude/lifecycle.js} +2 -2
  19. package/lib/memorax-code-backend/dist/{claude-memory-hook-runtime.js → clients/claude/memory-hook-runtime.js} +7 -7
  20. package/lib/memorax-code-backend/dist/clients/codex/lifecycle.js +131 -0
  21. package/lib/memorax-code-backend/dist/{codex-memory-hook-runtime.js → clients/codex/memory-hook-runtime.js} +9 -9
  22. package/lib/memorax-code-backend/dist/{codex-plugin-hooks.js → clients/codex/plugin-hooks.js} +1 -1
  23. package/lib/memorax-code-backend/dist/{codex-plugin-install.js → clients/codex/plugin-install.js} +6 -6
  24. package/lib/memorax-code-backend/dist/clients/dsh/lifecycle.js +75 -0
  25. package/lib/memorax-code-backend/dist/clients/dsh/memory-hook-runtime.js +258 -0
  26. package/lib/memorax-code-backend/dist/clients/dsh/session-turn.js +249 -0
  27. package/lib/memorax-code-backend/dist/clients/opencode/lifecycle.js +54 -0
  28. package/lib/memorax-code-backend/dist/clients/opencode/memory-hook-runtime.js +277 -0
  29. package/lib/memorax-code-backend/dist/clients/opencode/message-turn.js +78 -0
  30. package/lib/memorax-code-backend/dist/codex-adapter-lifecycle.js +1 -131
  31. package/lib/memorax-code-backend/dist/{memorax-code-config.js → config/memorax-code.js} +32 -4
  32. package/lib/memorax-code-backend/dist/{server-cli.js → entrypoints/backend-cli.js} +137 -37
  33. package/lib/memorax-code-backend/dist/jsonl-append.js +1 -75
  34. package/lib/memorax-code-backend/dist/{active-client-selection.js → lifecycle/active-clients.js} +12 -1
  35. package/lib/memorax-code-backend/dist/{backend-service-cleanup.js → lifecycle/backend/cleanup.js} +1 -1
  36. package/lib/memorax-code-backend/dist/{backend-service-record.js → lifecycle/backend/record.js} +1 -1
  37. package/lib/memorax-code-backend/dist/{backend-service-result.js → lifecycle/backend/result.js} +2 -2
  38. package/lib/memorax-code-backend/dist/{service.js → lifecycle/backend/service.js} +16 -16
  39. package/lib/memorax-code-backend/dist/{backend-token-record.js → lifecycle/backend/token-record.js} +1 -1
  40. package/lib/memorax-code-backend/dist/{backend-service-token.js → lifecycle/backend/token.js} +2 -2
  41. package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +109 -0
  42. package/lib/memorax-code-backend/dist/{client-selection.js → lifecycle/client-selection.js} +11 -5
  43. package/lib/memorax-code-backend/dist/{backend-lifecycle-lock.js → lifecycle/lock.js} +1 -1
  44. package/lib/memorax-code-backend/dist/{memorax-code-lifecycle.js → lifecycle/orchestrator.js} +325 -39
  45. package/lib/memorax-code-backend/dist/memorax-cli.js +1 -1
  46. package/lib/memorax-code-backend/dist/memorax-code.js +1 -1
  47. package/lib/memorax-code-backend/dist/{automatic-memory-retrieval.js → memory/automatic-retrieval.js} +3 -3
  48. package/lib/memorax-code-backend/dist/{automatic-memory-writeback.js → memory/automatic-writeback.js} +75 -21
  49. package/lib/memorax-code-backend/dist/{memory-cli.js → memory/cli.js} +23 -10
  50. package/lib/memorax-code-backend/dist/{memory-hook-command.js → memory/hook-command.js} +156 -9
  51. package/lib/memorax-code-backend/dist/memory/observability.js +1 -0
  52. package/lib/memorax-code-backend/dist/memory/payload-redaction.js +212 -0
  53. package/lib/memorax-code-backend/dist/{memory-reminder-trace-recorder.js → memory/reminder-trace-recorder.js} +20 -7
  54. package/lib/memorax-code-backend/dist/{repository-memory-context.js → memory/repository-session.js} +2 -2
  55. package/lib/memorax-code-backend/dist/{memory-service.js → memory/service.js} +27 -5
  56. package/lib/memorax-code-backend/dist/{memory-turn-coordinator.js → memory/turn-coordinator.js} +2 -1
  57. package/lib/memorax-code-backend/dist/{memory-writeback-buffer.js → memory/writeback-buffer.js} +3 -3
  58. package/lib/memorax-code-backend/dist/{memory-writeback-chunk.js → memory/writeback-chunk.js} +1 -1
  59. package/lib/memorax-code-backend/dist/{memory-writeback-reconciler.js → memory/writeback-reconciler.js} +6 -6
  60. package/lib/memorax-code-backend/dist/{memory-writeback-task-projection.js → memory/writeback-task-projection.js} +4 -4
  61. package/lib/memorax-code-backend/dist/{memorax-adapter.js → provider/memorax/adapter.js} +5 -5
  62. package/lib/memorax-code-backend/dist/{memorax-config.js → provider/memorax/config.js} +4 -4
  63. package/lib/memorax-code-backend/dist/{memorax-http.js → provider/memorax/http.js} +1 -1
  64. package/lib/memorax-code-backend/dist/{repo-memory-readiness.js → repository/readiness.js} +3 -3
  65. package/lib/memorax-code-backend/dist/server.js +3 -212
  66. package/lib/memorax-code-backend/dist/service-entrypoint.js +1 -1
  67. package/lib/memorax-code-backend/dist/{debug-log.js → shared/debug-log.js} +1 -1
  68. package/lib/memorax-code-backend/dist/shared/jsonl-append.js +75 -0
  69. package/lib/memorax-code-backend/dist/shared/windows-cli-invocation.js +148 -0
  70. package/lib/memorax-code-backend/dist/{trace-config.js → trace/config.js} +43 -5
  71. package/lib/memorax-code-backend/dist/{trace-context.js → trace/context.js} +53 -2
  72. package/lib/memorax-code-backend/dist/{trace-store.js → trace/store.js} +3 -3
  73. package/lib/memorax-code-backend/dist/{server-health.js → transport/http/health.js} +1 -1
  74. package/lib/memorax-code-backend/dist/{server-memory-hook.js → transport/http/memory-hook.js} +3 -3
  75. package/lib/memorax-code-backend/dist/{server-http.js → transport/http/request.js} +1 -1
  76. package/lib/memorax-code-backend/dist/{claude-local-transcript.js → viewer/history/claude-transcript.js} +3 -3
  77. package/lib/memorax-code-backend/dist/{memory-viewer-session-title.js → viewer/history/session-title.js} +1 -1
  78. package/lib/memorax-code-backend/dist/{server-memory-viewer.js → viewer/http/public-routes.js} +15 -15
  79. package/lib/memorax-code-backend/dist/viewer/model.js +1 -0
  80. package/lib/memorax-code-backend/dist/{memory-viewer-activity.js → viewer/projection/activity.js} +2 -2
  81. package/lib/memorax-code-backend/dist/{memory-viewer-history-projection.js → viewer/projection/history.js} +1 -1
  82. package/lib/memorax-code-backend/dist/{memory-viewer-observability.js → viewer/projection/observability.js} +1 -1
  83. package/lib/memorax-code-backend/dist/{memory-viewer-user-projection.js → viewer/projection/user.js} +1 -1
  84. package/lib/memorax-code-backend/dist/{memory-viewer-writeback-status.js → viewer/projection/writeback-status.js} +1 -1
  85. package/lib/memorax-code-backend/dist/{memory-viewer-store.js → viewer/store.js} +59 -32
  86. package/lib/memorax-code-backend/dist/{memory-viewer-user-html.js → viewer/ui/user-html.js} +11 -10
  87. package/lib/memorax-code-backend/dist/windows-cli-invocation.js +1 -148
  88. package/lib/memorax-code-backend/package.json +3 -3
  89. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  90. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  91. package/lib/memorax-code-claude-adapter/package.json +1 -1
  92. package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +11 -2
  93. package/lib/memorax-code-claude-adapter/skills/memorax-code/defaults.json +8 -5
  94. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  95. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +37 -9
  96. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/personal-read.md +1 -1
  97. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/personal-write.md +33 -13
  98. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-build.md +62 -13
  99. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-read.md +11 -4
  100. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-templates.md +179 -59
  101. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/repo-update.md +18 -7
  102. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/collect_all.py +122 -39
  103. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/detect_updates.py +73 -10
  104. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/github_resource_facets.py +9 -1
  105. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +9 -1
  106. package/lib/memorax-code-claude-adapter/skills/memorax-code/scripts/validate_memory.py +23 -1
  107. package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
  108. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  109. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  110. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/capture-cwd-hook.mjs +21 -14
  111. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +26 -7
  112. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +86 -95
  113. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-policy.mjs +58 -0
  114. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-auto-build.mjs +2 -2
  115. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-job-supervisor.mjs +1 -1
  116. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/windows-cli-invocation.mjs +13 -1
  117. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  118. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +11 -2
  119. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/defaults.json +8 -5
  120. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  121. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +37 -9
  122. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/personal-read.md +1 -1
  123. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/personal-write.md +33 -13
  124. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-build.md +62 -13
  125. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-read.md +11 -4
  126. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-templates.md +179 -59
  127. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/repo-update.md +18 -7
  128. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/collect_all.py +122 -39
  129. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/detect_updates.py +73 -10
  130. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/github_resource_facets.py +9 -1
  131. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +9 -1
  132. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/scripts/validate_memory.py +23 -1
  133. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +1 -0
  134. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  135. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  136. package/lib/memorax-code-codex-adapter/package.json +1 -1
  137. package/lib/memorax-code-codex-adapter/runtime-hooks/memory-skill-reminder.mjs +10 -2
  138. package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +11 -2
  139. package/lib/memorax-code-codex-adapter/skills/memorax-code/defaults.json +8 -5
  140. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  141. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +37 -9
  142. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/personal-read.md +1 -1
  143. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/personal-write.md +33 -13
  144. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-build.md +62 -13
  145. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-read.md +11 -4
  146. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-templates.md +179 -59
  147. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/repo-update.md +18 -7
  148. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/collect_all.py +122 -39
  149. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/detect_updates.py +73 -10
  150. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/github_resource_facets.py +9 -1
  151. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +9 -1
  152. package/lib/memorax-code-codex-adapter/skills/memorax-code/scripts/validate_memory.py +23 -1
  153. package/lib/memorax-code-dsh-adapter/cordis.patch.yml +10 -0
  154. package/lib/memorax-code-dsh-adapter/hooks/repo-memory-job.mjs +68 -0
  155. package/lib/memorax-code-dsh-adapter/package.json +69 -0
  156. package/lib/memorax-code-dsh-adapter/skills/memorax-code/SKILL.md +91 -0
  157. package/lib/memorax-code-dsh-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  158. package/lib/memorax-code-dsh-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  159. package/lib/memorax-code-dsh-adapter/skills/memorax-code/defaults.json +12 -0
  160. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +86 -0
  161. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +91 -0
  162. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/personal-read.md +46 -0
  163. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/personal-write.md +120 -0
  164. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-build.md +319 -0
  165. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-read.md +103 -0
  166. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  167. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/repo-update.md +127 -0
  168. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  169. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  170. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  171. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  172. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  173. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  174. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  175. package/lib/memorax-code-dsh-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  176. package/lib/memorax-code-dsh-adapter/src/backend-client.mjs +119 -0
  177. package/lib/memorax-code-dsh-adapter/src/dsh-message.mjs +32 -0
  178. package/lib/memorax-code-dsh-adapter/src/dsh-version.mjs +18 -0
  179. package/lib/memorax-code-dsh-adapter/src/http-client.mjs +55 -0
  180. package/lib/memorax-code-dsh-adapter/src/index.mjs +72 -0
  181. package/lib/memorax-code-dsh-adapter/src/personal-context-worker.mjs +48 -0
  182. package/lib/memorax-code-dsh-adapter/src/personal-context.mjs +114 -0
  183. package/lib/memorax-code-dsh-adapter/src/plugin.mjs +724 -0
  184. package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +1358 -0
  185. package/lib/memorax-code-dsh-adapter/src/protocol.mjs +145 -0
  186. package/lib/memorax-code-dsh-adapter/src/runtime-state.mjs +132 -0
  187. package/lib/memorax-code-opencode-adapter/hooks/repo-memory-job.mjs +37 -0
  188. package/lib/memorax-code-opencode-adapter/package.json +10 -0
  189. package/lib/memorax-code-opencode-adapter/skills/memorax-code/SKILL.md +91 -0
  190. package/lib/memorax-code-opencode-adapter/skills/memorax-code/agents/claude.yaml +10 -0
  191. package/lib/memorax-code-opencode-adapter/skills/memorax-code/agents/openai.yaml +7 -0
  192. package/lib/memorax-code-opencode-adapter/skills/memorax-code/defaults.json +12 -0
  193. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +86 -0
  194. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +91 -0
  195. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/personal-read.md +46 -0
  196. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/personal-write.md +120 -0
  197. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-build.md +319 -0
  198. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-read.md +103 -0
  199. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-templates.md +390 -0
  200. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/repo-update.md +127 -0
  201. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
  202. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
  203. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
  204. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
  205. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
  206. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
  207. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
  208. package/lib/memorax-code-opencode-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
  209. package/lib/memorax-code-opencode-adapter/src/adapter-paths.mjs +37 -0
  210. package/lib/memorax-code-opencode-adapter/src/cli.mjs +260 -0
  211. package/lib/memorax-code-opencode-adapter/src/diagnostics.mjs +31 -0
  212. package/lib/memorax-code-opencode-adapter/src/plugin-install.mjs +673 -0
  213. package/lib/memorax-code-opencode-adapter/src/plugin.mjs +539 -0
  214. package/lib/memorax-code-opencode-adapter/src/repo-memory-server-runner.mjs +442 -0
  215. package/lib/run-entrypoint.mjs +6 -0
  216. package/package.json +7 -3
  217. package/lib/memorax-code-backend/dist/client-plugin-removal.js +0 -67
  218. /package/lib/memorax-code-backend/dist/{claude-transcript-turn.js → clients/claude/transcript-turn.js} +0 -0
  219. /package/lib/memorax-code-backend/dist/{codex-effective-prompt.js → clients/codex/effective-prompt.js} +0 -0
  220. /package/lib/memorax-code-backend/dist/{codex-rollout-turn.js → clients/codex/rollout-turn.js} +0 -0
  221. /package/lib/memorax-code-backend/dist/{codex-session-turn-index.js → clients/codex/session-turn-index.js} +0 -0
  222. /package/lib/memorax-code-backend/dist/{codex-workspace-links.js → clients/codex/workspace-links.js} +0 -0
  223. /package/lib/memorax-code-backend/dist/{backend-env.js → config/backend-env.js} +0 -0
  224. /package/lib/memorax-code-backend/dist/{proxy-env.js → config/proxy-env.js} +0 -0
  225. /package/lib/memorax-code-backend/dist/{entrypoint.js → entrypoints/main.js} +0 -0
  226. /package/lib/memorax-code-backend/dist/{backend-process.js → lifecycle/backend/process.js} +0 -0
  227. /package/lib/memorax-code-backend/dist/{backend-shutdown-request.js → lifecycle/backend/shutdown-request.js} +0 -0
  228. /package/lib/memorax-code-backend/dist/{backend-status.js → lifecycle/backend/status.js} +0 -0
  229. /package/lib/memorax-code-backend/dist/{adapter-lifecycle-participant.js → lifecycle/contracts.js} +0 -0
  230. /package/lib/memorax-code-backend/dist/{install-watchdog.js → lifecycle/install-watchdog.js} +0 -0
  231. /package/lib/memorax-code-backend/dist/{memory-observability.js → lifecycle/participant.js} +0 -0
  232. /package/lib/memorax-code-backend/dist/{memory-project.js → memory/project.js} +0 -0
  233. /package/lib/memorax-code-backend/dist/{repository-memory-scope.js → repository/scope.js} +0 -0
  234. /package/lib/memorax-code-backend/dist/{incremental-jsonl-projection.js → shared/incremental-jsonl-projection.js} +0 -0
  235. /package/lib/memorax-code-backend/dist/{utils.js → shared/record.js} +0 -0
  236. /package/lib/memorax-code-backend/dist/{server-json.js → transport/http/json.js} +0 -0
  237. /package/lib/memorax-code-backend/dist/{memory-viewer-data-projection-cache.js → viewer/projection/cache.js} +0 -0
  238. /package/lib/memorax-code-backend/dist/{memory-viewer-event-identity.js → viewer/projection/event-identity.js} +0 -0
  239. /package/lib/memorax-code-backend/dist/{memory-viewer-redaction.js → viewer/projection/redaction.js} +0 -0
  240. /package/lib/memorax-code-backend/dist/{memory-viewer-turn-reference.js → viewer/projection/turn-reference.js} +0 -0
  241. /package/lib/memorax-code-backend/dist/{memory-viewer-icon.js → viewer/ui/icon.js} +0 -0
  242. /package/lib/memorax-code-backend/dist/{memory-viewer-logo.js → viewer/ui/logo.js} +0 -0
@@ -0,0 +1,528 @@
1
+ #!/usr/bin/env python3
2
+ """Manage repo-scoped user profile preferences under .repo_memory/user-profile."""
3
+
4
+ import argparse
5
+ import json
6
+ import os
7
+ import re
8
+ import subprocess
9
+ import sys
10
+ import tempfile
11
+ from contextlib import contextmanager
12
+ from dataclasses import dataclass, replace
13
+ from datetime import datetime, timezone
14
+ from pathlib import Path
15
+ from typing import Any, Iterator
16
+
17
+ ALLOWED_TYPES = {"communication", "workflow", "environment", "profile"}
18
+ ALLOWED_STATUSES = {"active", "superseded", "deleted"}
19
+ GITIGNORE_RULE = ".repo_memory/"
20
+ MAX_PREFERENCES_BYTES = 64 * 1024
21
+ PREFERENCES_SCHEMA = "repo_user_profile_memory.v0.1"
22
+ OWNER = "repo-user-profile-memory"
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class Preference:
27
+ id: str
28
+ type: str
29
+ description: str
30
+ applies_when: str
31
+ do_not_apply_when: str
32
+ created: str
33
+ updated: str
34
+ confidence: str = "user_stated"
35
+ status: str = "active"
36
+
37
+
38
+ class StorageError(RuntimeError):
39
+ """Preference storage is malformed or unsafe to mutate."""
40
+
41
+
42
+ def run(cmd: list[str], cwd: Path) -> tuple[int, str, str]:
43
+ proc = subprocess.run(cmd, cwd=cwd, text=True, capture_output=True)
44
+ return proc.returncode, proc.stdout.strip(), proc.stderr.strip()
45
+
46
+
47
+ def now_iso() -> str:
48
+ return datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
49
+
50
+
51
+ def resolve_repo(path: str) -> Path:
52
+ start = Path(path).resolve()
53
+ if not start.exists():
54
+ raise SystemExit(f"Repository path does not exist: {start}")
55
+ code, out, err = run(["git", "rev-parse", "--show-toplevel"], start)
56
+ if code == 0 and out:
57
+ return Path(out).resolve()
58
+ if start.is_file():
59
+ return start.parent
60
+ return start
61
+
62
+
63
+ def gitignore_has_repo_memory_rule(text: str) -> bool:
64
+ for line in text.splitlines():
65
+ stripped = line.strip()
66
+ if not stripped or stripped.startswith("#") or stripped.startswith("!"):
67
+ continue
68
+ rule = stripped.split("#", 1)[0].strip().lstrip("/")
69
+ if rule in {".repo_memory", ".repo_memory/"}:
70
+ return True
71
+ return False
72
+
73
+
74
+ def ensure_repo_memory_gitignore(repo: Path) -> bool:
75
+ gitignore = repo / ".gitignore"
76
+ existing = gitignore.read_text(encoding="utf-8") if gitignore.exists() else ""
77
+ if gitignore_has_repo_memory_rule(existing):
78
+ return False
79
+ separator = "" if not existing or existing.endswith("\n") else "\n"
80
+ gitignore.write_text(f"{existing}{separator}{GITIGNORE_RULE}\n", encoding="utf-8")
81
+ return True
82
+
83
+
84
+ def normalize_field(value: str) -> str:
85
+ # Keep field values one-line so user text cannot create headings/frontmatter.
86
+ return re.sub(r"\s+", " ", value.replace("\r", " ").replace("\n", " ")).strip()
87
+
88
+
89
+ def normalize_key(value: str) -> str:
90
+ return normalize_field(value).casefold()
91
+
92
+
93
+ def slugify(value: str, fallback: str) -> str:
94
+ ascii_value = value.encode("ascii", "ignore").decode("ascii")
95
+ words = re.findall(r"[a-zA-Z0-9]+", ascii_value.lower())
96
+ slug = "-".join(words[:5]).strip("-")
97
+ return slug or fallback
98
+
99
+
100
+ def preferences_path(repo: Path) -> Path:
101
+ return repo / ".repo_memory" / "user-profile" / "preferences.md"
102
+
103
+
104
+ def lock_path(repo: Path) -> Path:
105
+ return repo / ".repo_memory" / "user-profile" / ".preferences.lock"
106
+
107
+
108
+ @contextmanager
109
+ def preference_lock(repo: Path) -> Iterator[None]:
110
+ path = lock_path(repo)
111
+ path.parent.mkdir(parents=True, exist_ok=True)
112
+ with path.open("a+b") as lock_file:
113
+ if os.name == "nt":
114
+ import msvcrt
115
+
116
+ lock_file.seek(0, os.SEEK_END)
117
+ if lock_file.tell() == 0:
118
+ lock_file.write(b"\0")
119
+ lock_file.flush()
120
+ lock_file.seek(0)
121
+ msvcrt.locking(lock_file.fileno(), msvcrt.LK_LOCK, 1)
122
+ try:
123
+ yield
124
+ finally:
125
+ lock_file.seek(0)
126
+ msvcrt.locking(lock_file.fileno(), msvcrt.LK_UNLCK, 1)
127
+ else:
128
+ import fcntl
129
+
130
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
131
+ try:
132
+ yield
133
+ finally:
134
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
135
+
136
+
137
+ def ensure_storage(repo: Path) -> tuple[Path, bool]:
138
+ gitignore_updated = ensure_repo_memory_gitignore(repo)
139
+ path = preferences_path(repo)
140
+ path.parent.mkdir(parents=True, exist_ok=True)
141
+ if path.is_symlink():
142
+ raise StorageError("Invalid repo user profile preferences: preferences.md must not be a symlink")
143
+ if not path.exists():
144
+ write_preferences(path, [], now_iso())
145
+ return path, gitignore_updated
146
+
147
+
148
+ def strip_ticks(value: str) -> str:
149
+ value = value.strip()
150
+ if value.startswith("`") and value.endswith("`") and len(value) >= 2:
151
+ return value[1:-1]
152
+ return value
153
+
154
+
155
+ def field(block: str, name: str) -> str:
156
+ match = re.search(rf"^- {re.escape(name)}: (.*)$", block, flags=re.MULTILINE)
157
+ return match.group(1).strip() if match else ""
158
+
159
+
160
+ def strip_yaml_scalar(value: str) -> str:
161
+ trimmed = value.strip()
162
+ if len(trimmed) >= 2 and trimmed[0] == trimmed[-1] and trimmed[0] in {'"', "'"}:
163
+ return trimmed[1:-1]
164
+ return trimmed
165
+
166
+
167
+ def parse_frontmatter(text: str) -> dict[str, str]:
168
+ match = re.match(r"^---\r?\n(?P<body>.*?)\r?\n---(?:\r?\n|$)", text, flags=re.DOTALL)
169
+ if not match:
170
+ raise StorageError("Invalid repo user profile preferences: missing frontmatter")
171
+ metadata: dict[str, str] = {}
172
+ for raw_line in match.group("body").splitlines():
173
+ line = raw_line.strip()
174
+ if not line or line.startswith("#"):
175
+ continue
176
+ field_match = re.match(r"^([A-Za-z0-9_-]+):\s*(.*)$", line)
177
+ if not field_match:
178
+ raise StorageError("Invalid repo user profile preferences: malformed frontmatter")
179
+ metadata[field_match.group(1)] = strip_yaml_scalar(field_match.group(2))
180
+ return metadata
181
+
182
+
183
+ def parse_non_negative_int(value: str, name: str) -> int:
184
+ normalized = value.strip().replace("_", "")
185
+ if not re.match(r"^\d+$", normalized):
186
+ raise StorageError(f"Invalid repo user profile preferences: {name} must be a non-negative integer")
187
+ return int(normalized)
188
+
189
+
190
+ def validate_frontmatter(text: str) -> tuple[int, int]:
191
+ metadata = parse_frontmatter(text)
192
+ expected = {
193
+ "schema": PREFERENCES_SCHEMA,
194
+ "scope": "repo",
195
+ "owner": OWNER,
196
+ "trust_state": "user_stated",
197
+ }
198
+ for key, value in expected.items():
199
+ if metadata.get(key) != value:
200
+ raise StorageError(f"Invalid repo user profile preferences: {key} mismatch")
201
+ if "active_count" not in metadata or "total_count" not in metadata:
202
+ raise StorageError("Invalid repo user profile preferences: missing counts")
203
+ return (
204
+ parse_non_negative_int(metadata["active_count"], "active_count"),
205
+ parse_non_negative_int(metadata["total_count"], "total_count"),
206
+ )
207
+
208
+
209
+ def read_preferences(path: Path) -> list[Preference]:
210
+ if path.is_symlink():
211
+ raise StorageError("Invalid repo user profile preferences: preferences.md must not be a symlink")
212
+ if not path.exists():
213
+ return []
214
+ if path.stat().st_size > MAX_PREFERENCES_BYTES:
215
+ raise StorageError("Invalid repo user profile preferences: preferences.md is too large")
216
+ text = path.read_text(encoding="utf-8")
217
+ declared_active, declared_total = validate_frontmatter(text)
218
+ entries: list[Preference] = []
219
+ matches = list(re.finditer(r"^## Preference (?P<id>pref_[^\s]+)\s*$", text, flags=re.MULTILINE))
220
+ if len(matches) != declared_total:
221
+ raise StorageError("Invalid repo user profile preferences: total_count mismatch")
222
+ for index, match in enumerate(matches):
223
+ start = match.end()
224
+ end = matches[index + 1].start() if index + 1 < len(matches) else len(text)
225
+ block = text[start:end]
226
+ status = strip_ticks(field(block, "Status"))
227
+ if status not in ALLOWED_STATUSES:
228
+ raise StorageError("Invalid repo user profile preferences: unknown status")
229
+ pref_type = strip_ticks(field(block, "Type"))
230
+ if pref_type not in ALLOWED_TYPES:
231
+ raise StorageError("Invalid repo user profile preferences: unknown type")
232
+ confidence = strip_ticks(field(block, "Confidence"))
233
+ created = strip_ticks(field(block, "Created"))
234
+ updated = strip_ticks(field(block, "Updated"))
235
+ if not confidence or not created or not updated:
236
+ raise StorageError("Invalid repo user profile preferences: missing required metadata")
237
+ description = normalize_field(field(block, "Description"))
238
+ if status != "active":
239
+ continue
240
+ if not description:
241
+ raise StorageError("Invalid repo user profile preferences: active preference missing description")
242
+ if pref_type not in ALLOWED_TYPES:
243
+ continue
244
+ entries.append(
245
+ Preference(
246
+ id=match.group("id"),
247
+ type=pref_type,
248
+ status=status,
249
+ confidence=confidence,
250
+ created=created,
251
+ updated=updated,
252
+ description=description,
253
+ applies_when=normalize_field(field(block, "Applies when")),
254
+ do_not_apply_when=normalize_field(field(block, "Do not apply when")),
255
+ )
256
+ )
257
+ if len(entries) != declared_active:
258
+ raise StorageError("Invalid repo user profile preferences: active_count mismatch")
259
+ return entries
260
+
261
+
262
+ def entry_markdown(pref: Preference) -> str:
263
+ return "\n".join(
264
+ [
265
+ f"## Preference {pref.id}",
266
+ "",
267
+ f"- Type: `{pref.type}`",
268
+ "- Status: `active`",
269
+ f"- Confidence: `{pref.confidence}`",
270
+ f"- Created: `{pref.created}`",
271
+ f"- Updated: `{pref.updated}`",
272
+ f"- Description: {pref.description}",
273
+ f"- Applies when: {pref.applies_when or '-'}",
274
+ f"- Do not apply when: {pref.do_not_apply_when or '-'}",
275
+ f"- Raw lookup: `preferenceId={pref.id}`",
276
+ ]
277
+ )
278
+
279
+
280
+ def render_preferences(entries: list[Preference], updated_at: str) -> str:
281
+ sorted_entries = sorted(entries, key=lambda pref: pref.updated, reverse=True)
282
+ parts = [
283
+ "---",
284
+ f'schema: "{PREFERENCES_SCHEMA}"',
285
+ 'scope: "repo"',
286
+ f'owner: "{OWNER}"',
287
+ 'trust_state: "user_stated"',
288
+ f'updated_at: "{updated_at}"',
289
+ f"active_count: {len(sorted_entries)}",
290
+ f"total_count: {len(sorted_entries)}",
291
+ "---",
292
+ "",
293
+ "# Repo-Scoped User Profile And Preferences",
294
+ "",
295
+ "These memories are local to this repository. System, developer, and AGENTS.md instructions override current user instructions, and current user instructions override stored preferences. Do not treat these preferences as evidence about current code behavior.",
296
+ "",
297
+ "## Active Preferences",
298
+ "",
299
+ ]
300
+ if sorted_entries:
301
+ parts.append("\n\n---\n\n".join(entry_markdown(pref) for pref in sorted_entries))
302
+ parts.append("")
303
+ return "\n".join(parts).rstrip() + "\n"
304
+
305
+
306
+ def fsync_directory(path: Path) -> None:
307
+ if os.name == "nt":
308
+ return
309
+ try:
310
+ fd = os.open(path, os.O_RDONLY)
311
+ except OSError:
312
+ return
313
+ try:
314
+ os.fsync(fd)
315
+ finally:
316
+ os.close(fd)
317
+
318
+
319
+ def write_preferences(path: Path, entries: list[Preference], updated_at: str) -> None:
320
+ path.parent.mkdir(parents=True, exist_ok=True)
321
+ text = render_preferences(entries, updated_at)
322
+ if len(text.encode("utf-8")) > MAX_PREFERENCES_BYTES:
323
+ raise StorageError(
324
+ f"Invalid repo user profile preferences: rendered preferences.md exceeds {MAX_PREFERENCES_BYTES} bytes"
325
+ )
326
+ fd, tmp_name = tempfile.mkstemp(prefix=f".{path.name}.", suffix=".tmp", dir=path.parent)
327
+ tmp_path = Path(tmp_name)
328
+ try:
329
+ with os.fdopen(fd, "w", encoding="utf-8", newline="\n") as tmp_file:
330
+ tmp_file.write(text)
331
+ tmp_file.flush()
332
+ os.fsync(tmp_file.fileno())
333
+ os.replace(tmp_path, path)
334
+ fsync_directory(path.parent)
335
+ except Exception:
336
+ try:
337
+ tmp_path.unlink()
338
+ except FileNotFoundError:
339
+ pass
340
+ raise
341
+
342
+
343
+ def make_id(entries: list[Preference], pref_type: str, description: str, timestamp: str) -> str:
344
+ day = timestamp[:10].replace("-", "")
345
+ base = f"pref_{day}_{slugify(description, pref_type + '-preference')}"
346
+ existing = {pref.id for pref in entries}
347
+ if base not in existing:
348
+ return base
349
+ counter = 2
350
+ while f"{base}-{counter}" in existing:
351
+ counter += 1
352
+ return f"{base}-{counter}"
353
+
354
+
355
+ def preference_contains_add_content(pref: Preference, description: str) -> bool:
356
+ requested = normalize_key(description)
357
+ existing_text = normalize_key(" ".join([pref.description, pref.applies_when, pref.do_not_apply_when]))
358
+ return bool(requested) and requested in existing_text
359
+
360
+
361
+ def add_preference(args: argparse.Namespace) -> dict[str, Any]:
362
+ repo = resolve_repo(args.repo)
363
+ with preference_lock(repo):
364
+ path, gitignore_updated = ensure_storage(repo)
365
+ entries = read_preferences(path)
366
+ pref_type = args.type
367
+ description = normalize_field(args.description)
368
+ applies_when = normalize_field(args.applies_when)
369
+ do_not_apply_when = normalize_field(args.do_not_apply_when or "")
370
+ for pref in entries:
371
+ if preference_contains_add_content(pref, description):
372
+ return {
373
+ "ok": True,
374
+ "op": "add",
375
+ "status": "duplicate",
376
+ "id": pref.id,
377
+ "active_count": len(entries),
378
+ "total_count": len(entries),
379
+ "preferences_path": str(path),
380
+ "gitignore_updated": gitignore_updated,
381
+ }
382
+ timestamp = now_iso()
383
+ pref = Preference(
384
+ id=make_id(entries, pref_type, description, timestamp),
385
+ type=pref_type,
386
+ description=description,
387
+ applies_when=applies_when,
388
+ do_not_apply_when=do_not_apply_when,
389
+ created=timestamp,
390
+ updated=timestamp,
391
+ )
392
+ entries.append(pref)
393
+ write_preferences(path, entries, timestamp)
394
+ return {
395
+ "ok": True,
396
+ "op": "add",
397
+ "status": "added",
398
+ "id": pref.id,
399
+ "active_count": len(entries),
400
+ "total_count": len(entries),
401
+ "preferences_path": str(path),
402
+ "gitignore_updated": gitignore_updated,
403
+ }
404
+
405
+
406
+ def update_preference(args: argparse.Namespace) -> dict[str, Any]:
407
+ repo = resolve_repo(args.repo)
408
+ with preference_lock(repo):
409
+ path, gitignore_updated = ensure_storage(repo)
410
+ entries = read_preferences(path)
411
+ timestamp = now_iso()
412
+ updated_entries: list[Preference] = []
413
+ found = False
414
+ for pref in entries:
415
+ if pref.id != args.id:
416
+ updated_entries.append(pref)
417
+ continue
418
+ found = True
419
+ updated_entries.append(
420
+ replace(
421
+ pref,
422
+ description=normalize_field(args.description),
423
+ applies_when=normalize_field(args.applies_when) if args.applies_when is not None else pref.applies_when,
424
+ do_not_apply_when=normalize_field(args.do_not_apply_when) if args.do_not_apply_when is not None else pref.do_not_apply_when,
425
+ updated=timestamp,
426
+ )
427
+ )
428
+ if not found:
429
+ raise SystemExit(f"Preference id not found: {args.id}")
430
+ write_preferences(path, updated_entries, timestamp)
431
+ return {
432
+ "ok": True,
433
+ "op": "update",
434
+ "status": "updated",
435
+ "id": args.id,
436
+ "active_count": len(updated_entries),
437
+ "total_count": len(updated_entries),
438
+ "preferences_path": str(path),
439
+ "gitignore_updated": gitignore_updated,
440
+ }
441
+
442
+
443
+ def delete_preference(args: argparse.Namespace) -> dict[str, Any]:
444
+ repo = resolve_repo(args.repo)
445
+ with preference_lock(repo):
446
+ path, gitignore_updated = ensure_storage(repo)
447
+ entries = read_preferences(path)
448
+ remaining = [pref for pref in entries if pref.id != args.id]
449
+ if len(remaining) == len(entries):
450
+ raise SystemExit(f"Preference id not found: {args.id}")
451
+ timestamp = now_iso()
452
+ write_preferences(path, remaining, timestamp)
453
+ return {
454
+ "ok": True,
455
+ "op": "delete",
456
+ "status": "deleted",
457
+ "id": args.id,
458
+ "active_count": len(remaining),
459
+ "total_count": len(remaining),
460
+ "preferences_path": str(path),
461
+ "gitignore_updated": gitignore_updated,
462
+ }
463
+
464
+
465
+ def list_preferences(args: argparse.Namespace) -> dict[str, Any]:
466
+ repo = resolve_repo(args.repo)
467
+ path = preferences_path(repo)
468
+ entries = read_preferences(path)
469
+ return {
470
+ "ok": True,
471
+ "op": "list",
472
+ "active_count": len(entries),
473
+ "total_count": len(entries),
474
+ "preferences_path": str(path),
475
+ "preferences": [pref.__dict__ for pref in entries],
476
+ }
477
+
478
+
479
+ def add_common_repo_arg(parser: argparse.ArgumentParser) -> None:
480
+ parser.add_argument("--repo", default=".", help="Path inside the target git repository")
481
+
482
+
483
+ def build_parser() -> argparse.ArgumentParser:
484
+ parser = argparse.ArgumentParser(description="Manage repo-scoped user profile preferences.")
485
+ subparsers = parser.add_subparsers(dest="command", required=True)
486
+
487
+ add = subparsers.add_parser("add", help="Add a repo-scoped user preference")
488
+ add_common_repo_arg(add)
489
+ add.add_argument("--type", required=True, choices=sorted(ALLOWED_TYPES))
490
+ add.add_argument("--description", required=True)
491
+ add.add_argument("--applies-when", required=True)
492
+ add.add_argument("--do-not-apply-when", default="")
493
+ add.set_defaults(func=add_preference)
494
+
495
+ update = subparsers.add_parser("update", help="Update an existing preference by id")
496
+ add_common_repo_arg(update)
497
+ update.add_argument("--id", required=True)
498
+ update.add_argument("--description", required=True)
499
+ update.add_argument("--applies-when")
500
+ update.add_argument("--do-not-apply-when")
501
+ update.set_defaults(func=update_preference)
502
+
503
+ delete = subparsers.add_parser("delete", help="Delete an existing preference by id")
504
+ add_common_repo_arg(delete)
505
+ delete.add_argument("--id", required=True)
506
+ delete.set_defaults(func=delete_preference)
507
+
508
+ list_cmd = subparsers.add_parser("list", help="List active preferences")
509
+ add_common_repo_arg(list_cmd)
510
+ list_cmd.set_defaults(func=list_preferences)
511
+
512
+ return parser
513
+
514
+
515
+ def main(argv: list[str]) -> int:
516
+ parser = build_parser()
517
+ args = parser.parse_args(argv)
518
+ try:
519
+ result = args.func(args)
520
+ except StorageError as exc:
521
+ print(json.dumps({"ok": False, "error": str(exc)}, ensure_ascii=False), file=sys.stderr)
522
+ return 1
523
+ print(json.dumps(result, ensure_ascii=False, indent=2))
524
+ return 0
525
+
526
+
527
+ if __name__ == "__main__":
528
+ raise SystemExit(main(sys.argv[1:]))