openmymemo 0.2.4__tar.gz

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 (179) hide show
  1. openmymemo-0.2.4/.agents/plugins/marketplace.json +12 -0
  2. openmymemo-0.2.4/.claude-plugin/marketplace.json +14 -0
  3. openmymemo-0.2.4/.claude-plugin/plugin.json +39 -0
  4. openmymemo-0.2.4/.github/dependabot.yml +11 -0
  5. openmymemo-0.2.4/.github/workflows/ci.yml +75 -0
  6. openmymemo-0.2.4/.github/workflows/release.yml +202 -0
  7. openmymemo-0.2.4/.gitignore +227 -0
  8. openmymemo-0.2.4/AGENTS.md +72 -0
  9. openmymemo-0.2.4/LICENSE +201 -0
  10. openmymemo-0.2.4/PKG-INFO +229 -0
  11. openmymemo-0.2.4/PRIVACY.md +97 -0
  12. openmymemo-0.2.4/README.md +194 -0
  13. openmymemo-0.2.4/SECURITY.md +26 -0
  14. openmymemo-0.2.4/desktop/package.json +14 -0
  15. openmymemo-0.2.4/desktop/pnpm-lock.yaml +135 -0
  16. openmymemo-0.2.4/desktop/splash/icon.png +0 -0
  17. openmymemo-0.2.4/desktop/splash/index.html +82 -0
  18. openmymemo-0.2.4/desktop/src-tauri/.gitignore +7 -0
  19. openmymemo-0.2.4/desktop/src-tauri/Cargo.lock +5200 -0
  20. openmymemo-0.2.4/desktop/src-tauri/Cargo.toml +33 -0
  21. openmymemo-0.2.4/desktop/src-tauri/build.rs +16 -0
  22. openmymemo-0.2.4/desktop/src-tauri/capabilities/default.json +17 -0
  23. openmymemo-0.2.4/desktop/src-tauri/icons/128x128.png +0 -0
  24. openmymemo-0.2.4/desktop/src-tauri/icons/128x128@2x.png +0 -0
  25. openmymemo-0.2.4/desktop/src-tauri/icons/32x32.png +0 -0
  26. openmymemo-0.2.4/desktop/src-tauri/icons/app-icon.png +0 -0
  27. openmymemo-0.2.4/desktop/src-tauri/icons/icon.icns +0 -0
  28. openmymemo-0.2.4/desktop/src-tauri/icons/icon.ico +0 -0
  29. openmymemo-0.2.4/desktop/src-tauri/icons/icon.png +0 -0
  30. openmymemo-0.2.4/desktop/src-tauri/icons/tray.png +0 -0
  31. openmymemo-0.2.4/desktop/src-tauri/src/core.rs +898 -0
  32. openmymemo-0.2.4/desktop/src-tauri/src/install.rs +338 -0
  33. openmymemo-0.2.4/desktop/src-tauri/src/launcher.rs +386 -0
  34. openmymemo-0.2.4/desktop/src-tauri/src/main.rs +950 -0
  35. openmymemo-0.2.4/desktop/src-tauri/tauri.conf.json +33 -0
  36. openmymemo-0.2.4/docs/MEMORY_FORMAT.md +446 -0
  37. openmymemo-0.2.4/docs/distribution.md +52 -0
  38. openmymemo-0.2.4/docs/images/panel-changes.png +0 -0
  39. openmymemo-0.2.4/docs/images/panel-memories-dark.png +0 -0
  40. openmymemo-0.2.4/docs/images/panel-memories-light.png +0 -0
  41. openmymemo-0.2.4/docs/product-design.md +595 -0
  42. openmymemo-0.2.4/gemini-extension.json +26 -0
  43. openmymemo-0.2.4/integrations/claude-code/hooks/hooks.json +19 -0
  44. openmymemo-0.2.4/integrations/claude-desktop/.mcpbignore +7 -0
  45. openmymemo-0.2.4/integrations/claude-desktop/manifest.json +68 -0
  46. openmymemo-0.2.4/integrations/codex/setup.sh +10 -0
  47. openmymemo-0.2.4/integrations/gemini/GEMINI.md +20 -0
  48. openmymemo-0.2.4/integrations/hooks/session-start.sh +10 -0
  49. openmymemo-0.2.4/integrations/icons/icon-128.png +0 -0
  50. openmymemo-0.2.4/integrations/icons/icon-16.png +0 -0
  51. openmymemo-0.2.4/integrations/icons/icon-32.png +0 -0
  52. openmymemo-0.2.4/integrations/icons/icon-48.png +0 -0
  53. openmymemo-0.2.4/integrations/icons/icon-512.png +0 -0
  54. openmymemo-0.2.4/llms-install.md +40 -0
  55. openmymemo-0.2.4/mcp.json +11 -0
  56. openmymemo-0.2.4/plugin.json +31 -0
  57. openmymemo-0.2.4/pyproject.toml +72 -0
  58. openmymemo-0.2.4/scripts/build_mcpb.py +51 -0
  59. openmymemo-0.2.4/scripts/demo_vault.py +84 -0
  60. openmymemo-0.2.4/scripts/make_icons.py +68 -0
  61. openmymemo-0.2.4/scripts/stage_desktop.py +51 -0
  62. openmymemo-0.2.4/server.json +65 -0
  63. openmymemo-0.2.4/skills/codex-setup/SKILL.md +22 -0
  64. openmymemo-0.2.4/skills/memory/SKILL.md +14 -0
  65. openmymemo-0.2.4/skills/remember/SKILL.md +15 -0
  66. openmymemo-0.2.4/src/openmymemo/__init__.py +3 -0
  67. openmymemo-0.2.4/src/openmymemo/__main__.py +3 -0
  68. openmymemo-0.2.4/src/openmymemo/appfiles.py +87 -0
  69. openmymemo-0.2.4/src/openmymemo/cli.py +905 -0
  70. openmymemo-0.2.4/src/openmymemo/clients.py +1018 -0
  71. openmymemo-0.2.4/src/openmymemo/cloud.py +1382 -0
  72. openmymemo-0.2.4/src/openmymemo/codex.py +147 -0
  73. openmymemo-0.2.4/src/openmymemo/connectors.py +246 -0
  74. openmymemo-0.2.4/src/openmymemo/context.py +268 -0
  75. openmymemo-0.2.4/src/openmymemo/mcp_server.py +249 -0
  76. openmymemo-0.2.4/src/openmymemo/mdfile.py +371 -0
  77. openmymemo-0.2.4/src/openmymemo/proposals.py +474 -0
  78. openmymemo-0.2.4/src/openmymemo/snippets.py +93 -0
  79. openmymemo-0.2.4/src/openmymemo/static/assets/index-B3p4vYgC.js +2 -0
  80. openmymemo-0.2.4/src/openmymemo/static/assets/index-Bh2BAn9N.css +1 -0
  81. openmymemo-0.2.4/src/openmymemo/static/index.html +14 -0
  82. openmymemo-0.2.4/src/openmymemo/static/logo.png +0 -0
  83. openmymemo-0.2.4/src/openmymemo/textutil.py +130 -0
  84. openmymemo-0.2.4/src/openmymemo/topics.py +195 -0
  85. openmymemo-0.2.4/src/openmymemo/transfer.py +273 -0
  86. openmymemo-0.2.4/src/openmymemo/vault.py +875 -0
  87. openmymemo-0.2.4/src/openmymemo/web.py +666 -0
  88. openmymemo-0.2.4/tests/cloud_fake.py +277 -0
  89. openmymemo-0.2.4/tests/conftest.py +18 -0
  90. openmymemo-0.2.4/tests/fixtures/cloud/README.md +10 -0
  91. openmymemo-0.2.4/tests/fixtures/cloud/bridge/ack.json +5 -0
  92. openmymemo-0.2.4/tests/fixtures/cloud/bridge/bye.json +5 -0
  93. openmymemo-0.2.4/tests/fixtures/cloud/bridge/call.json +16 -0
  94. openmymemo-0.2.4/tests/fixtures/cloud/bridge/call_propose.json +20 -0
  95. openmymemo-0.2.4/tests/fixtures/cloud/bridge/close_codes.json +12 -0
  96. openmymemo-0.2.4/tests/fixtures/cloud/bridge/handshake.json +16 -0
  97. openmymemo-0.2.4/tests/fixtures/cloud/bridge/hello.json +306 -0
  98. openmymemo-0.2.4/tests/fixtures/cloud/bridge/item.json +12 -0
  99. openmymemo-0.2.4/tests/fixtures/cloud/bridge/ping.json +4 -0
  100. openmymemo-0.2.4/tests/fixtures/cloud/bridge/pong.json +4 -0
  101. openmymemo-0.2.4/tests/fixtures/cloud/bridge/result_error.json +8 -0
  102. openmymemo-0.2.4/tests/fixtures/cloud/bridge/result_ok.json +17 -0
  103. openmymemo-0.2.4/tests/fixtures/cloud/bridge/welcome.json +21 -0
  104. openmymemo-0.2.4/tests/fixtures/cloud/hpke_vector.json +32 -0
  105. openmymemo-0.2.4/tests/fixtures/cloud/link/authorize_url.json +4 -0
  106. openmymemo-0.2.4/tests/fixtures/cloud/link/request.json +8 -0
  107. openmymemo-0.2.4/tests/fixtures/cloud/link/response.json +13 -0
  108. openmymemo-0.2.4/tests/fixtures/cloud/link/unlink.json +10 -0
  109. openmymemo-0.2.4/tests/test_cli.py +323 -0
  110. openmymemo-0.2.4/tests/test_clients.py +875 -0
  111. openmymemo-0.2.4/tests/test_cloud.py +1168 -0
  112. openmymemo-0.2.4/tests/test_codex_setup.py +121 -0
  113. openmymemo-0.2.4/tests/test_format.py +270 -0
  114. openmymemo-0.2.4/tests/test_log.py +112 -0
  115. openmymemo-0.2.4/tests/test_mcp.py +185 -0
  116. openmymemo-0.2.4/tests/test_mdfile.py +105 -0
  117. openmymemo-0.2.4/tests/test_onboarded.py +83 -0
  118. openmymemo-0.2.4/tests/test_packaging.py +115 -0
  119. openmymemo-0.2.4/tests/test_proposals.py +366 -0
  120. openmymemo-0.2.4/tests/test_transfer_context.py +281 -0
  121. openmymemo-0.2.4/tests/test_vault.py +158 -0
  122. openmymemo-0.2.4/tests/test_vault_concurrency.py +39 -0
  123. openmymemo-0.2.4/tests/test_web.py +290 -0
  124. openmymemo-0.2.4/uv.lock +1194 -0
  125. openmymemo-0.2.4/web/index.html +13 -0
  126. openmymemo-0.2.4/web/package.json +27 -0
  127. openmymemo-0.2.4/web/pnpm-lock.yaml +1685 -0
  128. openmymemo-0.2.4/web/public/logo.png +0 -0
  129. openmymemo-0.2.4/web/src/App.test.ts +81 -0
  130. openmymemo-0.2.4/web/src/App.vue +245 -0
  131. openmymemo-0.2.4/web/src/api.ts +396 -0
  132. openmymemo-0.2.4/web/src/components/AppIcon.vue +23 -0
  133. openmymemo-0.2.4/web/src/components/AppToast.vue +21 -0
  134. openmymemo-0.2.4/web/src/components/ApprovalSwitch.vue +21 -0
  135. openmymemo-0.2.4/web/src/components/CloudRow.test.ts +221 -0
  136. openmymemo-0.2.4/web/src/components/CloudRow.vue +279 -0
  137. openmymemo-0.2.4/web/src/components/CloudSettings.test.ts +101 -0
  138. openmymemo-0.2.4/web/src/components/CloudSettings.vue +107 -0
  139. openmymemo-0.2.4/web/src/components/ConnectApps.test.ts +186 -0
  140. openmymemo-0.2.4/web/src/components/ConnectApps.vue +351 -0
  141. openmymemo-0.2.4/web/src/components/ConnectNote.vue +55 -0
  142. openmymemo-0.2.4/web/src/components/ConnectorPanel.vue +210 -0
  143. openmymemo-0.2.4/web/src/components/DesktopSidebar.vue +142 -0
  144. openmymemo-0.2.4/web/src/components/DesktopToolbar.vue +38 -0
  145. openmymemo-0.2.4/web/src/components/DigestRow.vue +136 -0
  146. openmymemo-0.2.4/web/src/components/DropMenu.vue +64 -0
  147. openmymemo-0.2.4/web/src/components/HistoryDialog.vue +48 -0
  148. openmymemo-0.2.4/web/src/components/ImportPaste.vue +83 -0
  149. openmymemo-0.2.4/web/src/components/MemoryInspector.test.ts +220 -0
  150. openmymemo-0.2.4/web/src/components/MemoryInspector.vue +262 -0
  151. openmymemo-0.2.4/web/src/components/MemoryRow.vue +146 -0
  152. openmymemo-0.2.4/web/src/components/ModalDialog.vue +44 -0
  153. openmymemo-0.2.4/web/src/components/NewTopicDialog.vue +45 -0
  154. openmymemo-0.2.4/web/src/components/PageHeader.vue +25 -0
  155. openmymemo-0.2.4/web/src/components/ProposalCard.vue +112 -0
  156. openmymemo-0.2.4/web/src/components/SettingsSheet.test.ts +19 -0
  157. openmymemo-0.2.4/web/src/components/SettingsSheet.vue +150 -0
  158. openmymemo-0.2.4/web/src/components/SnippetTabs.vue +44 -0
  159. openmymemo-0.2.4/web/src/components/SortDialog.vue +106 -0
  160. openmymemo-0.2.4/web/src/desktop.ts +76 -0
  161. openmymemo-0.2.4/web/src/i18n.ts +577 -0
  162. openmymemo-0.2.4/web/src/icons.ts +33 -0
  163. openmymemo-0.2.4/web/src/main.ts +8 -0
  164. openmymemo-0.2.4/web/src/memoryList.ts +87 -0
  165. openmymemo-0.2.4/web/src/memorySaves.ts +54 -0
  166. openmymemo-0.2.4/web/src/store.ts +107 -0
  167. openmymemo-0.2.4/web/src/style.css +218 -0
  168. openmymemo-0.2.4/web/src/views/ActivityView.vue +62 -0
  169. openmymemo-0.2.4/web/src/views/ChangesView.vue +135 -0
  170. openmymemo-0.2.4/web/src/views/ConnectorsView.test.ts +113 -0
  171. openmymemo-0.2.4/web/src/views/ConnectorsView.vue +59 -0
  172. openmymemo-0.2.4/web/src/views/ContextView.vue +51 -0
  173. openmymemo-0.2.4/web/src/views/ImportView.vue +103 -0
  174. openmymemo-0.2.4/web/src/views/MemoriesDesktopView.vue +304 -0
  175. openmymemo-0.2.4/web/src/views/MemoriesView.vue +276 -0
  176. openmymemo-0.2.4/web/src/views/WelcomeView.test.ts +328 -0
  177. openmymemo-0.2.4/web/src/views/WelcomeView.vue +409 -0
  178. openmymemo-0.2.4/web/tsconfig.json +17 -0
  179. openmymemo-0.2.4/web/vite.config.ts +31 -0
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "openmymemo",
3
+ "interface": { "displayName": "OpenMyMemo" },
4
+ "plugins": [
5
+ {
6
+ "name": "openmymemo",
7
+ "source": { "source": "local", "path": "./" },
8
+ "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" },
9
+ "category": "Productivity"
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "openmymemo",
3
+ "description": "OpenMyMemo: a local, user-owned AI memory for Claude Code.",
4
+ "owner": { "name": "MyMemo", "url": "https://github.com/MyMemoAI/openmymemo" },
5
+ "plugins": [
6
+ {
7
+ "name": "openmymemo",
8
+ "source": ".",
9
+ "description": "Start every session with your memory; what Claude writes shows up in a digest you can undo.",
10
+ "category": "productivity",
11
+ "tags": ["memory", "mcp", "local-first"]
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "openmymemo",
3
+ "displayName": "OpenMyMemo",
4
+ "version": "0.2.4",
5
+ "description": "Your AI memory as a local folder you own. Claude starts each session with it and writes what it learns; you see every change and can undo it.",
6
+ "author": {
7
+ "name": "MyMemo",
8
+ "url": "https://github.com/MyMemoAI/openmymemo"
9
+ },
10
+ "homepage": "https://github.com/MyMemoAI/openmymemo",
11
+ "repository": "https://github.com/MyMemoAI/openmymemo",
12
+ "license": "Apache-2.0",
13
+ "keywords": [
14
+ "memory",
15
+ "local-first",
16
+ "mcp",
17
+ "privacy"
18
+ ],
19
+ "mcpServers": {
20
+ "openmymemo": {
21
+ "command": "uv",
22
+ "args": [
23
+ "run",
24
+ "--directory",
25
+ "${CLAUDE_PLUGIN_ROOT}",
26
+ "--frozen", "--no-dev",
27
+ "--quiet",
28
+ "openmymemo",
29
+ "mcp",
30
+ "--connector",
31
+ "claude-code",
32
+ "--init",
33
+ "--panel-port",
34
+ "8787"
35
+ ]
36
+ }
37
+ },
38
+ "hooks": "./integrations/claude-code/hooks/hooks.json"
39
+ }
@@ -0,0 +1,11 @@
1
+ # Security fixes for Python and panel dependencies come from Dependabot security updates (repo setting).
2
+ # This file only keeps the GitHub Actions used by the workflows up to date, in one monthly PR.
3
+ version: 2
4
+ updates:
5
+ - package-ecosystem: github-actions
6
+ directory: /
7
+ schedule:
8
+ interval: monthly
9
+ groups:
10
+ actions:
11
+ patterns: ["*"]
@@ -0,0 +1,75 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ # Three jobs, one per kind of runner: Linux tests the oldest and newest Python we support, macOS tests the
16
+ # middle one together with the Mac app, and the panel job checks the committed build and packs the extension.
17
+ jobs:
18
+ linux:
19
+ name: Python tests (Linux, 3.11 and 3.13)
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v7
23
+ - uses: astral-sh/setup-uv@v7
24
+ - run: uv run --frozen --python 3.13 ruff check src tests scripts
25
+ - name: Tests on Python 3.11
26
+ run: uv run --frozen --python 3.11 pytest
27
+ - name: Tests on Python 3.13
28
+ run: uv run --frozen --python 3.13 pytest
29
+
30
+ macos:
31
+ name: macOS tests and Mac app (Python 3.12)
32
+ runs-on: macos-latest
33
+ steps:
34
+ - uses: actions/checkout@v7
35
+ - uses: astral-sh/setup-uv@v7
36
+ with:
37
+ python-version: "3.12"
38
+ - run: uv run --frozen pytest
39
+ - run: python3 scripts/stage_desktop.py # the bundled core and uv, which the Tauri build checks for
40
+ - run: cargo test --locked
41
+ working-directory: desktop/src-tauri
42
+
43
+ panel:
44
+ name: Panel build and extension
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v7
48
+ - uses: pnpm/action-setup@v6
49
+ with:
50
+ version: 10
51
+ - uses: actions/setup-node@v7
52
+ with:
53
+ node-version: 22
54
+ cache: pnpm
55
+ cache-dependency-path: web/pnpm-lock.yaml
56
+ - uses: astral-sh/setup-uv@v7
57
+ - run: pnpm install --frozen-lockfile
58
+ working-directory: web
59
+ - run: pnpm build # type-checks with vue-tsc first
60
+ working-directory: web
61
+ - run: pnpm test
62
+ working-directory: web
63
+ - name: src/openmymemo/static matches web/
64
+ run: |
65
+ if [ -n "$(git status --porcelain src/openmymemo/static)" ]; then
66
+ git status --short src/openmymemo/static
67
+ echo "::error::The committed panel build is out of date. Run 'cd web && pnpm build' and commit src/openmymemo/static."
68
+ exit 1
69
+ fi
70
+ - run: uv run --frozen python scripts/build_mcpb.py # validates the manifest
71
+ - uses: actions/upload-artifact@v7
72
+ with:
73
+ name: openmymemo-mcpb
74
+ path: dist/*.mcpb
75
+ if-no-files-found: error
@@ -0,0 +1,202 @@
1
+ name: Release
2
+
3
+ # Push a tag named after the version (v0.2.2) to publish the Mac app, the Claude Desktop extension and the
4
+ # Python package as a GitHub release, the Python package on PyPI, and server.json on the MCP Registry. The version
5
+ # must already be bumped everywhere (tests/test_packaging.py).
6
+ # The release is created once every file is built, so it never lacks the disk image; if a job fails, nothing
7
+ # is published and "Re-run failed jobs" picks up from there.
8
+ on:
9
+ push:
10
+ tags: ["v*"]
11
+
12
+ # Only the job that publishes can write; the Mac build runs many build scripts next to the signing secrets.
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ mac:
18
+ name: Mac app
19
+ # Builds OpenMyMemo-<version>-arm64.dmg. With these repository secrets the app is signed with a Developer
20
+ # ID and notarized; without them (a fork) it is signed ad hoc, and macOS asks once to open it (README).
21
+ # APPLE_CERTIFICATE the "Developer ID Application" certificate and its key as a .p12, base64
22
+ # APPLE_CERTIFICATE_PASSWORD the .p12's password
23
+ # APPLE_SIGNING_IDENTITY the certificate's name: "Developer ID Application: <Company> (<Team ID>)"
24
+ # APPLE_API_ISSUER notarization with an App Store Connect API key: the issuer id,
25
+ # APPLE_API_KEY_ID the key id,
26
+ # APPLE_API_PRIVATE_KEY and the text of the key's .p8 file
27
+ # APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID
28
+ # optional: notarization with an Apple ID and an app-specific password,
29
+ # used only when the API key secrets are absent
30
+ # Tauri reads the APPLE_* variables itself (tauri-apps/tauri, crates/tauri-bundler/src/bundle/macos/sign.rs):
31
+ # it imports the certificate into a temporary keychain, and APPLE_SIGNING_IDENTITY overrides the ad hoc
32
+ # "signingIdentity": "-" in tauri.conf.json (crates/tauri-cli/src/interface/rust.rs).
33
+ runs-on: macos-latest
34
+ permissions:
35
+ contents: read
36
+ steps:
37
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
38
+ with:
39
+ persist-credentials: false
40
+ - name: The tag matches the version
41
+ run: |
42
+ version=$(sed -n 's/^version = "\(.*\)"$/\1/p' pyproject.toml | head -n 1)
43
+ if [ "$GITHUB_REF_NAME" != "v$version" ]; then
44
+ echo "::error::Tag $GITHUB_REF_NAME doesn't match version $version in pyproject.toml."
45
+ exit 1
46
+ fi
47
+ echo "VERSION=$version" >> "$GITHUB_ENV"
48
+ - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7; stage_desktop.py bundles this uv
49
+ - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
50
+ with:
51
+ version: 10
52
+ - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
53
+ with:
54
+ node-version: 22
55
+ cache: pnpm
56
+ cache-dependency-path: desktop/pnpm-lock.yaml
57
+ - run: pnpm install --frozen-lockfile
58
+ working-directory: desktop
59
+ - name: Build the app and the disk image
60
+ working-directory: desktop
61
+ env:
62
+ # CI=true also makes Tauri skip the Finder scripting that lays out the disk image window.
63
+ CI: "true"
64
+ APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
65
+ APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
66
+ APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
67
+ APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
68
+ APPLE_API_KEY: ${{ secrets.APPLE_API_KEY_ID }}
69
+ APPLE_API_PRIVATE_KEY: ${{ secrets.APPLE_API_PRIVATE_KEY }}
70
+ APPLE_ID: ${{ secrets.APPLE_ID }}
71
+ APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
72
+ APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
73
+ run: |
74
+ # A missing secret arrives as an empty variable, which Tauri would take as set.
75
+ for name in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY APPLE_API_ISSUER \
76
+ APPLE_API_KEY APPLE_API_PRIVATE_KEY APPLE_ID APPLE_PASSWORD APPLE_TEAM_ID; do
77
+ if [ -z "${!name}" ]; then unset "$name"; fi
78
+ done
79
+ if [ -z "${APPLE_CERTIFICATE:-}" ] || [ -z "${APPLE_CERTIFICATE_PASSWORD:-}" ] || [ -z "${APPLE_SIGNING_IDENTITY:-}" ]; then
80
+ echo "::warning::No Developer ID certificate in the secrets: the app is signed ad hoc and not notarized."
81
+ unset APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY APPLE_API_ISSUER APPLE_API_KEY \
82
+ APPLE_ID APPLE_PASSWORD APPLE_TEAM_ID
83
+ elif [ -n "${APPLE_API_PRIVATE_KEY:-}" ] && [ -n "${APPLE_API_KEY:-}" ] && [ -n "${APPLE_API_ISSUER:-}" ]; then
84
+ # Tauri would use APPLE_ID first if it were set; the API key is the one to use.
85
+ unset APPLE_ID APPLE_PASSWORD
86
+ export APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey_$APPLE_API_KEY.p8"
87
+ ( umask 077 && printf '%s\n' "$APPLE_API_PRIVATE_KEY" > "$APPLE_API_KEY_PATH" )
88
+ elif [ -z "${APPLE_ID:-}" ] || [ -z "${APPLE_PASSWORD:-}" ] || [ -z "${APPLE_TEAM_ID:-}" ]; then
89
+ echo "::warning::No notarization credentials in the secrets: the app is signed but not notarized."
90
+ unset APPLE_ID APPLE_PASSWORD APPLE_API_ISSUER APPLE_API_KEY
91
+ fi
92
+ unset APPLE_API_PRIVATE_KEY
93
+ pnpm tauri build --bundles app,dmg
94
+ - name: Check the signature and name the disk image
95
+ run: |
96
+ bundle=desktop/src-tauri/target/release/bundle
97
+ codesign --verify --deep --strict --verbose=2 "$bundle/macos/OpenMyMemo.app"
98
+ codesign -dv "$bundle/macos/OpenMyMemo.app" 2>&1 | grep -E '^(Authority|Signature|TeamIdentifier)=' || true
99
+ mkdir -p dist
100
+ cp "$bundle/dmg/OpenMyMemo_${VERSION}_aarch64.dmg" "dist/OpenMyMemo-${VERSION}-arm64.dmg"
101
+ hdiutil verify "dist/OpenMyMemo-${VERSION}-arm64.dmg"
102
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
103
+ with:
104
+ name: openmymemo-dmg
105
+ path: dist/*.dmg
106
+ if-no-files-found: error
107
+
108
+ release:
109
+ needs: mac
110
+ runs-on: ubuntu-latest
111
+ permissions:
112
+ contents: write # gh release create
113
+ steps:
114
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
115
+ with:
116
+ persist-credentials: false
117
+ - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
118
+ - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
119
+ with:
120
+ node-version: 22
121
+ - name: The tag matches the version
122
+ run: |
123
+ version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
124
+ if [ "$GITHUB_REF_NAME" != "v$version" ]; then
125
+ echo "::error::Tag $GITHUB_REF_NAME doesn't match version $version in pyproject.toml."
126
+ exit 1
127
+ fi
128
+ - run: uv sync --frozen
129
+ - run: uv run pytest
130
+ - run: uv run python scripts/build_mcpb.py
131
+ - run: uv build
132
+ - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
133
+ with:
134
+ name: openmymemo-dmg
135
+ path: dist
136
+ - name: Publish the release
137
+ env:
138
+ GH_TOKEN: ${{ github.token }}
139
+ run: |
140
+ gh release create "$GITHUB_REF_NAME" dist/*.dmg dist/*.mcpb dist/*.whl dist/*.tar.gz \
141
+ --title "OpenMyMemo ${GITHUB_REF_NAME#v}" --generate-notes
142
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
143
+ with:
144
+ name: openmymemo-python
145
+ path: |
146
+ dist/*.whl
147
+ dist/*.tar.gz
148
+ if-no-files-found: error
149
+
150
+ pypi:
151
+ # The same wheel and sdist as the GitHub release, uploaded with PyPI's trusted publishing: PyPI accepts this
152
+ # job's OIDC token, so there is no API token in the secrets. One-time setup on pypi.org: a trusted publisher
153
+ # for MyMemoAI/openmymemo, workflow release.yml, environment pypi. If it fails after the GitHub release is
154
+ # out, "Re-run failed jobs" runs only this job. PyPI never takes the same version twice.
155
+ needs: release
156
+ runs-on: ubuntu-latest
157
+ environment:
158
+ name: pypi
159
+ url: https://pypi.org/project/openmymemo/
160
+ permissions:
161
+ id-token: write
162
+ steps:
163
+ - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
164
+ - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
165
+ with:
166
+ name: openmymemo-python
167
+ path: dist
168
+ - run: uv publish --trusted-publishing always dist/*
169
+
170
+ mcp-registry:
171
+ # Lists this version on the official MCP Registry (registry.modelcontextprotocol.io), which MCP clients and
172
+ # other directories read. The registry stores only server.json: it checks that the PyPI package of that
173
+ # version exists and that its README names this server ("mcp-name:" near the top of README.md), so it runs
174
+ # after PyPI. GitHub OIDC proves the namespace io.github.MyMemoAI, so there is no token in the secrets.
175
+ needs: pypi
176
+ runs-on: ubuntu-latest
177
+ permissions:
178
+ contents: read
179
+ id-token: write
180
+ steps:
181
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
182
+ with:
183
+ persist-credentials: false
184
+ - name: Install mcp-publisher
185
+ env:
186
+ VERSION: v1.8.1
187
+ SHA256: a06c9096dcb9727c13555b6be26c7effa707b01f06a4c561ba7a3635443cf2cc
188
+ run: |
189
+ curl -fsSL -o mcp-publisher.tar.gz \
190
+ "https://github.com/modelcontextprotocol/registry/releases/download/$VERSION/mcp-publisher_linux_amd64.tar.gz"
191
+ echo "$SHA256 mcp-publisher.tar.gz" | sha256sum --check
192
+ tar xzf mcp-publisher.tar.gz mcp-publisher
193
+ - run: ./mcp-publisher validate
194
+ - run: ./mcp-publisher login github-oidc
195
+ - name: Publish server.json
196
+ run: |
197
+ # PyPI can take a minute to serve a version it has just accepted.
198
+ for wait in 0 30 60 120; do
199
+ sleep "$wait"
200
+ if ./mcp-publisher publish; then exit 0; fi
201
+ done
202
+ exit 1
@@ -0,0 +1,227 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ /build/
12
+ /develop-eggs/
13
+ /dist/
14
+ /downloads/
15
+ /eggs/
16
+ /.eggs/
17
+ /lib/
18
+ /lib64/
19
+ /parts/
20
+ /sdist/
21
+ /var/
22
+ /wheels/
23
+ /share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ *.lcov
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ # Pipfile.lock
97
+
98
+ # uv
99
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # uv.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ # poetry.lock
110
+ # poetry.toml
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
115
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
116
+ # pdm.lock
117
+ # pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # pixi
122
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
123
+ # pixi.lock
124
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
125
+ # in the .venv directory. It is recommended not to include this directory in version control.
126
+ .pixi/*
127
+ !.pixi/config.toml
128
+
129
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130
+ __pypackages__/
131
+
132
+ # Celery stuff
133
+ celerybeat-schedule*
134
+ celerybeat.pid
135
+
136
+ # Redis
137
+ *.rdb
138
+ *.aof
139
+ *.pid
140
+
141
+ # RabbitMQ
142
+ mnesia/
143
+ rabbitmq/
144
+ rabbitmq-data/
145
+
146
+ # ActiveMQ
147
+ activemq-data/
148
+
149
+ # SageMath parsed files
150
+ *.sage.py
151
+
152
+ # Environments
153
+ .env
154
+ .envrc
155
+ .venv
156
+ env/
157
+ venv/
158
+ ENV/
159
+ env.bak/
160
+ venv.bak/
161
+
162
+ # Spyder project settings
163
+ .spyderproject
164
+ .spyproject
165
+
166
+ # Rope project settings
167
+ .ropeproject
168
+
169
+ # mkdocs/Zensical documentation
170
+ /site
171
+
172
+ # mypy
173
+ .mypy_cache/
174
+ .dmypy.json
175
+ dmypy.json
176
+
177
+ # Pyre type checker
178
+ .pyre/
179
+
180
+ # pytype static type analyzer
181
+ .pytype/
182
+
183
+ # Cython debug symbols
184
+ cython_debug/
185
+
186
+ # PyCharm
187
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
188
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
189
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
190
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
191
+ # .idea/
192
+
193
+ # Abstra
194
+ # Abstra is an AI-powered process automation framework.
195
+ # Ignore directories containing user credentials, local state, and settings.
196
+ # Learn more at https://abstra.io/docs
197
+ .abstra/
198
+
199
+ # Visual Studio Code
200
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
201
+ # can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
202
+ # and can be added to the global gitignore or merged into this file. However, if you prefer, you
203
+ # could uncomment the following to ignore the entire vscode folder
204
+ # .vscode/
205
+ # Temporary file for partial code execution
206
+ tempCodeRunnerFile.py
207
+
208
+ # Ruff stuff:
209
+ .ruff_cache/
210
+
211
+ # PyPI configuration file
212
+ .pypirc
213
+
214
+ # Marimo
215
+ marimo/_static/
216
+ marimo/_lsp/
217
+ __marimo__/
218
+
219
+ # Streamlit
220
+ .streamlit/secrets.toml
221
+
222
+ # OpenMyMemo
223
+ node_modules/
224
+ web/.vite/
225
+
226
+ # Agent worktrees created by Claude Code sessions
227
+ .claude/worktrees/
@@ -0,0 +1,72 @@
1
+ # OpenMyMemo — agent guide
2
+
3
+ A local-first, open-source AI memory folder with an approval panel. [docs/product-design.md](./docs/product-design.md) is the product definition; if an implementation would contradict it, update the design doc first (§12 and §13 record where v0.1 and v0.2 differ from earlier plans).
4
+
5
+ ## Non-negotiables
6
+
7
+ - Local only: no cloud storage, sync, accounts or telemetry, except the optional MyMemo Cloud connector (product-design.md §16), which does nothing until the person signs in and never holds memory in readable form at rest. The server listens on `127.0.0.1` only.
8
+ - All user data is plain text: `memories/*.md` is the source of truth for memory content; metadata, history, activity, proposals and settings under `.openmymemo/` are plain text too. Don't introduce a database.
9
+ - Every AI write goes through a proposal (`proposals.py`) and never edits files directly. A change that applies without the user (the default, or a connector set to `auto`) must stay undoable, must never write into a private topic, and must never silently change a memory the user wrote last (`last_actor`); the table in product-design.md §5.5 is normative.
10
+ - Works with zero external dependencies: anything that needs a model endpoint is optional and degrades instead of failing.
11
+ - No dependency on MyMemo services, accounts or databases. The MyMemo Cloud connector is the one optional piece that talks to MyMemo; everything else works without it, and removing it changes nothing else. Code and UI patterns may be borrowed from MyMemo's own apps; other runtime coupling may not.
12
+ - Don't weaken the panel API guard (`guard` in `web.py`: Host, Origin, `X-OpenMyMemo` header) or the `/mcp` token check.
13
+
14
+ ## Commands
15
+
16
+ ```bash
17
+ uv sync
18
+ uv run pytest # all tests, including MCP end to end over stdio and real HTTP
19
+ uv run ruff check src tests scripts
20
+ uv run openmymemo --vault /tmp/v init # CLI
21
+ uv run python scripts/demo_vault.py /tmp/d # demo data
22
+ cd web && pnpm install && pnpm build # panel → src/openmymemo/static/ (commit the build output)
23
+ cd web && pnpm test # panel tests (vitest)
24
+ cd desktop && pnpm install && pnpm tauri dev # Mac app (needs Rust); `pnpm tauri build` for the .app
25
+ python3 scripts/stage_desktop.py && (cd desktop/src-tauri && cargo test) # Mac app tests
26
+ ```
27
+
28
+ ## Code map
29
+
30
+ | File | Responsibility |
31
+ | --- | --- |
32
+ | `src/openmymemo/mdfile.py` | Parse and rewrite topic files (frontmatter, sections, memory lines; format-1 header migration); untouched lines must survive byte for byte |
33
+ | `src/openmymemo/vault.py` | The vault: sync (editor edits → meta/history), add/update/archive/restore/rollback, settings, locking |
34
+ | `src/openmymemo/topics.py` | The 14 default topics, groups, classification keywords |
35
+ | `docs/MEMORY_FORMAT.md` | The folder format; change it before changing what the code reads or writes |
36
+ | `docs/distribution.md`, `llms-install.md` | Where OpenMyMemo is published and listed, the shared listing details, and the setup guide AI agents such as Cline follow |
37
+ | `src/openmymemo/textutil.py` | Similarity, search scoring, rule-based classification |
38
+ | `src/openmymemo/connectors.py` | Connector permissions, tokens, revocation tombstones |
39
+ | `src/openmymemo/clients.py` | The catalog of AI apps (product-design.md §15.1): detection, status, one-click connect and disconnect by editing an app's MCP config (JSON or Codex TOML) or running its `mcp add`, and the manual snippets |
40
+ | `src/openmymemo/appfiles.py` | Writing other apps' files (their MCP configs, Codex's `hooks.json`): atomic, permissions kept from the first byte, through symlinks, with a one-time `<file>.openmymemo-backup` |
41
+ | `src/openmymemo/proposals.py` | Proposal lifecycle, dedupe, auto-apply and undo |
42
+ | `src/openmymemo/context.py` | Compiles what an AI sees, MEMORY.md, log.md, search |
43
+ | `src/openmymemo/transfer.py` | Import (ChatGPT / Claude / Markdown / JSON) and export |
44
+ | `src/openmymemo/mcp_server.py` | MCP tools (mcp SDK 2.x `MCPServer`); `relayed=` builds the MyMemo Cloud variant |
45
+ | `src/openmymemo/web.py` | Panel API, `/mcp` and `/cloud/callback` combined into one ASGI app; `panel_server()` is the uvicorn server that starts MyMemo Cloud once its port is bound |
46
+ | `src/openmymemo/cloud.py` | MyMemo Cloud (product-design.md §16): the per-computer link (`cloud.json`, never in the folder), linking with PKCE and a loopback callback, and the bridge: one outbound WebSocket that answers relayed tool calls as `cloud-<slug>` connectors and files the offline queue (HPKE-sealed proposals) through `proposals.py`. The wire format is pinned by `tests/fixtures/cloud/` (the MyMemo service tests against the same files); `tests/cloud_fake.py` is a fake service built from it |
47
+ | `src/openmymemo/cli.py` | Typer CLI |
48
+ | `src/openmymemo/codex.py` | Codex session hook: merges it into `~/.codex/hooks.json` and takes it out again (Codex doesn't run plugin hooks) |
49
+ | `web/` | Vue 3 + Tailwind v4 panel source; all copy lives in `web/src/i18n.ts` |
50
+ | `web/src/components/ConnectApps.vue` | The app list with Connect / Disconnect / Set up, used by the Connectors page and the first-run guide; `ConnectNote` shows what's left to do, `ConnectorPanel` a connected app's permissions, `CloudRow` the MyMemo Cloud row (sign-in, status, setup for ChatGPT and claude.ai, its web apps) |
51
+ | `web/src/desktop.ts` | The Mac layout switch (`?desktop=1`), the shell's commands and shortcuts; `MemoriesDesktopView`, `MemoryInspector`, `DesktopSidebar`, `DesktopToolbar` are its screens; `memorySaves.ts` queues the inspector's saves per memory. `PageHeader` becomes the window toolbar there |
52
+ | `desktop/` | Mac app (Tauri 2, product-design.md §14, §15.2): `src-tauri/src/core.rs` installs the core (non-editable) and finds or starts the server for the folder, `launcher.rs` writes the `openmymemo` launcher AI apps run, `install.rs` moves the app from the disk image to Applications, `main.rs` has the window, menus, menu-bar item, notifications, settings and link handling; `scripts/stage_desktop.py` stages the core and `uv` before each build |
53
+ | `.claude-plugin/` | Claude Code plugin manifest and marketplace (the repo root is the plugin) |
54
+ | `plugin.json`, `mcp.json`, `.agents/plugins/` | Codex plugin (Agent Plugins format) and its marketplace |
55
+ | `skills/` | Skills shared by the Claude Code and Codex plugins |
56
+ | `integrations/hooks/` | Session-start hook script shared by Claude Code (plugin hook in `integrations/claude-code/hooks/hooks.json`) and Codex (user-level hook installed by `setup codex`; `integrations/codex/setup.sh` runs it from the installed plugin) |
57
+ | `integrations/claude-desktop/` | `.mcpb` manifest; build with `scripts/build_mcpb.py` |
58
+ | `integrations/icons/`, `web/public/logo.png` | The MyMemo logo at each size, rendered from MyMemo's app icon by `scripts/make_icons.py` |
59
+
60
+ ## Conventions
61
+
62
+ - Everything user-facing is English: docs, panel copy, CLI output, MCP tool names and descriptions. Chinese belongs only in data handling (classifier keywords, parsing pasted pages, test fixtures), because users' memories may be in Chinese.
63
+ - File formats (`memories/*.md`, `connectors.yaml`, `.openmymemo/*`) are a public contract specified in [docs/MEMORY_FORMAT.md](./docs/MEMORY_FORMAT.md): change the spec first and keep reading older files.
64
+ - Topic slugs are the file names; titles come from the files.
65
+ - All writes go through `Vault` methods, which lock, re-read from disk, write atomically and update meta and history.
66
+ - The mcp SDK is 2.x: `from mcp.server.mcpserver import MCPServer, Context`; static resources can't take a `Context`.
67
+ - Plugins run `uv run` from the installed copy of this repo, so anything the package needs at runtime must be inside the repo (and inside the `.mcpb` staging list in `scripts/build_mcpb.py`). Keep the version in `pyproject.toml`, `src/openmymemo/__init__.py`, both `plugin.json` files, `server.json` (the MCP Registry entry, both of its versions), the `.mcpb` manifest, `web/package.json` and the Mac app (`desktop/package.json`, `desktop/src-tauri/tauri.conf.json`, `desktop/src-tauri/Cargo.toml`) in sync (`tests/test_packaging.py` checks), and run `uv lock` after a bump.
68
+ - CI (`.github/workflows/ci.yml`) is three jobs, one per runner: the Python tests and lint on Linux (Python 3.11 and 3.13), the Python tests and the Mac app's tests on macOS (Python 3.12), and the panel (its tests, and that the committed build matches `web/`) with packing the `.mcpb`. The `main` ruleset requires these three checks by name, so renaming a job means updating it. To release: bump the version, merge, then push a tag `v<version>`; `.github/workflows/release.yml` publishes the `.mcpb`, wheel and sdist as a GitHub release, the wheel and sdist on PyPI (trusted publishing, environment `pypi`), and then `server.json` on the MCP Registry (GitHub OIDC; the registry checks the `mcp-name:` line in `README.md`, so keep it).
69
+ - Don't add a root `.mcp.json`: Claude Code would treat it as project config for everyone working on this repo. The Claude Code MCP server is declared inline in `.claude-plugin/plugin.json`.
70
+ - Commit messages are imperative English; one topic per PR.
71
+ - MyMemo Cloud is hidden until turned on in Settings (or `openmymemo cloud settings --show`), and listed while linked (product-design.md §16.11). It reads `OPENMYMEMO_CLOUD_URL` (the service; overrides the one chosen in Settings, default `https://api.mymemo.ai`) and `OPENMYMEMO_CLOUD_DIR` (the folder of `cloud.json` and `settings.json`). `tests/conftest.py` gives every test its own HOME, cloud folder and a local URL; set both yourself before trying `openmymemo cloud …` or the panel's sign-in by hand, so nothing touches your real link or MyMemo.
72
+ - The Mac app adds nothing to the panel's API: it is the same panel in another layout. New panel features go into the web screens; give the Mac layout its own version only where the design has one. A native command the panel calls must be listed in `desktop/src-tauri/build.rs` and granted in `capabilities/default.json`, because the panel is a remote origin to Tauri.