coppermind-cmo 0.3.2__tar.gz → 0.3.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 (75) hide show
  1. {coppermind_cmo-0.3.2/src/coppermind_cmo.egg-info → coppermind_cmo-0.3.4}/PKG-INFO +1 -1
  2. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/pyproject.toml +2 -2
  3. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-actions.md +39 -0
  4. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-batch.md +44 -0
  5. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-connect.md +43 -0
  6. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-followup.md +25 -0
  7. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-handoff.md +57 -0
  8. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-help.md +53 -0
  9. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-morning.md +38 -0
  10. coppermind_cmo-0.3.4/src/coppermind_cmo/commands/cmo-review.md +60 -0
  11. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/server.py +35 -1
  12. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/tools/minds.py +13 -2
  13. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4/src/coppermind_cmo.egg-info}/PKG-INFO +1 -1
  14. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo.egg-info/SOURCES.txt +8 -0
  15. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/LICENSE +0 -0
  16. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/README.md +0 -0
  17. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/setup.cfg +0 -0
  18. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/__init__.py +0 -0
  19. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/__main__.py +0 -0
  20. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/active_client.py +0 -0
  21. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/background.py +0 -0
  22. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/config.py +0 -0
  23. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/db.py +0 -0
  24. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/embedding_client.py +0 -0
  25. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/embedding_server.py +0 -0
  26. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/errors.py +0 -0
  27. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/ingest.py +0 -0
  28. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/ingest_folder.py +0 -0
  29. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/llm_client.py +0 -0
  30. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/log.py +0 -0
  31. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/skills/cmo-onboard-client.md +0 -0
  32. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/statusline.py +0 -0
  33. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/statusline.sh +0 -0
  34. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/supabase-ca.pem +0 -0
  35. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/tips.py +0 -0
  36. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/tools/__init__.py +0 -0
  37. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/tools/brand.py +0 -0
  38. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/tools/intelligence.py +0 -0
  39. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/tools/memories.py +0 -0
  40. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/utils.py +0 -0
  41. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo/watch.py +0 -0
  42. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo.egg-info/dependency_links.txt +0 -0
  43. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo.egg-info/entry_points.txt +0 -0
  44. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo.egg-info/requires.txt +0 -0
  45. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/src/coppermind_cmo.egg-info/top_level.txt +0 -0
  46. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_active_client.py +0 -0
  47. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_api_contracts.py +0 -0
  48. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_background_ingestion.py +0 -0
  49. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_brand.py +0 -0
  50. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_config.py +0 -0
  51. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_customer_isolation.py +0 -0
  52. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_db.py +0 -0
  53. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_embedding_client.py +0 -0
  54. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_embedding_server.py +0 -0
  55. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_errors.py +0 -0
  56. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_ingest.py +0 -0
  57. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_ingest_folder.py +0 -0
  58. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_integration.py +0 -0
  59. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_integration_flows.py +0 -0
  60. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_integration_smoke.py +0 -0
  61. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_intelligence.py +0 -0
  62. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_llm_client.py +0 -0
  63. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_log.py +0 -0
  64. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_memories.py +0 -0
  65. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_minds.py +0 -0
  66. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_performance.py +0 -0
  67. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_pid_management.py +0 -0
  68. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_raw_documents.py +0 -0
  69. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_reconsolidation.py +0 -0
  70. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_schema.py +0 -0
  71. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_server.py +0 -0
  72. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_session_tracking.py +0 -0
  73. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_tips.py +0 -0
  74. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_upgrade_resilience.py +0 -0
  75. {coppermind_cmo-0.3.2 → coppermind_cmo-0.3.4}/tests/test_watch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coppermind-cmo
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: AI-powered memory for fractional CMOs — never forget a client detail, never leak data between clients
5
5
  Author-email: Ben Finklea <ben@volacci.com>
6
6
  License-Expression: LicenseRef-Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "coppermind-cmo"
7
- version = "0.3.2"
7
+ version = "0.3.4"
8
8
  description = "AI-powered memory for fractional CMOs — never forget a client detail, never leak data between clients"
9
9
  readme = "README.md"
10
10
  license = "LicenseRef-Proprietary"
@@ -71,4 +71,4 @@ exclude_lines = [
71
71
  where = ["src"]
72
72
 
73
73
  [tool.setuptools.package-data]
74
- coppermind_cmo = ["supabase-ca.pem", "skills/*.md", "statusline.sh"]
74
+ coppermind_cmo = ["supabase-ca.pem", "skills/*.md", "statusline.sh", "commands/*.md"]
@@ -0,0 +1,39 @@
1
+ # /cmo-actions — Cross-Client Action Items
2
+
3
+ Show overdue and open action items across all clients.
4
+
5
+ ## Steps
6
+
7
+ 1. Call `list_minds` to get all client minds.
8
+ 2. For each client, switch and search for commitment-type memories.
9
+ 3. Build a unified action item view grouped by urgency:
10
+
11
+ ```
12
+ ACTION ITEMS — All Clients
13
+
14
+ OVERDUE
15
+ - [Acme] Send revised media plan to Sarah — due 3/8 (7 days overdue)
16
+ - [5 Star] Follow up with Stephanie on LSA lead marking — due 3/11 (4 days overdue)
17
+
18
+ DUE THIS WEEK
19
+ - [Acme] Approve Q3 creative concepts — due 3/18
20
+ - [XSCAPE] Verify satellite office signage — no date, committed 3/11
21
+
22
+ OPEN (no due date)
23
+ - [5 Star] Evaluate RP1 as Lead Truffle replacement
24
+ - [5 Star] Help with van wrap design
25
+ - [XSCAPE] Review direct mail demographics with Josh before printing
26
+ - [Bluebell] Finalize Q2 content calendar
27
+
28
+ TOTAL: 8 open items across 3 clients
29
+ ```
30
+
31
+ 4. Ask: "Want me to focus on any of these?"
32
+
33
+ ## Rules
34
+ - Process all clients in parallel.
35
+ - Sort overdue items by how late they are (most overdue first).
36
+ - Only include commitments that haven't been resolved by a later memory.
37
+ - If a commitment was clearly completed based on subsequent memories, skip it.
38
+ - Keep descriptions to one line each.
39
+ - Show the client name in brackets on every line — this is a cross-client view.
@@ -0,0 +1,44 @@
1
+ # /cmo-batch — Batch Content Creation
2
+
3
+ Write content for multiple clients in one session.
4
+
5
+ ## Usage
6
+
7
+ User says something like:
8
+ - "Write this week's social posts for all my clients"
9
+ - "Draft newsletters for Acme and 5 Star"
10
+ - "Write a blog post for each client about spring topics"
11
+
12
+ ## Steps
13
+
14
+ 1. Parse which clients and what content type.
15
+ - If "all clients": use `list_minds` to get the full list.
16
+ - If specific clients named: use those.
17
+ 2. For each client:
18
+ a. Switch to the client
19
+ b. Pull brand voice, content themes, and recent memories
20
+ c. Write the requested content using the client's voice and themes
21
+ d. Label it clearly with the client name
22
+ 3. Present all content together with clear separators:
23
+
24
+ ```
25
+ ---
26
+ ACME CORP — Social Post (Instagram)
27
+ [content in Acme's brand voice]
28
+ ---
29
+ 5 STAR CHARLESTON — Social Post (Instagram)
30
+ [content in 5 Star's brand voice]
31
+ ---
32
+ XSCAPE — Social Post (Instagram)
33
+ [content in XSCAPE's brand voice]
34
+ ---
35
+ ```
36
+
37
+ 4. Ask: "Want me to adjust any of these, or copy them all to clipboard?"
38
+
39
+ ## Rules
40
+ - Each piece of content MUST use that client's brand voice. This is the whole point — don't use a generic voice.
41
+ - Reference recent events/memories when relevant ("Just launched our rebrand!" for XSCAPE, "Spring tune-up season is here" for 5 Star).
42
+ - Match the content type to the platform (short for social, longer for blogs, professional for newsletters).
43
+ - If a client has no brand voice set, mention it: "[Client] doesn't have brand voice configured yet. Want me to set it up?"
44
+ - Don't show the switch_client responses. Just present the final content.
@@ -0,0 +1,43 @@
1
+ # /cmo-connect — Pull Data from Any MCP
2
+
3
+ Bridge data from any connected MCP into Coppermind. Claude acts as the extractor — fetches data, breaks it into memories, and stores them with the right types.
4
+
5
+ ## Usage
6
+
7
+ - `/cmo-connect pull my last 3 Granola meetings into the right client minds`
8
+ - `/cmo-connect search Gmail for emails from sarah@acme.com about the rebrand`
9
+ - `/cmo-connect pull recent Slack messages from #acme-marketing`
10
+
11
+ ## Steps
12
+
13
+ 1. Parse what the user wants: which data source, what data, which client(s).
14
+ 2. Check if the required MCP is connected (look for its tools in available tools).
15
+ - **If not connected:** Tell them exactly how to add it. Example: "Granola isn't connected. Run `claude mcp add granola -- uvx granola-mcp-server` and restart."
16
+ - **If connected:** Proceed.
17
+ 3. Fetch the data using the source MCP's tools.
18
+ 4. For each item (meeting, email, message):
19
+ a. Determine which client mind it belongs to (match by participant names, company names, or ask the user).
20
+ b. Switch to that client mind.
21
+ c. Extract and store memories using the same rules as transcript ingestion:
22
+ - Stakeholder profiles, decisions, commitments, key facts
23
+ - Tag with the source date and source type
24
+ d. Report progress every 10 memories.
25
+ 5. Summarize: "Pulled [N] items from [source]. Stored [M] memories across [K] clients."
26
+
27
+ ## Known MCP Setups
28
+
29
+ - **Granola:** `claude mcp add granola -- uvx granola-mcp-server` (no API key, reads local cache)
30
+ - **Slack:** Check for `slack_read_channel`, `slack_search_public` tools
31
+ - **Gmail:** Check for `gmail_search_messages`, `gmail_read_message` tools
32
+ - **Google Calendar:** Check for `gcal_list_events` tools
33
+
34
+ ## Rate Limits
35
+ - Maximum 50 memories per invocation
36
+ - Maximum 20 source items per fetch (emails, meetings, messages)
37
+ - If the source has more, store the 50 most recent/important first and tell the user: "There's more — want me to continue?"
38
+
39
+ ## Rules
40
+ - Always confirm which client mind to store into before storing. If ambiguous, ask.
41
+ - Don't store duplicate content — check if a similar memory already exists before storing.
42
+ - Meeting transcripts from Granola should be processed the same way as pasted transcripts (full extraction, not just a raw dump).
43
+ - Report what was stored at the end, not during. Keep the output clean.
@@ -0,0 +1,25 @@
1
+ # /cmo-followup — Post-Meeting Follow-Up Email
2
+
3
+ Draft a follow-up email after a client meeting.
4
+
5
+ ## Steps
6
+
7
+ 1. Confirm the active client. If none, ask which client the meeting was with.
8
+ 2. Pull the most recent memories (last 24 hours) and the last meeting brief if saved.
9
+ 3. Check if a CMO voice profile exists (search for an internal mind called "CMO Profile" or check for a `cmo_voice` memory). If found, use it for tone and style. If not, use a neutral professional tone — warm, concise, and direct.
10
+ 4. Do NOT use the client's brand voice for emails. Brand voice is how the client talks to their customers. This email is from the CMO to the client.
11
+ 5. Draft a follow-up email that includes:
12
+ - Brief thank-you / recap of what was discussed
13
+ - Key decisions made (bulleted)
14
+ - Action items with owners and deadlines (bulleted)
15
+ - Next steps or next meeting date if known
16
+ 6. Match formality to the relationship — use stakeholder profile to gauge. If the stakeholder profile says "direct communicator" or the transcript shows casual language, be casual. Otherwise default to professional.
17
+ 7. Present the draft and ask: "Want me to adjust anything, or copy this to your clipboard?"
18
+
19
+ ## Rules
20
+ - Keep it short — 3-4 paragraphs max. CMOs send dozens of these.
21
+ - Never include internal notes or agency-specific details. This goes TO the client.
22
+ - Action items should clearly state who owns each one (including items the CMO owns).
23
+ - If the user just pasted a transcript, extract from that. Don't make them re-explain.
24
+ - Offer to copy to clipboard when done.
25
+ - If this is the first follow-up email and no CMO voice exists, mention it once: "Tip: paste 2-3 emails you've sent to clients and I'll learn your writing style for future emails."
@@ -0,0 +1,57 @@
1
+ # /cmo-handoff — Client Handoff Document
2
+
3
+ Generate a comprehensive handoff document for a client engagement. This is the "deliverable mind" — when the engagement ends, the client gets this.
4
+
5
+ ## Steps
6
+
7
+ 1. Confirm the active client. If argument provided, switch to that client.
8
+ 2. Gather everything:
9
+ - `get_brand_voice` — full brand DNA
10
+ - `search_memory` — broad search for decisions, commitments, stakeholders, facts, campaign outcomes
11
+ - `get_campaign_history` — all campaign results
12
+ - `get_last_brief` — most recent meeting brief
13
+ - Company info from the mind's configuration
14
+ 3. Generate a structured handoff document:
15
+
16
+ ```
17
+ CLIENT HANDOFF: [Client Name]
18
+ Prepared by: [CMO name] | Date: [today]
19
+ Engagement period: [first memory date] — [today]
20
+
21
+ COMPANY OVERVIEW
22
+ [From company_info: industry, location, services, key details]
23
+
24
+ BRAND DNA
25
+ Voice & Tone: [summary]
26
+ Positioning: [tagline, positioning statement]
27
+ Key Differentiators: [bulleted]
28
+ Target Audience: [description]
29
+ Content Themes: [bulleted]
30
+
31
+ KEY STAKEHOLDERS
32
+ [Name, title, role, communication style — for each]
33
+
34
+ DECISION LOG
35
+ [Chronological list of major decisions with dates and rationale]
36
+
37
+ ACTIVE COMMITMENTS
38
+ [Open action items with owners]
39
+
40
+ CAMPAIGN HISTORY & RESULTS
41
+ [Campaign summaries with outcomes]
42
+
43
+ STRATEGIC CONTEXT
44
+ [Key concerns, priorities, and unresolved questions]
45
+
46
+ RECOMMENDATIONS FOR NEXT CMO
47
+ [Based on patterns in the data — what to focus on, what to avoid, relationship dynamics]
48
+ ```
49
+
50
+ 4. Ask: "Want me to copy this to clipboard, or save it as a file?"
51
+
52
+ ## Rules
53
+ - This is the crown jewel feature. Make it comprehensive and polished.
54
+ - Write the "Recommendations" section as genuine strategic advice, not just a data dump.
55
+ - Include dates on everything — decisions, campaigns, stakeholder additions.
56
+ - The document should be useful to someone who has NEVER met this client.
57
+ - Use the brand voice description but write the document in a professional, neutral tone — it's a handoff, not marketing copy.
@@ -0,0 +1,53 @@
1
+ # /cmo-help — Coppermind Help
2
+
3
+ Provide help, troubleshooting, and how-to guidance for Coppermind CMO.
4
+
5
+ ## Steps
6
+
7
+ 1. Read `docs/product-knowledge.md` for the full knowledge base.
8
+ 2. Read `docs/QUICKSTART.md` if the user needs setup help.
9
+ 3. Answer the user's question conversationally from those sources.
10
+
11
+ ## If no specific question
12
+
13
+ Show this quick reference:
14
+
15
+ ```
16
+ COPPERMIND CMO — Quick Reference
17
+
18
+ GETTING STARTED
19
+ "Set up [client name]" Create a new client
20
+ "Switch to [client]" Change active client
21
+ "List my clients" Show all client minds
22
+
23
+ MEETINGS
24
+ "Prep my meeting with [client]" Build a meeting brief
25
+ "Save the brief" Persist for later retrieval
26
+ "Show my last brief" Pull up saved brief
27
+
28
+ MEMORY
29
+ "Remember: [anything]" Store a structured memory
30
+ "Quick note: [anything]" Fast capture, no classification
31
+ "What did we decide about X?" Search decisions
32
+ "Who is [person]?" Search stakeholders
33
+
34
+ CONTENT
35
+ "Write a blog post for [client]" Uses client's brand voice
36
+ "Draft a social post about X" On-brand content
37
+ "Write a follow-up email" Post-meeting email draft
38
+
39
+ POWER FEATURES
40
+ /cmo-morning Daily briefing across all clients
41
+ /cmo-followup Draft post-meeting follow-up email
42
+ /cmo-handoff Generate client handoff document
43
+ /cmo-actions Overdue items across all clients
44
+ /cmo-batch Write content for multiple clients
45
+ /cmo-review Quarterly review for a client
46
+ /cmo-connect Pull data from Granola/Gmail/Slack
47
+ ```
48
+
49
+ ## Rules
50
+ - Always check the knowledge base and quickstart before saying "I don't know."
51
+ - For setup issues, walk them through step by step — don't just link to the doc.
52
+ - Only suggest emailing coppermind@volacci.com for issues you genuinely cannot resolve.
53
+ - Keep answers short and actionable. CMOs don't want tutorials.
@@ -0,0 +1,38 @@
1
+ # /cmo-morning — Daily Briefing
2
+
3
+ Build a morning briefing across all clients.
4
+
5
+ ## Steps
6
+
7
+ 1. Call `list_minds` to get all client minds.
8
+ 2. For each client with memories, switch to it and gather:
9
+ - Open commitments (action items not marked complete)
10
+ - Any memories from the last 48 hours
11
+ - Current sprint status and what's due (if sprint plan exists)
12
+ 3. If Google Calendar MCP is connected, check today's meetings and match attendees to client minds.
13
+ 4. Present a unified briefing:
14
+
15
+ ```
16
+ GOOD MORNING — [Date]
17
+
18
+ TODAY'S MEETINGS
19
+ - 10:00 Acme Corp (Sarah Chen) — prep available, say "prep my meeting with Acme"
20
+ - 2:30 5 Star Charleston (Stephen Graham) — no recent notes, consider catching up
21
+
22
+ NEEDS ATTENTION
23
+ - [Acme] 2 action items overdue from Sprint 3
24
+ - [Bluebell] No interaction in 18 days — consider a check-in
25
+ - [5 Star] Stephen waiting on van wrap designs (committed 3/10)
26
+
27
+ THIS WEEK'S SPRINT DELIVERABLES
28
+ - [Acme] Publish 4 SEO pages, launch outbound calling
29
+ - [5 Star] Finalize hiring reqs, evaluate RP1 platform
30
+ ```
31
+
32
+ 5. End with: "Want me to prep any of these meetings?"
33
+
34
+ ## Rules
35
+ - Process clients in parallel if possible.
36
+ - Only show clients that have something to report — skip clients with no open items and no recent activity.
37
+ - Keep it scannable. No paragraphs. Bullets and short lines only.
38
+ - Don't show the raw switch_client responses. Just present the final briefing.
@@ -0,0 +1,60 @@
1
+ # /cmo-review — Quarterly Review
2
+
3
+ Generate a quarterly review for a client.
4
+
5
+ ## Usage
6
+
7
+ - `/cmo-review` — reviews current quarter for active client
8
+ - `/cmo-review Q4 2025` — reviews a specific quarter
9
+ - "How did Q1 go for Acme?" — triggers naturally
10
+
11
+ ## Steps
12
+
13
+ 1. Confirm the active client and quarter. Default to current quarter from cadence.
14
+ 2. Gather all memories from that quarter:
15
+ - Decisions made (with dates)
16
+ - Commitments — which were completed, which are still open
17
+ - Campaign outcomes and metrics
18
+ - Key facts and status changes
19
+ - Stakeholder changes (new hires, departures, role changes)
20
+ 3. If a sprint plan exists for that quarter, pull completion rates per initiative.
21
+ 4. Generate the review:
22
+
23
+ ```
24
+ QUARTERLY REVIEW: [Client Name] — [Quarter Year]
25
+
26
+ SUMMARY
27
+ [2-3 sentence executive summary of the quarter]
28
+
29
+ WINS
30
+ - [Completed initiatives, successful campaigns, goals met]
31
+
32
+ DECISIONS MADE
33
+ - [Date] [Decision and rationale]
34
+ - [Date] [Decision and rationale]
35
+
36
+ CAMPAIGNS & RESULTS
37
+ - [Campaign name]: [outcome, metrics if available]
38
+
39
+ OPEN ITEMS CARRIED FORWARD
40
+ - [Items not completed this quarter that carry into next]
41
+
42
+ SPRINT PLAN SCORECARD (if applicable)
43
+ - Initiative 1: 5/6 sprints complete ████████░░ 83%
44
+ - Initiative 2: 3/6 sprints complete █████░░░░░ 50%
45
+ - Initiative 3: CUT in Sprint 4
46
+
47
+ TEAM CHANGES
48
+ - [New hires, departures, role changes during the quarter]
49
+
50
+ LOOKING AHEAD
51
+ [Recommendations for next quarter based on what happened this quarter]
52
+ ```
53
+
54
+ 5. Ask: "Want me to copy this, or use it as input for next quarter's planning?"
55
+
56
+ ## Rules
57
+ - Be honest. If results were poor, say so constructively. CMOs need accurate assessments, not cheerleading.
58
+ - Include specific dates and numbers wherever available.
59
+ - The "Looking Ahead" section should be genuine strategic recommendations, not generic advice.
60
+ - If there's not enough data for a full review, say so: "I only have [N] memories from Q4. The review will be thin — want to feed me more context first?"
@@ -330,8 +330,9 @@ def _startup_checks():
330
330
  # Install statusline script if it doesn't exist
331
331
  _install_statusline()
332
332
 
333
- # Install bundled skills
333
+ # Install bundled skills and commands
334
334
  _install_skills()
335
+ _install_commands()
335
336
 
336
337
  # Optional: auto-start session watcher
337
338
  _start_watcher()
@@ -370,6 +371,39 @@ def _install_skills():
370
371
  logger.warn(f"Failed to install skills: {e}")
371
372
 
372
373
 
374
+ def _install_commands():
375
+ """Copy bundled /cmo command files to ~/.claude/commands/ if newer or missing."""
376
+ import hashlib
377
+ from pathlib import Path
378
+ from importlib import resources
379
+
380
+ commands_dir = Path.home() / ".claude" / "commands"
381
+ commands_dir.mkdir(parents=True, exist_ok=True)
382
+
383
+ try:
384
+ commands_package = resources.files("coppermind_cmo") / "commands"
385
+ if not commands_package.is_dir():
386
+ return
387
+
388
+ for cmd_file in commands_package.iterdir():
389
+ if not cmd_file.name.endswith(".md"):
390
+ continue
391
+
392
+ dest = commands_dir / cmd_file.name
393
+ source_content = cmd_file.read_text(encoding="utf-8")
394
+ source_hash = hashlib.md5(source_content.encode()).hexdigest()
395
+
396
+ if dest.exists():
397
+ dest_hash = hashlib.md5(dest.read_text(encoding="utf-8").encode()).hexdigest()
398
+ if source_hash == dest_hash:
399
+ continue
400
+
401
+ dest.write_text(source_content, encoding="utf-8")
402
+ logger.info(f"Installed command: {cmd_file.name}")
403
+ except Exception as e:
404
+ logger.warn(f"Failed to install commands: {e}")
405
+
406
+
373
407
  def _install_statusline():
374
408
  """Copy statusline.sh to ~/.coppermind/ on first run (skipped on Windows)."""
375
409
  if sys.platform == "win32":
@@ -141,6 +141,12 @@ def _switch_client(
141
141
  has_customer = _has_customer_id_col(db)
142
142
 
143
143
  # --- LOOKUP: filter by customer_id when available ---
144
+ # Default-deny: if isolation column exists but customer_id is missing, refuse lookup.
145
+ if has_customer and not customer_id:
146
+ return tool_error("PROVISIONING_ERROR",
147
+ "Your account could not be identified. "
148
+ "This usually means your API key is misconfigured. "
149
+ "Contact coppermind@volacci.com for help.")
144
150
  has_access = _has_access_table(db)
145
151
  if customer_id and has_customer:
146
152
  # Only show minds this customer owns or has access to
@@ -322,8 +328,13 @@ def _list_minds(limit: int, offset: int, db: Database, include_internal: bool =
322
328
  where_clauses = []
323
329
  params: list = []
324
330
 
325
- # Customer isolation: only show minds this customer owns or has access to
326
- if customer_id and _has_customer_id_col(db):
331
+ # Customer isolation: only show minds this customer owns or has access to.
332
+ # Default-deny: if the isolation column exists but customer_id is missing,
333
+ # return nothing rather than leaking all customers' data.
334
+ if _has_customer_id_col(db):
335
+ if not customer_id:
336
+ logger.warn("Isolation column present but no customer_id — returning empty list")
337
+ return []
327
338
  if _has_access_table(db):
328
339
  where_clauses.append(
329
340
  "(cm.customer_id = %s OR EXISTS ("
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coppermind-cmo
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: AI-powered memory for fractional CMOs — never forget a client detail, never leak data between clients
5
5
  Author-email: Ben Finklea <ben@volacci.com>
6
6
  License-Expression: LicenseRef-Proprietary
@@ -27,6 +27,14 @@ src/coppermind_cmo.egg-info/dependency_links.txt
27
27
  src/coppermind_cmo.egg-info/entry_points.txt
28
28
  src/coppermind_cmo.egg-info/requires.txt
29
29
  src/coppermind_cmo.egg-info/top_level.txt
30
+ src/coppermind_cmo/commands/cmo-actions.md
31
+ src/coppermind_cmo/commands/cmo-batch.md
32
+ src/coppermind_cmo/commands/cmo-connect.md
33
+ src/coppermind_cmo/commands/cmo-followup.md
34
+ src/coppermind_cmo/commands/cmo-handoff.md
35
+ src/coppermind_cmo/commands/cmo-help.md
36
+ src/coppermind_cmo/commands/cmo-morning.md
37
+ src/coppermind_cmo/commands/cmo-review.md
30
38
  src/coppermind_cmo/skills/cmo-onboard-client.md
31
39
  src/coppermind_cmo/tools/__init__.py
32
40
  src/coppermind_cmo/tools/brand.py
File without changes
File without changes
File without changes