@charzhu/openjaw-agent 0.2.0

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.
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: refresh-token
3
+ description: "Refreshes Microsoft Graph API tokens used by OpenJaw for Teams and Outlook."
4
+ whenToUse: "Graph API tokens are expired or need refreshing for Teams/Outlook automation"
5
+ ---
6
+
7
+ # Skill: Refresh Graph Token
8
+
9
+ ## Description
10
+ Refreshes Microsoft Graph API tokens (graph-chat + outlook-mail) used by OpenJaw for Teams and Outlook automation.
11
+
12
+ ## Prerequisites
13
+ - Edge running with `--remote-debugging-port=9222 --remote-allow-origins=*`
14
+ - Logged into outlook.office.com in Edge
15
+ - Node.js 22+ with `ws` package (`npm i -g ws` if not installed)
16
+
17
+ ## How to Execute
18
+
19
+ Write the following script to a temp file and run it. The script is fully self-contained.
20
+
21
+ ```javascript
22
+ // Save this as a temp .mjs file and run with: node <tempfile>.mjs
23
+ import { WebSocket } from 'ws';
24
+ import { writeFileSync, readFileSync, mkdirSync, existsSync } from 'fs';
25
+ import { join } from 'path';
26
+ import { homedir } from 'os';
27
+
28
+ const CDP_PORT = 9222;
29
+ const CLIENT_ID = '9199bf20-a13f-4107-85dc-02114787ef48';
30
+ const TOKEN_DIR = join(homedir(), '.graph-token', 'tokens');
31
+
32
+ const AUDIENCES = [
33
+ { name: 'graph-chat', audience: 'https://graph.microsoft.com' },
34
+ { name: 'outlook-mail', audience: 'https://outlook.office.com' },
35
+ ];
36
+
37
+ function decodeJwt(jwt) {
38
+ try { return JSON.parse(Buffer.from(jwt.split('.')[1], 'base64url').toString()); }
39
+ catch { return null; }
40
+ }
41
+
42
+ async function listPages() {
43
+ return await (await fetch(`http://localhost:${CDP_PORT}/json`)).json();
44
+ }
45
+
46
+ async function evalInTab(wsUrl, expr) {
47
+ return new Promise(resolve => {
48
+ const ws = new WebSocket(wsUrl);
49
+ const timer = setTimeout(() => { ws.close(); resolve(null); }, 15000);
50
+ ws.on('open', () => ws.send(JSON.stringify({ id: 1, method: 'Runtime.evaluate', params: { expression: expr, returnByValue: true } })));
51
+ ws.on('message', d => { const r = JSON.parse(d.toString()); if (r.id === 1) { clearTimeout(timer); ws.close(); resolve(r.result?.result?.value ?? null); } });
52
+ ws.on('error', () => { clearTimeout(timer); resolve(null); });
53
+ });
54
+ }
55
+
56
+ async function openTab(url) {
57
+ const ver = await (await fetch(`http://localhost:${CDP_PORT}/json/version`)).json();
58
+ return new Promise(resolve => {
59
+ const ws = new WebSocket(ver.webSocketDebuggerUrl);
60
+ const timer = setTimeout(() => { ws.close(); resolve(null); }, 15000);
61
+ ws.on('open', () => ws.send(JSON.stringify({ id: 1, method: 'Target.createTarget', params: { url } })));
62
+ ws.on('message', d => { const r = JSON.parse(d.toString()); if (r.id === 1) { clearTimeout(timer); ws.close(); resolve(r.result?.targetId); } });
63
+ ws.on('error', () => { clearTimeout(timer); resolve(null); });
64
+ });
65
+ }
66
+
67
+ const EXTRACT_RT = `(function(){
68
+ var rtKeys=Object.keys(localStorage).filter(k=>k.includes('refreshtoken'));
69
+ if(!rtKeys.length) return JSON.stringify({error:'no_rt'});
70
+ var rtKey=rtKeys.find(k=>k.includes('|refreshtoken|${CLIENT_ID}|'))||rtKeys[0];
71
+ var rt=JSON.parse(localStorage.getItem(rtKey));
72
+ var acctKeys=JSON.parse(localStorage.getItem('msal.2.account.keys')||'[]');
73
+ if(!acctKeys.length) return JSON.stringify({error:'no_account'});
74
+ var acct=JSON.parse(localStorage.getItem(acctKeys[0]));
75
+ var tid=acct.realm||acct.tenantId||acct.homeAccountId?.split('.')[1]||'';
76
+ return JSON.stringify({refreshToken:rt.data||rt.secret||'',tenantId:tid});
77
+ })()`;
78
+
79
+ async function main() {
80
+ // Check CDP
81
+ try { await fetch(`http://localhost:${CDP_PORT}/json/version`); }
82
+ catch { console.error('❌ CDP not available. Start Edge with --remote-debugging-port=9222'); process.exit(1); }
83
+
84
+ // Find Outlook tab
85
+ let pages = await listPages();
86
+ let tab = pages.find(p => (p.url?.includes('outlook.office.com') || p.url?.includes('outlook.cloud.microsoft')) && p.webSocketDebuggerUrl && p.type === 'page');
87
+
88
+ if (!tab) {
89
+ console.log('⏳ Opening Outlook...');
90
+ await openTab('https://outlook.office.com/mail/');
91
+ for (let i = 0; i < 20; i++) {
92
+ await new Promise(r => setTimeout(r, 2000));
93
+ pages = await listPages();
94
+ tab = pages.find(p => p.url?.includes('outlook.office') && p.webSocketDebuggerUrl && p.type === 'page');
95
+ if (tab) break;
96
+ }
97
+ if (!tab) { console.error('❌ Outlook did not load'); process.exit(1); }
98
+ await new Promise(r => setTimeout(r, 8000)); // Wait for MSAL
99
+ pages = await listPages();
100
+ tab = pages.find(p => p.url?.includes('outlook.office') && p.webSocketDebuggerUrl && p.type === 'page');
101
+ }
102
+
103
+ console.log('✅ Outlook tab found');
104
+
105
+ // Extract RT
106
+ const raw = await evalInTab(tab.webSocketDebuggerUrl, EXTRACT_RT);
107
+ if (!raw) { console.error('❌ CDP eval failed'); process.exit(1); }
108
+ const info = JSON.parse(raw);
109
+ if (info.error) { console.error('❌ ' + info.error); process.exit(1); }
110
+ console.log('✅ RT extracted (tenant: ' + info.tenantId.slice(0, 8) + '...)');
111
+
112
+ // Exchange for access tokens
113
+ mkdirSync(TOKEN_DIR, { recursive: true });
114
+ for (const { name, audience } of AUDIENCES) {
115
+ const body = new URLSearchParams({ client_id: CLIENT_ID, grant_type: 'refresh_token', refresh_token: info.refreshToken, scope: audience + '/.default offline_access' });
116
+ const resp = await fetch(`https://login.microsoftonline.com/${info.tenantId}/oauth2/v2.0/token`, {
117
+ method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Origin: 'https://outlook.office.com' }, body: body.toString()
118
+ });
119
+ const result = await resp.json();
120
+ if (result.access_token) {
121
+ const exp = decodeJwt(result.access_token)?.exp || 0;
122
+ const mins = Math.floor((exp - Date.now() / 1000) / 60);
123
+ writeFileSync(join(TOKEN_DIR, name + '.json'), JSON.stringify({ access_token: result.access_token, token_type: 'Bearer', saved_at: new Date().toISOString(), source: 'refresh-skill' }, null, 2));
124
+ console.log('✅ ' + name + ': refreshed (' + mins + 'm validity)');
125
+ } else {
126
+ console.error('❌ ' + name + ': ' + (result.error_description || result.error || 'unknown'));
127
+ }
128
+ }
129
+ }
130
+
131
+ main().catch(e => { console.error('❌ ' + e.message); process.exit(1); });
132
+ ```
133
+
134
+ ## How to Run
135
+
136
+ The agent should:
137
+ 1. Write the above script to a temp file: `file_write({ path: "<temp>/refresh-graph.mjs", content: <script> })`
138
+ 2. Execute: `system_run({ command: "node <temp>/refresh-graph.mjs", timeout: 60000 })`
139
+ 3. Verify: `outlook_switch_channel("graph")` → `outlook_go_to_inbox()`
140
+
141
+ ## Token File Locations
142
+ ```
143
+ ~/.graph-token/tokens/graph-chat.json — Teams Graph channel
144
+ ~/.graph-token/tokens/outlook-mail.json — Outlook Graph channel
145
+ ```
146
+
147
+ ## Troubleshooting
148
+
149
+ | Problem | Fix |
150
+ |---------|-----|
151
+ | CDP not available | Restart Edge with `--remote-debugging-port=9222 --remote-allow-origins=*` |
152
+ | `no_rt` error | Outlook Web session expired — log in manually |
153
+ | RT exchange failed | Refresh token revoked — close Edge, reopen, log in fresh |
154
+ | Tokens expire in ~80 min | Run this skill periodically |
155
+
156
+ ## Verification
157
+ After refresh, test with:
158
+ ```
159
+ outlook_switch_channel("graph") → outlook_go_to_inbox()
160
+ teams_switch_channel("graph") → teams_read_messages()
161
+ ```
@@ -0,0 +1,122 @@
1
+ ---
2
+ name: skill-creator
3
+ description: "Create new skills, modify existing skills, and improve skill quality."
4
+ whenToUse: "Jon identifies a new capability gap that should be systematized"
5
+ ---
6
+
7
+ # Skill: Skill Creator (Meta-Skill)
8
+
9
+ ## Description
10
+ Create new skills for Jon, modify existing skills, and improve skill quality. This is the meta-skill — the skill that makes other skills. Based on Anthropic's skill-creator but adapted for OpenJaw Agent.
11
+
12
+ ## When to Use
13
+ - Jon identifies a new capability gap that should be systematized
14
+ - User asks to "teach Jon something new" or "add a new skill"
15
+ - After completing a novel task that should be repeatable
16
+ - When an existing skill needs improvement based on real usage
17
+
18
+ ## Skill Creation Process
19
+
20
+ ### 1. Capture Intent
21
+ Understand what the skill should do:
22
+ - What task does this skill enable?
23
+ - When should it trigger? (what user phrases/contexts)
24
+ - What's the expected output?
25
+ - What tools/APIs does it need?
26
+ - What are the common failure modes?
27
+
28
+ ### 2. Research & Reference
29
+ Before writing, gather context:
30
+ - Check existing skills for overlap: `file_list("~/.openjaw-agent/skills")` and `file_list("<package>/skills")`
31
+ - Search GitHub for similar skills: `web_search("SKILL.md [topic] agent")`
32
+ - Check Anthropic's official skills: `web_fetch("https://raw.githubusercontent.com/anthropics/skills/main/skills/[name]/SKILL.md")`
33
+ - Review memory for past experiences: `memory_search("[topic]")`
34
+
35
+ ### 3. Write the SKILL.md
36
+
37
+ #### Required Structure
38
+ ```markdown
39
+ # Skill: [Name]
40
+
41
+ ## Description
42
+ [1-2 sentences: What it does. Be "pushy" — list all trigger phrases and contexts
43
+ so the skill doesn't under-trigger.]
44
+
45
+ ## When to Use
46
+ - [Trigger condition 1]
47
+ - [Trigger condition 2]
48
+ - [Chinese trigger phrase]
49
+ - [English trigger phrase]
50
+
51
+ ## Prerequisites
52
+ - [Required tools, libraries, APIs]
53
+
54
+ ## Workflow / Steps
55
+ [The core instructions — step by step]
56
+
57
+ ## Important Notes
58
+ [Gotchas, edge cases, lessons learned from real usage]
59
+
60
+ ## Output & Delivery
61
+ [How to deliver the result to the user]
62
+ ```
63
+
64
+ #### Writing Guidelines
65
+ - **Be specific, not generic** — include actual code snippets, file paths, API endpoints
66
+ - **Include failure modes** — what goes wrong and how to fix it
67
+ - **Use real examples** from actual usage with CharZ
68
+ - **Include Chinese trigger phrases** — CharZ communicates in both languages
69
+ - **Reference other skills** — skills should compose together (e.g., "use email-with-attachment skill to send")
70
+ - **Keep it actionable** — every section should help complete the task
71
+ - **"Pushy" descriptions** — list many trigger conditions to prevent under-triggering
72
+
73
+ #### Anti-Patterns to Avoid
74
+ - ❌ Generic advice without specific implementation
75
+ - ❌ Listing tools without showing how to use them
76
+ - ❌ Ignoring the actual environment (Windows, Chinese fonts, Outlook REST API)
77
+ - ❌ Not referencing other skills that should compose together
78
+ - ❌ Missing error handling / troubleshooting section
79
+
80
+ ### 4. Save & Register
81
+ ```python
82
+ # Save to user skills directory (NOT the bundled skills dir)
83
+ file_write("~/.openjaw-agent/skills/{skill-name}.md", content)
84
+
85
+ # Update memory
86
+ memory_append("Added new skill: {name} — {description}")
87
+ ```
88
+
89
+ ### 5. Test
90
+ After creating a skill, ideally test it:
91
+ - Try triggering it with a realistic user request
92
+ - Verify the steps actually work in the current environment
93
+ - Check that referenced tools/APIs are available
94
+
95
+ ### 6. Iterate
96
+ Skills should improve based on real usage:
97
+ - When a skill fails or produces suboptimal results → update it
98
+ - When new tools become available → enhance the skill
99
+ - When user gives feedback → incorporate it
100
+ - Periodically review skills for staleness
101
+
102
+ ## Skill Quality Checklist
103
+ - [ ] Has clear trigger conditions (English + Chinese)
104
+ - [ ] Has specific, actionable steps (not just advice)
105
+ - [ ] Includes code snippets with real paths/APIs
106
+ - [ ] References other composable skills
107
+ - [ ] Has troubleshooting / error handling section
108
+ - [ ] Has been tested at least once
109
+ - [ ] Includes lessons from real usage
110
+
111
+ ## Current Skills Directory
112
+ Path: `~/.openjaw-agent/skills/` (user skills — agent-created, persists across updates)
113
+
114
+ Skills compose together — for example:
115
+ - doc-coauthoring → create-pdf-report → email-with-attachment
116
+ - deep-research → summarization → internal-comms → email-drafting
117
+ - meeting-summarizer → translation → proofreading
118
+
119
+ ## Skill Naming Convention
120
+ - Lowercase with hyphens: `skill-name.md`
121
+ - Descriptive but concise: `create-pdf-report.md`, not `pdf.md`
122
+ - Action-oriented when possible: `email-drafting.md`, not `emails.md`
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: summarization
3
+ description: "Summarize long content into concise, structured summaries."
4
+ whenToUse: "User pastes long text and asks for a summary"
5
+ ---
6
+
7
+ # Skill: Summarization
8
+
9
+ ## Description
10
+ Summarize long content (emails, documents, chat threads, articles) into concise, structured summaries. Supports extractive (key sentences), abstractive (rewritten), and hierarchical (multi-level) approaches.
11
+
12
+ ## When to Use
13
+ - User pastes long text and asks for a summary
14
+ - User asks to summarize emails, Teams chats, or documents
15
+ - User says "太长了帮我总结" or "TL;DR" or "summarize this"
16
+ - After reading a long email thread or chat history
17
+
18
+ ## Approach Selection
19
+
20
+ | Content Type | Best Approach | Output Length |
21
+ |-------------|--------------|---------------|
22
+ | Single email | Extractive (key points) | 2-3 bullets |
23
+ | Email thread (5+ msgs) | Abstractive + timeline | 5-10 bullets |
24
+ | Meeting transcript | Hierarchical (decisions > discussion > details) | ~1 page |
25
+ | Technical document | Abstractive + key terms | Paragraph + bullet points |
26
+ | Chat thread (50+ msgs) | Thematic grouping | Grouped bullets by topic |
27
+ | News article | Extractive headline + key points | 3-5 bullets |
28
+
29
+ ## Workflow
30
+
31
+ ### 1. Assess the Content
32
+ - Length: How long is the source?
33
+ - Type: Email? Chat? Document? Article?
34
+ - Purpose: Why does the user want a summary? (Decision? Catch up? Share?)
35
+
36
+ ### 2. Apply the Right Technique
37
+
38
+ #### Extractive (Pull key sentences)
39
+ Best for: Short-medium content where exact wording matters
40
+ ```
41
+ - Identify the most important 3-5 sentences
42
+ - Preserve original phrasing
43
+ - Bold key terms
44
+ ```
45
+
46
+ #### Abstractive (Rewrite concisely)
47
+ Best for: Long content that needs simplification
48
+ ```
49
+ - Understand the overall message
50
+ - Rewrite in fewer words without losing meaning
51
+ - Use simpler language
52
+ ```
53
+
54
+ #### Hierarchical (Multi-level)
55
+ Best for: Complex content with multiple topics
56
+ ```
57
+ Level 1: One-line summary (< 20 words)
58
+ Level 2: Key points (3-5 bullets)
59
+ Level 3: Detailed summary (paragraph per topic)
60
+ ```
61
+
62
+ #### Thematic (Group by topic)
63
+ Best for: Long chat threads with multiple interleaved discussions
64
+ ```
65
+ Group messages by topic:
66
+ - Topic A: [Summary of all messages about topic A]
67
+ - Topic B: [Summary of all messages about topic B]
68
+ - Action items: [Extracted from all topics]
69
+ ```
70
+
71
+ ### 3. Format Output
72
+
73
+ #### Quick Summary (default)
74
+ ```
75
+ **TL;DR:** [One sentence summary]
76
+
77
+ Key points:
78
+ • [Point 1]
79
+ • [Point 2]
80
+ • [Point 3]
81
+
82
+ Action needed: [If any]
83
+ ```
84
+
85
+ #### Detailed Summary
86
+ ```
87
+ ## Summary: [Title/Subject]
88
+
89
+ **Context:** [Why this matters]
90
+
91
+ ### Main Points
92
+ 1. [Point with detail]
93
+ 2. [Point with detail]
94
+
95
+ ### Decisions Made
96
+ - [Decision 1]
97
+
98
+ ### Action Items
99
+ - [Owner]: [Action] by [Date]
100
+
101
+ ### What's Still Open
102
+ - [Unresolved question]
103
+ ```
104
+
105
+ ### 4. Rules
106
+ - **Match the language** of the source content
107
+ - **Never add information** not in the source
108
+ - **Preserve nuance** — don't oversimplify disagreements
109
+ - **Front-load the most important info** — if user only reads first 2 lines, they get the gist
110
+ - **For sensitive content** — follow the confidentiality rules (no salary/review info)
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: translation
3
+ description: "Translate text between Chinese and English with cultural adaptation and natural tone."
4
+ whenToUse: "User asks to translate text between Chinese and English"
5
+ ---
6
+
7
+ # Skill: Translation (Chinese-English)
8
+
9
+ ## Description
10
+ Translate text between Chinese and English with cultural adaptation, natural tone, and technical term consistency. Optimized for Microsoft PM work context.
11
+
12
+ ## When to Use
13
+ - User asks to translate text between Chinese and English
14
+ - User says "翻译一下" or "translate this"
15
+ - User needs to convert Chinese notes to English email or vice versa
16
+ - User needs help polishing English/Chinese writing
17
+
18
+ ## Translation Principles
19
+
20
+ ### 1. Meaning Over Literal
21
+ - Don't translate word-by-word — translate the meaning
22
+ - Chinese idioms → English equivalents (not literal)
23
+ - English jargon → Natural Chinese (not transliteration)
24
+
25
+ ### 2. Tone Matching
26
+ | Source Tone | Translation Approach |
27
+ |------------|---------------------|
28
+ | Formal business email | Maintain formality level |
29
+ | Casual team chat | Keep it casual and natural |
30
+ | Technical document | Precise terminology, clear structure |
31
+ | Leadership update | Confident, data-driven, concise |
32
+
33
+ ### 3. Technical Term Consistency
34
+ Keep these in English (don't translate):
35
+ - Product names: Copilot, BizChat, Teams, Outlook
36
+ - Technical terms: token, cache, latency, API, LLM, prompt
37
+ - Metrics: CIE, DAU, MAU, P95, SLA
38
+ - Methodologies: Agile, Scrum, sprint, standup
39
+
40
+ ### 4. Chinese → English
41
+ ```
42
+ Approach:
43
+ 1. Understand the full meaning in Chinese
44
+ 2. Restructure for English grammar (SVO, shorter sentences)
45
+ 3. Make it sound native — not "translated from Chinese"
46
+ 4. Keep technical terms in English
47
+ 5. Adjust formality for Western business culture
48
+ ```
49
+
50
+ Common patterns:
51
+ - 关于...的问题 → "Regarding..." or just state the issue directly
52
+ - 请问一下 → Remove (just ask the question directly)
53
+ - 辛苦了 → "Thanks for your help" / "Appreciate the effort"
54
+ - 麻烦你了 → "Would you mind..." / "Could you please..."
55
+
56
+ ### 5. English → Chinese
57
+ ```
58
+ Approach:
59
+ 1. Understand the full meaning in English
60
+ 2. Restructure for Chinese flow (topic-comment, longer compound sentences OK)
61
+ 3. Make it sound natural Chinese — not "翻译腔"
62
+ 4. Keep technical terms in English (don't force-translate)
63
+ 5. Adjust for Chinese business culture (more polite hedging)
64
+ ```
65
+
66
+ Common patterns:
67
+ - "I'd like to..." → 我想...
68
+ - "Could you..." → 麻烦你.../能不能帮忙...
69
+ - "FYI" → 供参考
70
+ - "Heads up" → 提前知会一下
71
+ - "Action needed" → 需要你的action
72
+
73
+ ### 6. Quality Checks
74
+ - Read the translation out loud — does it sound natural?
75
+ - Would a native speaker write it this way?
76
+ - Are all technical terms consistent?
77
+ - Is the tone appropriate for the audience?
78
+ - No "翻译腔" (translationese)
79
+
80
+ ## Example
81
+
82
+ ### Chinese → English
83
+ ```
84
+ Chinese: 这个feature的latency有点高,P95到了2秒,我们需要跟engine team对齐一下优化方案,争取下个sprint能降到1.5秒以下。
85
+
86
+ English: The latency for this feature is higher than expected — P95 is at 2 seconds. We need to sync with the engine team on an optimization plan, targeting sub-1.5s by next sprint.
87
+ ```
88
+
89
+ ### English → Chinese
90
+ ```
91
+ English: We're seeing a regression in cache hit rate after the last deployment. The impact is ~5% increase in token consumption. I've filed a bug and the team is investigating.
92
+
93
+ Chinese: 上次部署后cache hit rate出现了回退,token消耗大概增加了5%。Bug已经提了,团队在排查中。
94
+ ```
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: web-research
3
+ description: "Conduct structured web research on topics, competitors, or technologies."
4
+ whenToUse: "User asks to research a topic, product, or competitor"
5
+ ---
6
+
7
+ # Skill: Web Research & Competitor Analysis
8
+
9
+ ## Description
10
+ Conduct structured web research on topics, competitors, or technologies. Compile findings into organized reports. Useful for PM work like compete analysis, market research, and technology scouting.
11
+
12
+ ## When to Use
13
+ - User asks to research a topic, product, or competitor
14
+ - User asks to compare products or technologies
15
+ - User asks "帮我调研一下" or "看看竞品怎么做的"
16
+ - Need to gather external information for decision-making
17
+
18
+ ## Steps
19
+
20
+ ### 1. Define Research Scope
21
+ Before searching, clarify:
22
+ - What specific questions need answers?
23
+ - What competitors/products to cover?
24
+ - What time frame is relevant?
25
+ - What format for the output?
26
+
27
+ ### 2. Multi-Source Research
28
+ ```
29
+ # Search with multiple angles
30
+ web_search("topic + specific question 1")
31
+ web_search("topic + competitor name")
32
+ web_search("topic + latest news 2026")
33
+ web_search("topic + comparison OR vs OR alternative")
34
+ ```
35
+
36
+ ### 3. Deep Dive on Key Sources
37
+ ```
38
+ # Fetch full content from promising results
39
+ web_fetch(url, max_length=8000)
40
+ ```
41
+
42
+ ### 4. Structured Output Template
43
+
44
+ ```markdown
45
+ ## [Topic] Research Report — [Date]
46
+
47
+ ### Executive Summary
48
+ [2-3 sentence overview of key findings]
49
+
50
+ ### Key Findings
51
+
52
+ #### 1. [Finding Area 1]
53
+ - **What**: Description
54
+ - **So what**: Why this matters
55
+ - **Source**: [URL]
56
+
57
+ #### 2. [Finding Area 2]
58
+ ...
59
+
60
+ ### Competitor Comparison
61
+ | Feature | Product A | Product B | Our Product |
62
+ |---------|-----------|-----------|-------------|
63
+ | Feature 1 | ✅ | ❌ | ✅ |
64
+ | Feature 2 | ❌ | ✅ | 🟡 |
65
+
66
+ ### Implications for Us
67
+ 1. [Actionable insight 1]
68
+ 2. [Actionable insight 2]
69
+
70
+ ### Sources
71
+ 1. [Title](URL) — accessed [date]
72
+ ```
73
+
74
+ ### 5. Quality Checks
75
+ - Cross-reference claims across multiple sources
76
+ - Check source dates — prefer recent information
77
+ - Distinguish facts from opinions
78
+ - Note any gaps in available information
79
+
80
+ ## Compete Analysis Framework (for PM work)
81
+ 1. **Product capabilities**: Feature comparison matrix
82
+ 2. **User experience**: UX/UI differences
83
+ 3. **Performance**: Speed, accuracy, reliability
84
+ 4. **Pricing**: Cost comparison
85
+ 5. **Market position**: User base, growth trends
86
+ 6. **Strengths to learn from**: What they do better
87
+ 7. **Weaknesses to exploit**: Where we can win
88
+
89
+ ## Important Notes
90
+ - Always cite sources with URLs
91
+ - Clearly mark speculation vs confirmed facts
92
+ - For sensitive compete data, check if it's public before sharing
93
+ - Save research output as PDF for easy sharing (use create-pdf-report skill)