blumkin 0.2.0__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 (133) hide show
  1. blumkin-0.2.0/.cursor/rules/git-commit-identity.mdc +156 -0
  2. blumkin-0.2.0/.cursor/rules/lexicographic-code-organization.mdc +95 -0
  3. blumkin-0.2.0/.cursor/rules/local-live-graph-tests.mdc +33 -0
  4. blumkin-0.2.0/.cursor/rules/main-worktree-off-limits.mdc +38 -0
  5. blumkin-0.2.0/.cursor/rules/no-secret-exposure.mdc +46 -0
  6. blumkin-0.2.0/.cursor/rules/no-third-party-side-effects.mdc +71 -0
  7. blumkin-0.2.0/.cursor/rules/pr-ship-and-review.mdc +125 -0
  8. blumkin-0.2.0/.cursor/rules/pre-pr-checks.mdc +67 -0
  9. blumkin-0.2.0/.cursor/rules/read-agents-and-rules.mdc +21 -0
  10. blumkin-0.2.0/.cursor/rules/remote-timeouts-retries.mdc +66 -0
  11. blumkin-0.2.0/.cursor/rules/repo-practices-after-config-change.mdc +59 -0
  12. blumkin-0.2.0/.cursor/rules/stacking-tool.mdc +56 -0
  13. blumkin-0.2.0/.cursor/skills/blumkin/SKILL.md +241 -0
  14. blumkin-0.2.0/.github/CODEOWNERS +7 -0
  15. blumkin-0.2.0/.github/ci/assert-uv-lock-version +54 -0
  16. blumkin-0.2.0/.github/ci/pytest-hermetic +6 -0
  17. blumkin-0.2.0/.github/ci/python-static +11 -0
  18. blumkin-0.2.0/.github/ci/secret-scan +212 -0
  19. blumkin-0.2.0/.github/ci/setup-python +10 -0
  20. blumkin-0.2.0/.github/ci/shellcheck +37 -0
  21. blumkin-0.2.0/.github/copilot-instructions.md +10 -0
  22. blumkin-0.2.0/.github/dependabot.yml +24 -0
  23. blumkin-0.2.0/.github/secret-audit.json +1 -0
  24. blumkin-0.2.0/.github/stacking-tool +1 -0
  25. blumkin-0.2.0/.github/workflows/ci.yml +215 -0
  26. blumkin-0.2.0/.github/workflows/cleanup-branch-on-merge.yml +25 -0
  27. blumkin-0.2.0/.github/workflows/cleanup-merged-branches.yml +109 -0
  28. blumkin-0.2.0/.github/workflows/cve-check.yml +195 -0
  29. blumkin-0.2.0/.github/workflows/dependabot-auto-merge.yml +25 -0
  30. blumkin-0.2.0/.github/workflows/merged-pr-closer.yml +135 -0
  31. blumkin-0.2.0/.github/workflows/release-please.yml +216 -0
  32. blumkin-0.2.0/.gitignore +16 -0
  33. blumkin-0.2.0/.python-version +1 -0
  34. blumkin-0.2.0/.release-please-manifest.json +3 -0
  35. blumkin-0.2.0/AGENTS.md +172 -0
  36. blumkin-0.2.0/CHANGELOG.md +85 -0
  37. blumkin-0.2.0/CLAUDE.md +9 -0
  38. blumkin-0.2.0/HANDOFF.md +114 -0
  39. blumkin-0.2.0/LICENSE +21 -0
  40. blumkin-0.2.0/PKG-INFO +235 -0
  41. blumkin-0.2.0/PLAN.md +552 -0
  42. blumkin-0.2.0/README.md +201 -0
  43. blumkin-0.2.0/RETROSPECTIVE-M1.md +79 -0
  44. blumkin-0.2.0/docs/RELEASING.md +67 -0
  45. blumkin-0.2.0/docs/agent-integration.md +409 -0
  46. blumkin-0.2.0/docs/google-setup.md +293 -0
  47. blumkin-0.2.0/pyproject.toml +85 -0
  48. blumkin-0.2.0/release-please-config.json +17 -0
  49. blumkin-0.2.0/scripts/embed_build_metadata +78 -0
  50. blumkin-0.2.0/scripts/verify-pypi-release +110 -0
  51. blumkin-0.2.0/src/blumkin/__init__.py +9 -0
  52. blumkin-0.2.0/src/blumkin/__main__.py +8 -0
  53. blumkin-0.2.0/src/blumkin/_build_metadata.py +6 -0
  54. blumkin-0.2.0/src/blumkin/attachments.py +98 -0
  55. blumkin-0.2.0/src/blumkin/auth.py +373 -0
  56. blumkin-0.2.0/src/blumkin/cli.py +2737 -0
  57. blumkin-0.2.0/src/blumkin/config.py +592 -0
  58. blumkin-0.2.0/src/blumkin/exit_codes.py +10 -0
  59. blumkin-0.2.0/src/blumkin/graph.py +50 -0
  60. blumkin-0.2.0/src/blumkin/help_text.py +722 -0
  61. blumkin-0.2.0/src/blumkin/output.py +53 -0
  62. blumkin-0.2.0/src/blumkin/pipx_install.py +64 -0
  63. blumkin-0.2.0/src/blumkin/providers/__init__.py +56 -0
  64. blumkin-0.2.0/src/blumkin/providers/google/__init__.py +1 -0
  65. blumkin-0.2.0/src/blumkin/providers/google/calendar.py +540 -0
  66. blumkin-0.2.0/src/blumkin/providers/google/chat.py +584 -0
  67. blumkin-0.2.0/src/blumkin/providers/google/mail.py +640 -0
  68. blumkin-0.2.0/src/blumkin/providers/google/mail_writes.py +717 -0
  69. blumkin-0.2.0/src/blumkin/providers/google/people.py +248 -0
  70. blumkin-0.2.0/src/blumkin/providers/google_auth.py +312 -0
  71. blumkin-0.2.0/src/blumkin/providers/google_http.py +72 -0
  72. blumkin-0.2.0/src/blumkin/providers/google_provider.py +445 -0
  73. blumkin-0.2.0/src/blumkin/providers/kind.py +29 -0
  74. blumkin-0.2.0/src/blumkin/providers/microsoft.py +468 -0
  75. blumkin-0.2.0/src/blumkin/providers/protocol.py +258 -0
  76. blumkin-0.2.0/src/blumkin/skills/__init__.py +882 -0
  77. blumkin-0.2.0/src/blumkin/skills/calendar.py +708 -0
  78. blumkin-0.2.0/src/blumkin/skills/calendar_writes.py +248 -0
  79. blumkin-0.2.0/src/blumkin/skills/chat.py +826 -0
  80. blumkin-0.2.0/src/blumkin/skills/freebusy_suggest.py +58 -0
  81. blumkin-0.2.0/src/blumkin/skills/mail.py +1961 -0
  82. blumkin-0.2.0/src/blumkin/skills/meeting.py +135 -0
  83. blumkin-0.2.0/src/blumkin/skills/people.py +171 -0
  84. blumkin-0.2.0/src/blumkin/version.py +158 -0
  85. blumkin-0.2.0/test_packaging +104 -0
  86. blumkin-0.2.0/tests/conftest.py +26 -0
  87. blumkin-0.2.0/tests/test_auth_refresh_unit.py +167 -0
  88. blumkin-0.2.0/tests/test_auth_scopes.py +81 -0
  89. blumkin-0.2.0/tests/test_auth_secret_perms_unit.py +136 -0
  90. blumkin-0.2.0/tests/test_auth_status.py +32 -0
  91. blumkin-0.2.0/tests/test_calendar_suggest_unit.py +403 -0
  92. blumkin-0.2.0/tests/test_calendar_timezone_unit.py +127 -0
  93. blumkin-0.2.0/tests/test_calendar_today_unit.py +49 -0
  94. blumkin-0.2.0/tests/test_chat_attachments_unit.py +491 -0
  95. blumkin-0.2.0/tests/test_cli_completion_unit.py +43 -0
  96. blumkin-0.2.0/tests/test_cli_help.py +153 -0
  97. blumkin-0.2.0/tests/test_cli_usage.py +1598 -0
  98. blumkin-0.2.0/tests/test_config.py +368 -0
  99. blumkin-0.2.0/tests/test_error_hints_unit.py +173 -0
  100. blumkin-0.2.0/tests/test_google_calendar_writes_unit.py +479 -0
  101. blumkin-0.2.0/tests/test_google_chat_reads_unit.py +315 -0
  102. blumkin-0.2.0/tests/test_google_chat_writes_unit.py +426 -0
  103. blumkin-0.2.0/tests/test_google_http_unit.py +72 -0
  104. blumkin-0.2.0/tests/test_google_mail_reads_unit.py +371 -0
  105. blumkin-0.2.0/tests/test_google_mail_writes_unit.py +828 -0
  106. blumkin-0.2.0/tests/test_google_people_unit.py +266 -0
  107. blumkin-0.2.0/tests/test_google_provider_unit.py +831 -0
  108. blumkin-0.2.0/tests/test_graph_request_config_unit.py +32 -0
  109. blumkin-0.2.0/tests/test_graph_timeout_unit.py +41 -0
  110. blumkin-0.2.0/tests/test_json_envelope_unit.py +98 -0
  111. blumkin-0.2.0/tests/test_live_reads.py +86 -0
  112. blumkin-0.2.0/tests/test_logout_cache.py +36 -0
  113. blumkin-0.2.0/tests/test_mail_attach_unit.py +356 -0
  114. blumkin-0.2.0/tests/test_mail_attachments_unit.py +651 -0
  115. blumkin-0.2.0/tests/test_mail_filters_unit.py +716 -0
  116. blumkin-0.2.0/tests/test_mail_folders_unit.py +476 -0
  117. blumkin-0.2.0/tests/test_mail_get_unit.py +258 -0
  118. blumkin-0.2.0/tests/test_mail_recipients_unit.py +205 -0
  119. blumkin-0.2.0/tests/test_mail_reply_unit.py +475 -0
  120. blumkin-0.2.0/tests/test_mail_signature_reapply_unit.py +159 -0
  121. blumkin-0.2.0/tests/test_mail_signature_unit.py +160 -0
  122. blumkin-0.2.0/tests/test_people_resolve_unit.py +494 -0
  123. blumkin-0.2.0/tests/test_phase2_reads_unit.py +762 -0
  124. blumkin-0.2.0/tests/test_phase3_writes_unit.py +1130 -0
  125. blumkin-0.2.0/tests/test_phase4_writes_unit.py +362 -0
  126. blumkin-0.2.0/tests/test_profile_email_unit.py +361 -0
  127. blumkin-0.2.0/tests/test_providers_unit.py +127 -0
  128. blumkin-0.2.0/tests/test_release_config_unit.py +37 -0
  129. blumkin-0.2.0/tests/test_skills_catalog.py +28 -0
  130. blumkin-0.2.0/tests/test_skills_schema.py +408 -0
  131. blumkin-0.2.0/tests/test_upgrade_unit.py +263 -0
  132. blumkin-0.2.0/tests/test_version_unit.py +119 -0
  133. blumkin-0.2.0/uv.lock +1217 -0
@@ -0,0 +1,156 @@
1
+ ---
2
+ description: Commits and PRs attribute only the committer — no agent or machine co-authors
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Git commit identity
7
+
8
+ Commits and pull requests must attribute **only** whoever is making the commit —
9
+ their configured git `user.name` and `user.email`. Do not leak hostnames, Cursor,
10
+ or other co-authors.
11
+
12
+ ## Commits
13
+
14
+ - Use plain `git commit -m "…"` or a HEREDOC with **subject and body only**.
15
+ - Do **not** add `Co-authored-by`, `Made-with: Cursor`, or any `--trailer` flags.
16
+ - Do **not** append attribution lines to commit messages, even if Cursor defaults
17
+ would add them.
18
+ - Do **not** pass `--no-gpg-sign` (or otherwise skip signing) when `commit.gpgsign`
19
+ is enabled.
20
+ - After any commit (hooks / tooling may rewrite the message), inspect
21
+ `git show -s --format=%B HEAD` before push. If it contains `Co-authored-by:` or
22
+ `Made-with:`, **stop**, fix the message, and do not push until clean.
23
+ - Prefer `git verify-commit HEAD` after signing commits; confirm GitHub shows the
24
+ commit as Verified once pushed.
25
+
26
+ ## Pull requests
27
+
28
+ - Do **not** add `Co-authored-by` or `Made-with: Cursor` to `gh pr create --body`
29
+ or `gh pr edit --body`.
30
+
31
+ ## Git identity
32
+
33
+ - Rely on git's configured author for the current committer (`git config user.name`
34
+ and `git config user.email`). Do not hardcode a specific name or email.
35
+ - If `user.email` is unset, stop and ask the committer to configure git before
36
+ committing (avoid hostname fallback addresses like `user@machine.local`).
37
+ - Also survey **effective** identity (env overrides beat config): compare
38
+ `git var GIT_AUTHOR_IDENT` and `git var GIT_COMMITTER_IDENT` to the approved
39
+ GitHub name/email. Reject machine-local addresses (e.g. `*.local`) and stop if
40
+ `GIT_AUTHOR_*`, `GIT_COMMITTER_*`, `EMAIL`, or `git commit --author` would produce
41
+ a different identity than configured.
42
+
43
+ ## Commit signing (GPG / SSH)
44
+
45
+ At the **start of any session where the agent may commit**, survey whether commit
46
+ signing is set up for the committer:
47
+
48
+ 1. `git config --get commit.gpgsign` should be `true` (global or local, as they intend).
49
+ 2. `git config --get user.signingkey` should be set (GPG key id, or SSH public key
50
+ path when `gpg.format` is `ssh`).
51
+ 3. Check `git config --get gpg.format` (and any repo-local override). For GPG
52
+ signing, prefer `openpgp` (or unset). If a leftover `gpg.format=ssh` is set
53
+ while `user.signingkey` is a GPG key id, Git will mis-treat the key — set
54
+ `gpg.format` to `openpgp` (or unset ssh) before committing.
55
+ 4. If `gpg.format` is `ssh`, confirm the public key path exists **and** the matching
56
+ private key is loaded (`ssh-add -L` should list it), or run a real signed-commit
57
+ probe. Otherwise confirm `gpg --list-secret-keys --keyid-format=long <signingkey>`
58
+ succeeds (secret present on **this** host).
59
+ 5. On Darwin (GPG): `pinentry-program` in `~/.gnupg/gpg-agent.conf` should point at an
60
+ existing binary (prefer `pinentry-mac` from Homebrew).
61
+ 6. Optional one-shot probe: `echo test | gpg --clearsign -u <KEYID>`. If it hangs or
62
+ fails in the agent TTY, tell the operator to unlock via GUI pinentry or finish the
63
+ commit in Terminal.app — agent terminals often cannot drive curses pinentry.
64
+ 7. GitHub Verified checks depend on signing format:
65
+ - **OpenPGP** (`gpg.format` is `openpgp` or unset): the **committer email** must
66
+ appear on the GPG key UID and be a **verified** email on the GitHub account
67
+ (same value as `git config user.email`). Optional list check:
68
+ `gh api user/gpg_keys` (needs `read:gpg_key`; `admin:gpg_key` also works). If the
69
+ API is unavailable, guide manual paste of `gpg --armor --export <KEYID>`.
70
+ - **SSH** (`gpg.format=ssh`): UID / `user/gpg_keys` do **not** apply. Confirm the
71
+ configured public key is registered on GitHub as a **Signing Key** (not only
72
+ Authentication) and that `ssh-add -L` lists the matching private key.
73
+
74
+ If signing is missing or incomplete, **alert the committer** and surface setup
75
+ instructions before committing (unless they explicitly opt out for a one-off).
76
+
77
+ ### Per-machine / personal vs work keys
78
+
79
+ - Prefer a **separate signing key per machine** (or at least personal vs work). Do
80
+ **not** export a personal laptop’s secret key onto a corporate device.
81
+ - Match `user.name` / `user.email` to the GitHub identity. For OpenPGP, put that same
82
+ verified email in the GPG UID; upload **that** public key to GitHub → Settings →
83
+ SSH and GPG keys. For SSH signing, upload the public key as a Signing Key.
84
+ - When a work machine is retired, revoke/rotate that machine’s key on GitHub and
85
+ remove the local secret.
86
+
87
+ ### Passphrase
88
+
89
+ - **Recommend using a passphrase** on the signing key (protects the secret at rest;
90
+ `gpg-agent` can cache it with `default-cache-ttl` / `max-cache-ttl`).
91
+ - Do **not** suggest empty-passphrase keys “for agent convenience.”
92
+ - Agent / non-TTY sessions usually cannot drive curses pinentry — use GUI
93
+ `pinentry-mac` (macOS) or commit from Terminal.app after unlocking once.
94
+
95
+ ### GPG key (macOS / Homebrew)
96
+
97
+ ```bash
98
+ brew install gnupg pinentry-mac
99
+ # If brew cannot link into /opt/homebrew/bin (ownership / permissions), fix Homebrew
100
+ # ownership for that prefix or use the full $(brew --prefix)/bin path below.
101
+
102
+ mkdir -p ~/.gnupg
103
+ chmod 700 ~/.gnupg
104
+ echo "pinentry-program $(brew --prefix)/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
105
+ # optional passphrase cache (seconds), so you are not prompted every commit:
106
+ # echo "default-cache-ttl 28800" >> ~/.gnupg/gpg-agent.conf
107
+ # echo "max-cache-ttl 86400" >> ~/.gnupg/gpg-agent.conf
108
+ gpgconf --kill gpg-agent
109
+
110
+ gpg --full-generate-key
111
+ # Choose RSA (or default), key size ≥ 3072, set an expiration, and use a passphrase.
112
+ # UID email must be a GitHub-verified address matching git config user.email.
113
+ gpg --list-secret-keys --keyid-format=long
114
+ git config --global gpg.format openpgp
115
+ git config --global user.signingkey <KEYID>
116
+ git config --global commit.gpgsign true
117
+ # Clear a leftover repo-local ssh signing override if present:
118
+ # git config --unset gpg.format # run inside the repo if it forced ssh
119
+ gpg --armor --export <KEYID>
120
+ # Upload the armored public key: GitHub → Settings → SSH and GPG keys → New GPG key
121
+
122
+ # Sanity check (GUI pinentry should appear if the agent needs the passphrase):
123
+ echo test | gpg --clearsign -u <KEYID>
124
+ ```
125
+
126
+ ### SSH signing (alternative)
127
+
128
+ ```bash
129
+ git config --global gpg.format ssh
130
+ git config --global user.signingkey ~/.ssh/id_ed25519.pub
131
+ git config --global commit.gpgsign true
132
+ ssh-add -L # private key must be available via ssh-agent
133
+ # Add the same public key on GitHub as a Signing Key (not only Authentication)
134
+ ```
135
+
136
+ ## Cursor CLI attribution (per machine)
137
+
138
+ At the **start of any session where the agent may commit or open PRs**, read
139
+ `~/.cursor/cli-config.json` (if present) and verify attribution is disabled:
140
+
141
+ ```json
142
+ "attribution": {
143
+ "attributeCommitsToAgent": false,
144
+ "attributePRsToAgent": false
145
+ }
146
+ ```
147
+
148
+ If the file is missing, unreadable, or either flag is not `false`, **alert the
149
+ committer** before committing and surface setup instructions — explain that Cursor
150
+ will inject co-author trailers unless they fix their local config. Provide the
151
+ exact keys to set (or create the file with the block above). Also remind them to
152
+ disable **Settings → Git & PRs → Attribution** in the Cursor IDE (IDE and CLI
153
+ settings are separate).
154
+
155
+ Do not commit on their behalf until they acknowledge or fix the config (unless
156
+ they explicitly opt out for a one-off commit).
@@ -0,0 +1,95 @@
1
+ ---
2
+ description: Lexicographic code organization — public then private; sorted closed sets
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Lexicographic code organization
7
+
8
+ **Default:** sort **ASCII lexicographically by name** whenever possible. **Insert** new
9
+ definitions and closed-set members in sorted position — do not append at the bottom of a
10
+ block.
11
+
12
+ Keep language-standard import / `from … import` blocks at the top of the module (e.g. PEP 8
13
+ for Python); they are not part of the public/private sorts below.
14
+
15
+ ## Module level
16
+
17
+ Two blocks, in order:
18
+
19
+ 1. **Public** — module-level names **without** a leading `_`: constants (`ALL_CAPS`),
20
+ classes, module-level functions (`def` / `async def`), and other public bindings. Sort
21
+ **within this block** by name (ASCII).
22
+ 2. **Private** — module-level names **with** a leading `_` (helpers, module-private
23
+ constants, etc.). Sort **within this block** by name (ASCII).
24
+
25
+ Public names are **not** interleaved with private names by strict global ASCII
26
+ (`build_foo` stays in the public block even though `_` sorts before `b`).
27
+
28
+ ```python
29
+ # public block (sorted)
30
+ DEFAULT_TIMEOUT_S = 30
31
+ class Widget: ...
32
+ def build_status() -> None: ...
33
+
34
+ # private block (sorted)
35
+ def _epoch_to_iso_z(epoch: float) -> str: ...
36
+ def _format_label(dt: datetime) -> str: ...
37
+ ```
38
+
39
+ When adding a new `_helper`, insert it among the other `_` names — not after the public
40
+ APIs and not at the end of the file unless it sorts last in the private block.
41
+
42
+ ## Inside a `class`
43
+
44
+ Two blocks, in order:
45
+
46
+ 1. **Public** — class-level members whose names do **not** start with `_`, including
47
+ methods, `@property` getters, constants, nested classes, and other bindings. Treat
48
+ `async def` like `def`. Dunder methods (`__init__`, `__str__`, …) live in this block and
49
+ participate in the same lexicographic sort.
50
+ 2. **Private** — class-level members whose names start with `_` (single leading underscore),
51
+ including methods, properties, constants, nested classes, and other bindings. Sort
52
+ lexicographically within this block.
53
+
54
+ ```python
55
+ class Example:
56
+ def __init__(self) -> None: ...
57
+ def __str__(self) -> str: ...
58
+ def close(self) -> None: ...
59
+ def fetch(self) -> None: ...
60
+
61
+ def _validate(self) -> None: ...
62
+ ```
63
+
64
+ ## Closed-set literals
65
+
66
+ For **unordered** closed name sets, keep members in **ASCII lexicographic order** and insert
67
+ new members in sorted position (do not append):
68
+
69
+ - `frozenset({...})` / `set` literals of names
70
+
71
+ For **order-sensitive** declarations, preserve declaration order when it is part of the
72
+ contract (enum auto-values, iteration/serialization order, or user-visible sequencing). Sort
73
+ lexicographically only when order is not significant:
74
+
75
+ - Enum members — sort when values/iteration order are not contractual; otherwise keep
76
+ declaration order
77
+ - Tuple / list constants of identifier-like strings — sort when order is not observable
78
+ externally; otherwise keep declaration order
79
+
80
+ ```python
81
+ # good — unordered name set
82
+ ALLOWED = frozenset({"alpha", "beta", "gamma"})
83
+
84
+ # bad — unsorted / append-only unordered set
85
+ ALLOWED = frozenset({"gamma", "alpha", "beta"})
86
+ ```
87
+
88
+ ## Other languages
89
+
90
+ Apply the same **public-then-private, lexicographic within each block** idea elsewhere when
91
+ organizing code (e.g. grouped `const` / `enum` members in TypeScript, export blocks in TS
92
+ modules) unless a file already documents a different stable layout.
93
+
94
+ Whole-file reorders of legacy modules are optional drive-by refactors; **new and moved APIs
95
+ must follow this rule in the block they belong to.**
@@ -0,0 +1,33 @@
1
+ ---
2
+ description: Run live Graph/auth tests locally (CI mocks only)
3
+ globs: src/blumkin/auth.py,src/blumkin/graph.py,src/blumkin/skills/**/*.py,tests/test_auth*.py,tests/test_live*.py
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Local live Graph / auth tests
8
+
9
+ CI runs **mocked** unit tests only (`pytest -m 'not live'`). Silent refresh and real Graph reads are validated **on this machine**.
10
+
11
+ Live means **real**: never verify with a skill that notifies others (no invites,
12
+ sends, or chats). See `.cursor/rules/no-third-party-side-effects.mdc`.
13
+
14
+ When changing auth, Graph client, calendar/mail/chat skills, or live/auth tests:
15
+
16
+ 1. Unit (same as CI):
17
+ ```bash
18
+ uv run pytest -m 'not live'
19
+ ```
20
+ 2. Live reads + forced access-token expiry → silent refresh:
21
+ ```bash
22
+ BLUMKIN_LIVE=1 uv run pytest -m live
23
+ ```
24
+ Requires config + token cache + auth record with a refresh token under
25
+ `~/.config/blumkin/` (or `BLUMKIN_CONFIG_DIR`). Do not commit those files.
26
+
27
+ 3. Smoke CLI after live green:
28
+ ```bash
29
+ blumkin auth status
30
+ blumkin calendar today --json
31
+ ```
32
+
33
+ Do not skip step 2 for auth/token-cache changes: the live test force-expires the cached access token and asserts Graph still works and `auth status` shows a future expiry.
@@ -0,0 +1,38 @@
1
+ ---
2
+ description: Never modify the primary (main) git worktree unless the user explicitly authorizes it
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Main worktree is off-limits
7
+
8
+ The **primary clone** at the repo root (first line of `git worktree list`) is the **main worktree**. Do **not** modify it unless the user explicitly authorizes that in the current conversation.
9
+
10
+ ## Do all work in a stack worktree
11
+
12
+ 1. Run `~/work/ai/repository-helpers/scripts/dev/start-development --worktree <stack-name> --no-interactive`.
13
+ 2. `cd` into `.worktrees/<stack-name>-wt` before any implementation, tests, or git writes.
14
+ 3. Point tools at the stack worktree (`--dir`, working directory, file paths).
15
+
16
+ ## Forbidden on the main worktree (without explicit user authorization)
17
+
18
+ - File edits, dependency installs, tests, builds, linters, formatters
19
+ - `dep-updater --dir <repo-root>` (fast-forwards the primary clone and changes git state)
20
+ - `gh stack …` / commits / checkouts / other git write operations
21
+ - Leaving uncommitted changes or stray branches
22
+
23
+ ## Read-only on main is OK
24
+
25
+ `git log`, `git show`, `gh pr view`, and reading files without writing are fine on the main worktree.
26
+
27
+ ## Investigating without polluting main
28
+
29
+ Use a detached temporary worktree, not the primary clone:
30
+
31
+ ```bash
32
+ git worktree add --detach /tmp/repo-inspect-<id> origin/main
33
+ # ... read-only or isolated experiments ...
34
+ git worktree remove --force /tmp/repo-inspect-<id>
35
+ ```
36
+
37
+ `start-development` may sync the main worktree for environment setup; that does **not** grant permission to implement or run mutating commands there.
38
+
@@ -0,0 +1,46 @@
1
+ ---
2
+ description: Never expose secrets in logs, PRs, tool output, or the repo
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # No secret exposure (logs, PRs, repo)
7
+
8
+ Coding agents must not leak secrets into **any** channel: shell/tool output,
9
+ transcripts, PR/issue text, review replies, commits, fixtures, or docs.
10
+
11
+ This complements CI secret-scan (detection after the fact). Prefer prevention.
12
+
13
+ Org tracking / template sync: [repository-helpers#566](https://github.com/the-hcma/repository-helpers/issues/566).
14
+
15
+ ## Never print or paste
16
+
17
+ - Client secrets, passwords, API keys, PATs, webhook secrets
18
+ - Access / refresh tokens, MSAL / auth record JSON, private keys
19
+ - Full contents of `~/.config/blumkin/config.toml`, `.env`, or token-cache files
20
+ - Raw `Authorization` headers or Graph/MSAL error bodies that embed tokens
21
+
22
+ ## When inspecting local config
23
+
24
+ - Prefer **allowlisted non-secret keys** only (`tenant_id`, `default_tz`,
25
+ `provider`, `wo1162425_scopes`, path existence flags).
26
+ - Redact values that might be sensitive: `client_id = "(redacted)"`.
27
+ - Do **not** `cat` / `type` whole config or env dumps “for debugging.”
28
+
29
+ ```bash
30
+ # bad — may dump client_secret / tokens into the transcript
31
+ cat ~/.config/blumkin/config.toml
32
+
33
+ # good — keys only, redact known secret-ish values
34
+ rg -n '^[a-z_]+' ~/.config/blumkin/config.toml | sed -E 's/(client_id|client_secret|google_oauth_client_file|.*token.*)\s*=.*/\1 = "(redacted)"/I'
35
+ ```
36
+
37
+ ## Repo / PR hygiene
38
+
39
+ - Never commit secrets, `.msal_token_cache.json`, `.auth_record.json`, `.env`.
40
+ - Never put secret values in PR titles, bodies, comments, or review replies.
41
+ - Test fixtures use obvious fakes (`test-client`, `not-a-secret`).
42
+
43
+ ## If something already leaked
44
+
45
+ Tell the operator to **rotate** the credential. Do **not** repeat the secret
46
+ value in chat, logs, or GitHub.
@@ -0,0 +1,71 @@
1
+ ---
2
+ description: Never let local verification reach other people (no invites, mail, or chats)
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Local verification must not reach other people
7
+
8
+ blumkin acts as the operator against their real mailbox, calendar, and Teams. A
9
+ verification run is not a sandbox: anything it sends is indistinguishable from the
10
+ operator doing it by hand, lands in someone else's inbox or calendar, and cannot be
11
+ recalled.
12
+
13
+ **Never invoke a skill that notifies others in order to test, verify, demo, or
14
+ reproduce something.** Do it only when the operator asks for that specific action
15
+ in the current conversation, on the real recipients they named.
16
+
17
+ ## The trip-wire
18
+
19
+ `blumkin skills list --json` marks every skill with `notifies_others`. Treat
20
+ `notifies_others: true` as off-limits for verification. **The flag is
21
+ authoritative** — the list below is a snapshot, so re-read the catalog rather
22
+ than trusting it to stay complete. At the time of writing:
23
+
24
+ - `calendar.accept` — sends a response to the organizer
25
+ - `calendar.cancel` — sends cancellations to every attendee
26
+ - `calendar.create` — invites attendees
27
+ - `chat.delete` — every participant sees the message vanish
28
+ - `chat.edit` — rewrites a message other people already read
29
+ - `chat.send` — messages a person
30
+ - `mail.send-draft` — sends mail
31
+
32
+ To check the current set without trusting this list:
33
+
34
+ ```bash
35
+ blumkin skills list --json | jq -r '.skills[] | select(.notifies_others) | .id'
36
+ ```
37
+
38
+ Absence of the flag is not a blank cheque: `mail.draft`, `mail.update-draft`, and
39
+ `mail.delete-draft` are safe because they stay in the operator's own mailbox, and
40
+ that is the actual test — *does this reach anyone else?*
41
+
42
+ ## Verify with reads instead
43
+
44
+ Reads cover most behavior, cost nothing, and reach no one:
45
+
46
+ ```bash
47
+ blumkin calendar today --json
48
+ blumkin calendar view --from YYYY-MM-DD --to YYYY-MM-DD --json
49
+ blumkin mail list --folder sentitems --top 5 --json
50
+ blumkin mail folders --json
51
+ blumkin chat find --with "Name" --json
52
+ blumkin chat last --with "Name" --n 3 --json
53
+ ```
54
+
55
+ To exercise a write path, prefer one that is self-contained and reversible:
56
+ create a draft, inspect it, delete it. Never promote that to a send.
57
+
58
+ For anything genuinely unreachable without notifying someone, verify with mocked
59
+ unit tests and say plainly that the live path was not exercised. An honest gap
60
+ beats a real invitation.
61
+
62
+ ## If a notifying action is explicitly requested
63
+
64
+ Confirm the recipients and the exact payload before running it, pass `--yes`
65
+ knowingly rather than reflexively, and never widen the recipient list to make a
66
+ test more convincing.
67
+
68
+ ## Free/busy and other people's data
69
+
70
+ `calendar.freebusy` reads other people's availability. It notifies no one, so it
71
+ is fine — but do not spray it across colleagues to generate test fixtures.
@@ -0,0 +1,125 @@
1
+ ---
2
+ description: Ship a PR end-to-end — local gates, submit, agent review loop, operator email
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # PR ship, agent review, and operator email
7
+
8
+ When the user asks to **ship**, **submit**, **open a PR**, or **follow the flow**, run this
9
+ sequence in a **stack worktree** (never the primary clone). Read `.github/stacking-tool` and
10
+ `.cursor/rules/stacking-tool.mdc` before creating branches or submitting.
11
+
12
+ Helper scripts live in **repository-helpers** (canonical agent review loop):
13
+
14
+ ```bash
15
+ rh="${REPOSITORY_HELPERS_DIR:-$HOME/work/ai/repository-helpers}"
16
+ "${rh}/scripts/wait-for-agent-review" …
17
+ "${rh}/scripts/trigger-agent-review" …
18
+ ```
19
+
20
+ Configure `~/.config/agent-review.env` from `${rh}/etc/agent-review.env.example` (`AGENT_REVIEW_REPORT_TO`, SMTP).
21
+
22
+ <!-- pr-ship-canonical-skill: https://github.com/the-hcma/repository-helpers/blob/main/.cursor/skills/ship-and-review/SKILL.md -->
23
+ <!-- pr-ship-canonical-rule: https://github.com/the-hcma/repository-helpers/blob/main/.cursor/rules/pr-ship-and-review.mdc -->
24
+
25
+ **Canonical playbook (read first):** This file is a consumer summary. Exit codes,
26
+ early-complete semantics, quota chains, and triage details are maintained in
27
+ repository-helpers and may change between releases.
28
+
29
+ Agents **must** read the canonical Skill before running the agent review loop:
30
+
31
+ - **GitHub (`main`):** https://github.com/the-hcma/repository-helpers/blob/main/.cursor/skills/ship-and-review/SKILL.md
32
+ - **Local clone (when `${rh}` is synced to `main`):** `${rh}/.cursor/skills/ship-and-review/SKILL.md`
33
+
34
+ When this summary and the canonical Skill disagree, follow the canonical Skill.
35
+
36
+ ## 1. Local quality gates (all must pass before submit)
37
+
38
+ Run this repository's quality gates from the stack worktree (tests, linters, etc.).
39
+
40
+ ## 2. Commit and submit
41
+
42
+ Only after §1 quality gates pass. **Read** `.github/stacking-tool` (`graphite` or `gh-stack`)
43
+ and follow `.cursor/rules/stacking-tool.mdc` — do not mix backends on the same stack.
44
+
45
+ ```bash
46
+ # When marker is graphite (apply-fix comments the inactive backend):
47
+ # gt create <stack>/<topic> -m 'feat: …' # or gt modify -m '…'
48
+ # gt submit --publish --no-interactive
49
+
50
+ # When marker is gh-stack:
51
+ gh stack init <stack>/<topic> # or gh stack add for a higher layer
52
+ git add -A && git commit -m 'feat: …'
53
+ gh stack submit --auto --open --remote origin
54
+ ```
55
+
56
+ Wait for CI:
57
+
58
+ ```bash
59
+ "${rh}/scripts/dev/post-pr-submission-checks" --pr <n>
60
+ ```
61
+
62
+ If stderr shows `NOTE: GITHUB_RATE_LIMIT_*`, the helpers are waiting on GitHub API
63
+ quota reset — let them finish (do not treat as a hard local failure mid-wait).
64
+
65
+ Patch title/body if stale: `gh pr edit <n> --title … --body …`
66
+
67
+ ## 3. Agent review loop
68
+
69
+ > **Reply before resolve (required):** For every valid agent review thread (Copilot, Bugbot, CodeRabbit, …), post an **on-thread human reply** as the authenticated operator **before** resolving. Exit code **3** means threads **lack a human reply** — resolving without replying is non-compliant.
70
+
71
+ **Prerequisites:** `gh auth` must be set up for the operator running the loop. Copy
72
+ `${rh}/etc/agent-review.env.example` to `~/.config/agent-review.env` with
73
+ `AGENT_REVIEW_REPORT_TO` and SMTP before `complete`. `complete_ready: true` is
74
+ reported by `"${rh}/scripts/wait-for-agent-review" check --pr <n>` (or `status`)
75
+ JSON when CI is green and agent review threads are clear.
76
+
77
+ Prefer the built-in loop:
78
+
79
+ ```bash
80
+ "${rh}/scripts/wait-for-agent-review" loop --pr <n>
81
+ ```
82
+
83
+ **Early complete:** when all threads are addressed, CI is green, there is **no** pending requested
84
+ Copilot/Bugbot review, and CodeRabbit’s workflow is **not** running, the loop finishes — no
85
+ mandatory idle dwell. **12h PR cap** (`AGENT_REVIEW_PR_TIMEOUT`) when review cycles never
86
+ converge (exit **6** give-up). Per-agent quota caches skip exhausted agents (CodeRabbit, Copilot,
87
+ Bugbot).
88
+
89
+ **CodeRabbit is on_push:** a new push starts CodeRabbit — never post `@coderabbitai review`.
90
+ If quota-limited, wait the cooldown with feedback polls (issue #369; default poll **60s**) +
91
+ grace (default **60s**); only then, if still no real review on head, the loop may post a
92
+ one-shot `@coderabbitai full review`. When CodeRabbit says wait, do not re-ask until
93
+ `retry_after`. Rate-limit stubs are not reviews. Mid-cooldown pending
94
+ feedback wakes the loop (exit **3**).
95
+
96
+ When `loop` exits **3**, triage each unaddressed agent thread:
97
+
98
+ 1. Fix in the worktree when actionable (or skip with a brief on-thread reason).
99
+ 2. **Reply on-thread** as the authenticated human:
100
+ ```bash
101
+ "${rh}/scripts/wait-for-agent-review" reply-thread --thread-id <PRRT_…> --body 'Fixed in abc1234: …'
102
+ ```
103
+ 3. **Resolve only after step 2:**
104
+ ```bash
105
+ "${rh}/scripts/wait-for-agent-review" resolve-thread --thread-id <PRRT_…>
106
+ ```
107
+ Or batch: `"${rh}/scripts/wait-for-agent-review" resolve-addressed --pr <n>` (requires an existing human reply per thread).
108
+
109
+ **Never** resolve via raw GraphQL/API without an on-thread human reply first.
110
+
111
+ Then push fixes, re-run `"${rh}/scripts/dev/post-pr-submission-checks" --pr <n>`, and start the next loop iteration.
112
+
113
+ When `check` reports `complete_ready: true`:
114
+
115
+ ```bash
116
+ "${rh}/scripts/wait-for-agent-review" complete --pr <n>
117
+ ```
118
+
119
+ `complete_ready` requires an agent sign-off on the current head (not a bare CodeRabbit
120
+ commit status / rate-limit stub). This emails `AGENT_REVIEW_REPORT_TO`. It does **not**
121
+ run `gh pr review --approve` (self-approve is skipped; merge stays an explicit operator step).
122
+
123
+ Do **not** add `merge-it` unless the user explicitly confirms. Org merge path is
124
+ GitHub auto-merge (`gh pr merge --auto --squash` / Enable auto-merge) when the
125
+ operator asks to merge.
@@ -0,0 +1,67 @@
1
+ ---
2
+ description: Block PR submit until pre-pr-checks pass; format before check; no truncated output
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Pre-PR checks (required)
7
+
8
+ `pre-pr-checks` lives in **repository-helpers**, not in this repo. Resolve the
9
+ clone once:
10
+
11
+ ```bash
12
+ rh="${REPOSITORY_HELPERS_DIR:-$HOME/work/ai/repository-helpers}"
13
+ ```
14
+
15
+ `pre-pr-checks` resolves its target from `$PWD`, so it audits **this** repo when
16
+ run from a feature worktree here. The `submit-stack` wrapper does **not** — it
17
+ resolves the repo from its own path and would submit the repository-helpers
18
+ clone, so consumers submit with a bare `gh stack submit` / `gt submit`.
19
+
20
+ Before submitting a PR:
21
+
22
+ 1. Run **`"${rh}/scripts/dev/pre-pr-checks"`** from this repo's feature worktree
23
+ (must exit 0), then submit with bare **`gh stack submit --auto`** or
24
+ **`gt submit`** per this repo's `.github/stacking-tool` marker, from the same
25
+ worktree. Do **not** use `"${rh}/scripts/dev/submit-stack"` from here.
26
+
27
+ 2. Do **not** submit if pre-pr-checks failed or was skipped. A skipped job is
28
+ allowed **only** when the user has approved it for this PR: pass
29
+ `PRE_PR_CHECKS_SKIP=job1,job2` (never a silent skip) and record the skipped
30
+ jobs and the reason in the PR **Test plan**.
31
+
32
+ 3. In the PR **Test plan**, note that `"${rh}/scripts/dev/pre-pr-checks"` passed
33
+ (paste the final `==> pre-pr-checks passed` line from the full run).
34
+
35
+ 4. Scripts must not leave changes on the **primary (main) worktree**.
36
+
37
+ ## Apply formatters before check (required)
38
+
39
+ `pre-pr-checks` is **check-only** by default (e.g. `ruff format --check`, `cargo fmt -- --check`).
40
+ After review-fix edits — especially string literals — **apply** formatters first, then run the gate:
41
+
42
+ ```bash
43
+ # Python (match CI paths; adjust for the repo)
44
+ uv run ruff format .
45
+ # Rust
46
+ cargo fmt --all
47
+ # Then the gate
48
+ "${rh}/scripts/dev/pre-pr-checks"
49
+ # Or apply + check in one shot (mutates the worktree):
50
+ "${rh}/scripts/dev/pre-pr-checks" --fix
51
+ ```
52
+
53
+ Commit any format-only diff before submit. Do **not** treat a green `pytest` / `ruff check` /
54
+ partial job as a pre-PR pass.
55
+
56
+ ## No truncated pre-PR output
57
+
58
+ Do **not** pipe `pre-pr-checks` to `tail` / `head`. Require exit **0** and the final
59
+ `==> pre-pr-checks passed` line from the full run.
60
+
61
+ ```bash
62
+ # ❌ Truncated — hides failures above the last few lines
63
+ "${rh}/scripts/dev/pre-pr-checks" 2>&1 | tail -8
64
+
65
+ # ✅ Full output + exit status
66
+ "${rh}/scripts/dev/pre-pr-checks"
67
+ ```
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: On every new agent session, read AGENTS.md and .cursor/rules before acting
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Session start: read AGENTS.md and project rules
7
+
8
+ At the **start of every new agent session** (before implementing, editing, or
9
+ answering from assumed conventions), actively load project guidance:
10
+
11
+ 1. **Read** `AGENTS.md` at the repository root when it exists (use the Read tool;
12
+ do not rely only on auto-injected snippets).
13
+ 2. **List** `.cursor/rules/*.mdc` and **read** each rule that applies to this
14
+ session — at minimum every `alwaysApply: true` rule, plus any glob-matched
15
+ rules for files you will touch.
16
+
17
+ Do this on the first turn of a new conversation even if some guidance already
18
+ appears in context — confirm the on-disk files are what you follow.
19
+
20
+ If `AGENTS.md` is missing, note that and continue with `.cursor/rules/` only.
21
+ Do not invent org conventions that are not in those files.