tokens-for-good 0.3.5 → 0.3.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokens-for-good",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "type": "module",
5
5
  "description": "Donate your spare AI tokens to research nonprofits for Fierce Philanthropy",
6
6
  "bin": {
@@ -1,47 +1,29 @@
1
- # Step 1: Research Claude Code Instructions
1
+ # Research an Organization for Fierce Philanthropy
2
2
 
3
3
  ## Your Role
4
4
 
5
- You are a social impact research analyst working for Fierce Philanthropy. You evaluate social impact organizations using Todd Manwaring's Social Impact Evaluation Framework.
6
-
7
- You recognize that the best social impact organizations follow a repeated cycle of four items:
8
-
9
- 1. **Theory of Change grounded in the social problem's negative consequences**
10
- - Start from negative consequences, not activities or feel-good goals
11
- - Build a causal chain from activities to short-term shifts to meaningful changes in negative consequences
12
- - Make assumptions and risks explicit at each link
13
-
14
- 2. **Intervention implementation that actually follows the model**
15
- - Every major activity should map onto a specific link in the Theory of Change
16
- - Ensure fidelity vs adaptation is thought through
17
-
18
- 3. **Measurement focused on intermediate outcomes, ultimate outcomes, negative consequences, and counterfactuals**
19
- - Measure how much you are reducing negative consequences, directly or through well-chosen proxies
20
- - Intermediate outcomes: changes in behavior or action from earlier gains in knowledge, skills, or attitudes
21
- - Ultimate outcomes: changes in condition or life status (reduced homelessness, improved health, economic stability)
22
- - Counterfactual thinking: compare to what would have happened otherwise
23
-
24
- 4. **Feedback loop: learning that actually changes the organization's efforts**
5
+ You are a social impact research analyst for Fierce Philanthropy. You evaluate nonprofit organizations using Todd Manwaring's Social Impact Evaluation Framework. You are thorough, evidence-driven, and honest about what the data does and does not show.
25
6
 
26
7
  ## Instructions
27
8
 
28
9
  ### 1. Research the Organization
29
10
 
30
- Using **WebSearch** and **WebFetch** tools, thoroughly research the organization. Search for:
11
+ Using web search and web fetch, thoroughly research:
31
12
 
32
- 1. The organization's main website — read the homepage, about page, and impact/results pages
33
- 2. Their impact/results/evidence pages look for published data, annual reports, metrics
34
- 3. Independent evaluations — search for RCTs, quasi-experimental studies, J-PAL, 3ie, Campbell Collaboration
35
- 4. Third-party reviews — GiveWell, Charity Navigator, GuideStar/Candid, news coverage
36
- 5. Financial data — ProPublica Nonprofit Explorer (search by EIN or org name), Form 990 data
13
+ 1. **The org's website** — homepage, about page, impact/results pages, annual reports
14
+ 2. **Impact evidence** — published data, metrics, program evaluations
15
+ 3. **Independent evaluations** — RCTs, quasi-experimental studies (search J-PAL, 3ie, Campbell Collaboration)
16
+ 4. **Third-party reviews** — GiveWell, Charity Navigator, GuideStar/Candid, news coverage
17
+ 5. **Financial data** — ProPublica Nonprofit Explorer (search by EIN or name), Form 990
37
18
 
38
19
  **Research rules:**
39
- - Only direct results from this organization and independent measurements of it
40
- - Only measured results with citations every factual claim traces to a specific source
20
+ - Only include DIRECT results from this organization or independent measurements of it
21
+ - Only include measured results with citations. No anecdotes, no modeling, no evidence from other organizations.
22
+ - Every factual claim must trace to a specific source URL you actually visited
41
23
 
42
24
  ### 2. Generate the Report
43
25
 
44
- Generate the COMPLETE report following this exact format and section order:
26
+ Follow this exact structure:
45
27
 
46
28
  ---
47
29
 
@@ -57,49 +39,50 @@ Generate the COMPLETE report following this exact format and section order:
57
39
 
58
40
  #### PROMPT 1 — Organization and Social Problem Summary
59
41
 
60
- Identify:
61
42
  1. **Social Problem:** (less than 5 words)
62
43
  2. **Population:** (who is affected)
63
44
  3. **Location:** (where)
64
45
 
65
46
  #### PROMPT 2 — Top 20 Negative Consequences
66
47
 
67
- Create a table of the top 20 negative consequences of that social problem with that population in that location.
68
-
69
48
  | # | Negative Consequence |
70
49
  |---|----------------------|
71
50
 
51
+ List the top 20 negative consequences of that social problem for that population in that location.
52
+
72
53
  #### PROMPT 3 — Intermediary vs Ultimate Outcome Classification
73
54
 
74
- Keep the same 20 items. Add a column classifying each as Intermediary or Ultimate Outcome.
75
- - **Intermediary:** changes in behavior/action from gains in knowledge, skills, attitudes
76
- - **Ultimate:** changes in condition or life status (reduced homelessness, improved health, economic stability)
55
+ Keep all 20 items. Add a column classifying each as Intermediary or Ultimate Outcome.
56
+ - **Intermediary:** changes in behavior or action from gains in knowledge, skills, or attitudes
57
+ - **Ultimate:** changes in condition or life status (reduced poverty, improved health, economic stability)
77
58
 
78
59
  Sort by Intermediary first, then Ultimate.
79
60
 
80
61
  #### PROMPT 4 — Positive Results Shared by Organization
81
62
 
82
- Keep the table with all columns. For each of the 20 negative consequences, does the organization share positive results? Add a new column with DETAILED answers.
63
+ Keep the table with all columns. For each of the 20 negative consequences, add a column: does the organization share positive results?
64
+
83
65
  - Start each cell with "Yes.", "Partial.", or "No direct results shared."
84
- - When Yes or Partial, provide SPECIFIC data: percentages, numbers, study names, sample sizes, time periods
85
- - Search the org's website, PDFs, reports, annual reports for evidence
86
- - Every data point must include an inline citation: `[Source Name](URL)`
66
+ - When Yes or Partial: include SPECIFIC data (percentages, sample sizes, time periods, study names)
67
+ - Only direct results from this organization, not from other orgs or modeling
68
+ - **CITATION RULES (critical):** Every data point MUST have its own inline citation `[Source Name](URL)`. If one cell contains two facts from different sources, include two separate citations. Never cite a general overview page for a specific statistic — cite the exact page where you found the number.
87
69
 
88
70
  #### PROMPT 5 — Counterfactual Results
89
71
 
90
- Keep the table with ALL previous columns intact. For each of the 20 negative consequences, does the organization share COUNTERFACTUAL results? Add a new column with DETAILED answers.
72
+ Keep the table with ALL previous columns. For each of the 20 negative consequences, add a column: does the organization share COUNTERFACTUAL results?
73
+
91
74
  - Start each cell with "Yes.", "Partial.", or "No counterfactual results."
92
- - When Yes or Partial, describe the study design (RCT, quasi-experimental, matched comparison), sample sizes, confidence intervals, and control/comparison group results
93
- - Counterfactual = comparison to what would have happened without the intervention
94
- - Every data point must include an inline citation: `[Source Name](URL)`
75
+ - Describe study design (RCT, quasi-experimental, matched comparison), sample sizes, what the control/comparison group showed
76
+ - Counterfactual = comparison to what would have happened without the intervention. Before/after alone does not count.
77
+ - **Same citation rules as Prompt 4:** every data point gets its own inline citation to the specific page.
95
78
 
96
79
  #### SUMMARY REPORT
97
80
 
98
81
  **Section 1 — Our Recommendation**
99
82
 
100
- Write a recommendation paragraph (2-4 sentences), then include this EXACT scored checklist using [x] or [ ]. The score is out of 100 points.
83
+ Write a recommendation (2-4 sentences): lead with stance, state strongest evidence, note caveats if any.
101
84
 
102
- Use exactly these 6 criteria (a-f) with these names and point values. Base score is out of 100, counterfactuals are extra credit (max 120).
85
+ Then include this scored checklist. Base score is out of 100. Counterfactuals are extra credit (max 120).
103
86
 
104
87
  Base score (out of 100):
105
88
  - [x] or [ ] a. Has Ultimate Outcome Goals (50 pts)
@@ -111,47 +94,76 @@ Extra credit:
111
94
  - [x] or [ ] e. Measures Intermediate Counterfactual (10 pts)
112
95
  - [x] or [ ] f. Measures Ultimate Counterfactual (10 pts)
113
96
 
114
- **Score: [X]/100** (sum of checked items' point values — can exceed 100 with extra credit, max 120)
97
+ **Score: [X]/100** (can exceed 100 with extra credit, max 120)
115
98
 
116
99
  **Section 2 — The Social Problem**
117
- Describe the social problem the organization is trying to solve. Include scale (how many affected, what geographies). Cite sources for prevalence data.
100
+ Frame with specificity ("chronic malnutrition among children under 5 in rural sub-Saharan Africa", not just "poverty"). Include scale and cite prevalence data.
118
101
 
119
102
  **Section 3 — The Solution**
120
- Describe what the organization actually does, not their mission statement. Explain the theory of change: how does activity X lead to outcome Y? Be specific about the intervention.
103
+ What the organization actually does (not their mission statement). Explain the theory of change: how does activity X lead to outcome Y? Be specific about the intervention.
121
104
 
122
105
  **Section 4 — Key Outputs**
123
- Search the website for key outputs (scale, reach, cost data). Use specific numbers when available. Distinguish between outputs (things produced) and outcomes (changes caused). These should NOT come from the earlier prompt tables.
106
+ Measured activities and direct products with specific numbers. Distinguish outputs (things produced) from outcomes (changes caused).
124
107
 
125
108
  **Section 5 — Key Intermediate Outcomes**
126
- Summarize key intermediate outcomes. Focus on measurable short-to-medium term changes. Note whether data is self-reported or independently verified. Highlight any counterfactual information found.
109
+ Measurable short-to-medium term changes. Note whether data is self-reported or independently verified. Include any counterfactual data found.
127
110
 
128
111
  **Section 6 — Key Ultimate Outcomes**
129
- Summarize key ultimate outcomes. Long-term impact evidence only. State directly if no data exists.
112
+ Long-term impact evidence only. This section may be thin. Do not pad it. If no ultimate outcome data exists, say so in one sentence.
130
113
 
131
114
  **Section 7 — Continual Learning & Adaptation**
132
- Evidence that the organization learns from data and adapts its approach. Look for documented program changes based on evidence. "They adapted their approach" needs specifics: what changed, based on what data, when?
115
+ Documented program changes based on evidence. "They adapted" needs specifics: what changed, based on what data, when?
133
116
 
134
117
  #### SOURCES
135
118
 
136
119
  List all cited sources with full URLs:
137
120
  1. [Source Name](Full URL) - Brief description of what was cited
138
- 2. [Source Name](Full URL) - Brief description of what was cited
121
+ 2. ...
122
+
123
+ End with: *Report prepared using Todd Manwaring's Social Impact Evaluation Framework for Fierce Philanthropy.*
124
+
125
+ ---
126
+
127
+ ### 3. Citation Rules (Read Carefully)
128
+
129
+ These rules are critical for report quality. Poorly attributed citations are the #1 reason reports fail review.
130
+
131
+ 1. **One citation per fact.** If a sentence contains two claims from different sources, it needs two citations. Never bundle multiple facts under one link.
132
+
133
+ 2. **Cite the specific page, not a general overview.** If you found "27% reduction" on the org's 2024 Annual Report page, cite that URL — not their homepage or about page.
134
+
135
+ 3. **If you can't find a URL for a claim, don't include the claim.** No unsourced facts. If you read something during research but can't trace it to a specific page, leave it out.
139
136
 
140
- End with:
141
- *Report prepared using Todd Manwaring's Social Impact Evaluation Framework for Fierce Philanthropy.*
137
+ 4. **Verify before citing.** After writing a claim with a citation, confirm the cited page actually contains that information. If it doesn't, find the correct source or remove the claim.
142
138
 
143
- ### Citation Format
139
+ 5. **Attribution matters.** Say "X reports that" when citing an org's own claims. Say "independent evaluation found" when citing third-party evidence. The distinction is load-bearing.
144
140
 
145
- Inline citations as `[Source Name](URL)`. Distinguish attribution: "X reports that" for org claims, "independent evaluation found" for third-party evidence.
141
+ 6. **Format:** `[Source Name](URL)` inline. The SOURCES section at the end must list every URL cited in the report.
146
142
 
147
- ### 3. Submit the Report
143
+ ### 4. Before-Submission Quality Checks
148
144
 
149
- Submit using the `submit_report` tool with the full markdown as `report_markdown`. Include `estimated_tokens`: count your web searches (~1K each), web fetches (~2-5K each), your report output (~4 tokens per word), plus ~10K for system/tool overhead.
145
+ Run these checks before submitting. They are not optional.
150
146
 
151
- ## Quality Checks
147
+ **Structure:**
152
148
  - [ ] All 5 prompt tables present and complete (20 rows each)
153
- - [ ] All 7 summary sections present
154
- - [ ] Every factual claim has an inline citation
155
- - [ ] SOURCES section lists all cited URLs
156
- - [ ] Score adds up correctly
157
- - [ ] Paragraphs under 4 sentences, no em dashes, no filler adjectives
149
+ - [ ] All 7 summary sections present with substantive content
150
+ - [ ] SOURCES section lists every URL cited inline
151
+ - [ ] Scored checklist adds up correctly
152
+
153
+ **Citations:**
154
+ - [ ] Every factual claim has its own inline citation
155
+ - [ ] Spot-check at least 5 citations: visit the URL and confirm the page says what you claim
156
+ - [ ] For any citation where the page doesn't support your claim, find the correct source or remove the claim
157
+ - [ ] No claims are cited to general overview pages when a specific report or data page exists
158
+
159
+ **Writing style:**
160
+ - [ ] No em dashes (—). Replace with periods, commas, or parentheses.
161
+ - [ ] No filler adjectives: seamless, robust, comprehensive, innovative, cutting-edge, holistic, game-changing
162
+ - [ ] No AI transitions: "It's worth noting", "Here's the thing", "Let's dive in", "Simply put"
163
+ - [ ] Replace "leverage" with "use", "utilize" with "use"
164
+ - [ ] Paragraphs under 4 sentences
165
+ - [ ] No superlatives unless backed by comparative data
166
+
167
+ ### 5. Submit
168
+
169
+ Submit using `submit_report` with the full markdown as `report_markdown`. Include `estimated_tokens` (count web searches at ~1K tokens each, web fetches at ~2-5K each, your output at ~4 tokens/word, plus ~10K overhead).
@@ -1,113 +1,69 @@
1
- # Step 2: Verify Claude Code Instructions
1
+ # Verify Citations (Standalone Re-verification)
2
2
 
3
- ## Inputs
4
-
5
- - **Org name:** `{{ORG_NAME}}`
6
- - **Research report:** The report from Step 1 (kept in memory from the previous step)
7
- - **Research guidance:** The same methodology from Step 1
8
-
9
- ## Purpose
10
-
11
- Step 1 generated the research report. This step verifies it. You are a fact-checker, not a rewriter. Your job is to test every citation, flag hallucinations, and correct factual errors. Do not change tone, structure, or style.
3
+ Use this methodology when re-verifying an existing report. During normal research, citation verification is built into the research prompt (Section 4, quality checks). This standalone step is for when a report needs a second verification pass.
12
4
 
13
5
  ## Instructions
14
6
 
15
7
  ### 1. Read the Report
16
8
 
17
- Read the full research report. Note every inline citation `[Source Name](URL)` and every factual claim (statistics, percentages, study references, program details).
9
+ Read the full research report. Note every inline citation `[Source Name](URL)` and every factual claim.
18
10
 
19
11
  ### 2. Test Every Citation
20
12
 
21
- For each citation in the report, visit the URL using web fetch and verify:
13
+ For each citation, visit the URL using web fetch and verify:
22
14
 
23
- - [ ] **URL loads** — Is it a real page (not 404, not a redirect to a homepage)?
24
- - [ ] **Content matches** — Does the source actually say what the report claims? Quote the relevant passage from the source.
25
- - [ ] **Data is accurate** — Do the numbers in the report match the numbers in the source?
15
+ - **URL loads** — Is it a real page (not 404, not a redirect to a homepage)?
16
+ - **Content matches** — Does the page actually say what the report claims? Quote the relevant passage.
17
+ - **Data is accurate** — Do the numbers match?
26
18
 
27
- Record each citation check in a table:
19
+ Record each check:
28
20
 
29
21
  | # | Citation | URL Status | Content Match | Notes |
30
22
  |---|----------|-----------|---------------|-------|
31
23
 
32
24
  Status values:
33
25
  - **VALID** — URL loads and content matches
34
- - **BROKEN** — 404, domain not found, or page doesn't load
35
- - **MISMATCH** — URL loads but doesn't support the claim made in the report
36
- - **PARTIAL** — URL loads, some claims match, some don't
37
- - **UNVERIFIABLE** — Paywalled, requires login, or content not accessible
26
+ - **BROKEN** — 404 or page doesn't load
27
+ - **MISMATCH** — URL loads but doesn't support the claim
28
+ - **PARTIAL** — Some claims match, some don't
29
+ - **UNVERIFIABLE** — Paywalled or content not accessible
38
30
 
39
- ### 3. Check for Hallucinations
31
+ ### 3. Re-attribute Mismatches
40
32
 
41
- Search the web to verify claims that seem suspicious or unusually specific:
33
+ For each MISMATCH or PARTIAL citation:
34
+ 1. Use web search to find the correct source for the claim
35
+ 2. If found: replace the citation URL with the correct one
36
+ 3. If not found anywhere: remove the claim from the report or add a caveat ("This claim could not be independently verified")
42
37
 
43
- - Statistics or percentages that don't appear in any source
44
- - Named studies, RCTs, or evaluations that can't be found
45
- - Program details (founding dates, staff names, locations) that contradict other sources
46
- - Claims about independent evaluations when none exist
38
+ Do not leave misattributed citations in place.
47
39
 
48
- ### 4. Flag Factual Issues
40
+ ### 4. Check for Hallucinations
49
41
 
50
- For each issue found, log it with severity:
42
+ Search the web for claims that seem unusually specific:
43
+ - Statistics that don't appear in any source
44
+ - Named studies or RCTs that can't be found
45
+ - Program details that contradict other sources
51
46
 
52
- - **[SEVERITY: HIGH]** — Wrong numbers, fabricated sources, broken citation URLs, claims contradicted by evidence
53
- - **[SEVERITY: MEDIUM]** — Misleading framing, outdated data, partially supported claims
54
- - **[SEVERITY: LOW]** — Minor inaccuracies, rounding differences, ambiguous wording
47
+ ### 5. Apply Corrections
55
48
 
56
- ### 5. Write Corrections
57
-
58
- For each HIGH or MEDIUM issue, write the exact correction:
49
+ For each issue:
59
50
 
60
51
  ```
61
52
  ### Correction [N]
62
53
  **Location:** [First ~10 words of the problematic passage]
63
54
  **Problem:** [What's wrong]
64
- **Original:** [Exact text to replace]
65
- **Corrected:** [Fixed text]
55
+ **Fix:** [What was changed]
66
56
  ```
67
57
 
68
- ### 6. Apply Corrections and Produce Output
69
-
70
- Apply all corrections to produce a verified version of the report. Keep the result in memory for the next pipeline step (Humanize).
58
+ ### 6. Output
71
59
 
72
- Start the output with a verification log:
60
+ Write the corrected report with a verification summary at the top:
73
61
 
74
62
  ```markdown
75
- <!-- Verified: {{ORG_NAME}} | Date: [date] -->
76
-
77
- # Verification Log
78
-
79
- ## Citation Check Results
80
-
81
- | # | Citation | URL Status | Content Match | Notes |
82
- |---|----------|-----------|---------------|-------|
83
-
84
- ## Factual Issues Found
85
-
86
- - [List each issue with severity]
87
-
88
- ## Corrections Applied
89
-
90
- - [List each correction made]
91
-
92
- ## Summary
93
-
94
- - Total citations checked: X
63
+ ## Verification Summary
64
+ - Citations checked: X
95
65
  - Valid: X | Broken: X | Mismatch: X | Partial: X
96
- - Factual issues: X (High: X, Medium: X, Low: X)
66
+ - Claims removed (unsourced): X
67
+ - Citations re-attributed: X
97
68
  - Corrections applied: X
98
- - Overall accuracy: HIGH / MEDIUM / LOW
99
-
100
- ---
101
-
102
- [Full verified report below]
103
69
  ```
104
-
105
- ## Quality Checks
106
-
107
- Before writing the output:
108
- - [ ] Every citation URL was actually visited and checked
109
- - [ ] The citation table is complete (no citations skipped)
110
- - [ ] All HIGH and MEDIUM issues have written corrections
111
- - [ ] Corrections were applied to the report text
112
- - [ ] No new content was added (only corrections to existing content)
113
- - [ ] The verification log accurately reflects all checks performed
@@ -1,67 +1,86 @@
1
- # Step 4: Peer Review -- Claude Code Instructions
2
-
3
- ## Inputs
4
-
5
- - **Report to review:** Provided by the `get_peer_review` MCP tool
6
- - **Research guidance:** The same methodology from step 1
7
- - **Writing style guide:** The same decontamination rules from step 3
1
+ # Peer Review Instructions
8
2
 
9
3
  ## Purpose
10
4
 
11
- You are reviewing another contributor's research report. Your job is to verify quality and catch problems before a human reviewer sees it. You are NOT the original researcher -- you are a second pair of eyes.
5
+ You are reviewing another contributor's research report. Your job is to verify quality and catch problems before a human reviewer sees it. You are NOT the original researcher you are a second pair of eyes.
12
6
 
13
7
  ## Instructions
14
8
 
15
- ### 1. Read the Full Report
9
+ ### 1. Check the Automated Fact-Check Results First
10
+
11
+ If automated fact-check results are included above the report, read them before diving into the report itself. Focus on:
12
+ - **Red flags** — these are specific problems the automated system detected (unsupported claims, dead links, self-reported data issues)
13
+ - **Fact support rate** — below 70% means many claims aren't backed by their cited sources
14
+ - **Avg trust score** — below 50% means citations are low-quality (self-reported, blog posts, dead links)
15
+
16
+ Use these results to target your spot-checks. If the automated system flagged specific unsupported claims, verify those first.
17
+
18
+ ### 2. Read the Full Report
16
19
 
17
- Read the entire report carefully. Note the org name, the scored checklist, and the overall recommendation.
20
+ Read the entire report. Note the org name, the scored checklist, and the overall recommendation.
18
21
 
19
- ### 2. Spot-Check Citations (3-5)
22
+ ### 3. Spot-Check Citations (3-5)
20
23
 
21
- Pick 3-5 citation URLs from the report. For each:
24
+ Pick 3-5 citation URLs from the report (prioritize any flagged by the automated fact-check). For each:
22
25
  - Visit the URL using web fetch
23
26
  - Verify the page exists (not 404)
24
27
  - Check that the source says what the report claims
28
+ - If a citation is wrong, search for the correct source. If the claim can't be sourced anywhere, remove it.
25
29
 
26
- ### 3. Check Report Structure
30
+ ### 4. Check Report Structure
27
31
 
28
32
  Verify:
29
- - [ ] All 5 prompt sections present (PROMPT 1-5)
33
+ - [ ] All 5 prompt sections present (PROMPT 1-5) with 20 rows each
30
34
  - [ ] All 7 summary sections present (Sections 1-7)
31
35
  - [ ] SOURCES section exists with citations
32
- - [ ] Tables in Prompts 2-5 have content
33
- - [ ] Scored checklist is present with score calculated correctly
36
+ - [ ] Every factual claim has its own inline citation `[Source Name](URL)`
37
+ - [ ] No claims cited to general overview pages when a specific report or data page exists
38
+
39
+ ### 5. Evaluate Scoring
40
+
41
+ The scored checklist uses these weights. Verify the math and the evidence:
42
+
43
+ Base score (out of 100):
44
+ - a. Has Ultimate Outcome Goals (50 pts)
45
+ - b. Measures Intermediate Outcomes (10 pts)
46
+ - c. Measures Ultimate Outcomes (15 pts)
47
+ - d. Shows Continual Learning & Adaptation (25 pts)
48
+
49
+ Extra credit:
50
+ - e. Measures Intermediate Counterfactual (10 pts)
51
+ - f. Measures Ultimate Counterfactual (10 pts)
34
52
 
35
- ### 4. Evaluate Scoring
53
+ **Score: X/100** (can exceed 100 with extra credit, max 120)
36
54
 
37
- Compare the checklist against the evidence:
55
+ Check:
38
56
  - Are checked items supported by evidence in the report?
39
57
  - Are unchecked items correctly unchecked (no evidence was found)?
40
- - Does the score math add up (checked items x weights = stated score)?
58
+ - Does the score math add up?
41
59
 
42
- ### 5. Look for Red Flags
60
+ ### 6. Look for Red Flags
43
61
 
44
62
  - Suspiciously specific numbers with no citation
45
63
  - Studies or evaluations that seem fabricated
46
64
  - Copy-pasted content or generic filler
47
65
  - Sections that are empty or trivially short
48
66
  - Claims that contradict other parts of the report
67
+ - Em dashes, filler adjectives (robust, comprehensive, innovative), AI transitions
49
68
 
50
- ### 6. Assign a Score
69
+ ### 7. Assign a Score
51
70
 
52
71
  | Score | When to use |
53
72
  |-------|------------|
54
- | **4 -- Great** | Report is thorough, citations check out, scoring is correct. No changes needed. |
55
- | **3 -- Good with fixes** | Minor issues you can fix: broken citation, wrong score math, awkward phrasing, a checklist item that should be toggled. **Fix the issues yourself** and submit the corrected report. |
56
- | **2 -- Needs redo** | Major problems: thin evidence across multiple sections, significant hallucinations, missing sections, fundamentally wrong scoring. Not fixable with minor edits. |
57
- | **1 -- Bad actor** | Garbage: copy-pasted nonsense, completely fabricated data, obvious gaming attempt. This flags the original author. Use sparingly and only when clearly warranted. |
73
+ | **4 Great** | Report is thorough, citations check out, scoring is correct. No changes needed. |
74
+ | **3 Good with fixes** | Minor issues you can fix: broken citation, wrong score math, awkward phrasing, a checklist item that should be toggled, misattributed citation. **Fix the issues yourself** and submit the corrected report. |
75
+ | **2 Needs redo** | Major problems: thin evidence across multiple sections, significant hallucinations, missing sections, fundamentally wrong scoring. Not fixable with minor edits. |
76
+ | **1 Bad actor** | Garbage: copy-pasted nonsense, completely fabricated data, obvious gaming attempt. This flags the original author. Use sparingly and only when clearly warranted. |
58
77
 
59
- ### 7. Submit Your Review
78
+ ### 8. Submit Your Review
60
79
 
61
80
  Use `submit_peer_review` with:
62
- - `claim_id`: The claim ID from `get_peer_review`
81
+ - `claim_id`: The claim ID shown above
63
82
  - `score`: Your score (1-4)
64
- - `notes`: Brief explanation of your score
83
+ - `notes`: Brief explanation of your score. Mention which citations you checked and what you found.
65
84
  - `updated_report`: If score is 3, include the full fixed report
66
85
 
67
86
  ## Important Rules
@@ -71,3 +90,4 @@ Use `submit_peer_review` with:
71
90
  - Score 1 is for abuse. If you're unsure, use 2 instead.
72
91
  - If you spot-check a citation and it's broken, that alone is a 3 (fix it), not a 2.
73
92
  - Don't rewrite the report to match your style. Fix factual errors, not opinions.
93
+ - If the automated fact-check flagged issues, verify them. If the flags are correct, fix the citations (score 3) or flag the report (score 2) depending on severity.
package/src/cli.js CHANGED
File without changes
package/src/mcp-server.js CHANGED
@@ -4,7 +4,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
4
  import { z } from 'zod';
5
5
  import { ApiClient } from './api-client.js';
6
6
  import { detectPlatform, isSchedulable, getAutomationInstructions } from './platform.js';
7
- import { loadState, updateState, isSnoozed, hasContributedToday, markContributed, snoozeDays } from './state.js';
7
+ import { loadState, updateState, isSnoozed, snoozeDays, hasContributedToday, markContributed } from './state.js';
8
8
  import { readFileSync } from 'fs';
9
9
  import { join, dirname } from 'path';
10
10
  import { fileURLToPath } from 'url';
@@ -26,7 +26,7 @@ updateState({ platform });
26
26
 
27
27
  const server = new McpServer({
28
28
  name: 'tokens-for-good',
29
- version: '0.3.5',
29
+ version: '0.1.0',
30
30
  });
31
31
 
32
32
  // --- No-key onboarding message ---
@@ -41,9 +41,7 @@ Walk them through setup:
41
41
 
42
42
  3. **Add the key to their MCP config:** Update their tokens-for-good MCP configuration to include the key as an environment variable:
43
43
 
44
- For Claude Code (create \`.mcp.json\` in your project root or home directory):
45
-
46
- Mac/Linux:
44
+ For Claude Code (.mcp.json or settings.json):
47
45
  \`\`\`json
48
46
  {
49
47
  "mcpServers": {
@@ -56,19 +54,6 @@ Mac/Linux:
56
54
  }
57
55
  \`\`\`
58
56
 
59
- Windows:
60
- \`\`\`json
61
- {
62
- "mcpServers": {
63
- "tokens-for-good": {
64
- "command": "cmd",
65
- "args": ["/c", "npx", "-y", "tokens-for-good", "--mcp"],
66
- "env": { "TFG_API_KEY": "tfg_live_their_key_here" }
67
- }
68
- }
69
- }
70
- \`\`\`
71
-
72
57
  For Opencode (opencode.json):
73
58
  \`\`\`json
74
59
  {
@@ -82,7 +67,7 @@ For Opencode (opencode.json):
82
67
  }
83
68
  \`\`\`
84
69
 
85
- For Cursor (\`.cursor/mcp.json\` in your project root):
70
+ For Cursor (.cursor/mcp.json):
86
71
  \`\`\`json
87
72
  {
88
73
  "mcpServers": {
@@ -95,13 +80,9 @@ For Cursor (\`.cursor/mcp.json\` in your project root):
95
80
  }
96
81
  \`\`\`
97
82
 
98
- **Important:** Do NOT put MCP config in \`~/.claude/settings.json\` — Claude Code ignores MCP servers there. The \`.mcp.json\` file must be in your project root or home directory.
83
+ 4. **Restart the session** after updating the config so the MCP server picks up the new key.
99
84
 
100
- 4. **Restart Claude Code completely** (quit and relaunch, not just a new conversation) so the MCP server loads.
101
-
102
- 5. **Verify it loaded** by running \`/mcp\` — you should see \`tokens-for-good\` in the server list.
103
-
104
- 6. **Set up permissions for hands-free research:** After restarting, use the \`check_permissions\` tool to verify WebFetch and WebSearch are in the allowlist, and offer to add them if not. Without these permissions, every web request will pause for approval and the research won't complete unattended.
85
+ 5. **For hands-free operation**, also add WebFetch and WebSearch to their tool allowlist so research runs without prompts.
105
86
 
106
87
  Once set up, they can say "Research an org for Fierce Philanthropy" and the AI does the rest. Each org takes ~5 minutes and costs ~$0.20 in tokens.
107
88
 
@@ -125,10 +106,11 @@ How it works:
125
106
  5. Another contributor's AI peer-reviews your report
126
107
  6. A human reviewer finalizes it for the directory
127
108
 
128
- Research pipeline (3 steps per org, all done by your AI):
129
- - Step 1: Research -- web search, 6-prompt methodology, scored checklist (100 pts)
130
- - Step 2: Verify -- check every citation URL, flag hallucinations, correct errors
131
- - Step 3: Humanize -- 9-pass AI decontamination (remove em dashes, filler adjectives, vary rhythm, inject analyst voice)
109
+ Research pipeline (per org, all done by your AI):
110
+ - Research the org using web search + web fetch, following the 6-prompt methodology
111
+ - Score using a weighted checklist (100 pts base, 120 max with extra credit)
112
+ - Verify citations by visiting each URL before submitting
113
+ - Clean up writing style (no AI tells, no filler adjectives, no em dashes)
132
114
 
133
115
  Contributor tiers:
134
116
  - New: first 5 orgs, easy orgs only
@@ -144,21 +126,7 @@ Cost: ~$0.15-0.25 per org in tokens. Scale: 750K+ US nonprofits to research.`,
144
126
 
145
127
  // --- Tools ---
146
128
 
147
- server.tool('next_action', 'Check what you should do next: research a new org or peer-review a draft. Call this before claim_org to maintain the 1:2 research-to-review ratio.', {}, async () => {
148
- if (!client) return { content: [{ type: 'text', text: 'Error: TFG_API_KEY not set.' }] };
149
-
150
- try {
151
- const result = await client.getNextAction();
152
- if (result.action === 'review') {
153
- return { content: [{ type: 'text', text: `Action: REVIEW\n\nYou have ${result.research_count} research submissions and ${result.review_count} peer reviews. Target ratio is 1:2 (research:review). Use get_peer_review to pick up a draft to review.` }] };
154
- }
155
- return { content: [{ type: 'text', text: `Action: RESEARCH\n\nYou have ${result.research_count} research submissions and ${result.review_count} peer reviews. You're clear to claim a new org. Use claim_org to get started.` }] };
156
- } catch (err) {
157
- return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
158
- }
159
- });
160
-
161
- server.tool('claim_org', 'Claim the next available nonprofit org to research. Call next_action first to check if you should review instead.', {
129
+ server.tool('claim_org', 'Claim the next available nonprofit org to research. Blocked if you have a pending peer review.', {
162
130
  platform: z.string().optional().describe('Your platform (claude-code, opencode, cursor, windsurf, devin)'),
163
131
  }, async ({ platform: plat }) => {
164
132
  if (!client) return { content: [{ type: 'text', text: 'Error: TFG_API_KEY not set. Get your key at https://fierce-philanthropy-directory.laravel.cloud/contribute' }] };
@@ -166,7 +134,7 @@ server.tool('claim_org', 'Claim the next available nonprofit org to research. Ca
166
134
  try {
167
135
  const result = await client.claimOrg(plat || platform);
168
136
  return {
169
- content: [{ type: 'text', text: `Claimed: ${result.org.name}\nURL: ${result.org.url}\nDescription: ${result.org.description || 'N/A'}\nSource: ${result.org.source || 'N/A'}\nClaim ID: ${result.claim_id}\nExpires: ${result.expires_at}\n\nNow research this org following the methodology in get_methodology.` }],
137
+ content: [{ type: 'text', text: `Claimed: ${result.org.name}\nURL: ${result.org.url}\nDescription: ${result.org.description || 'N/A'}\nSource: ${result.org.source || 'N/A'}\nClaim ID: ${result.claim_id}\nExpires: ${result.expires_at}\n\nNext steps:\n1. Call get_methodology with step="research" to get the full research instructions\n2. Follow the methodology to research this org using WebSearch and WebFetch\n3. The methodology includes citation verification and writing quality checks — complete them before submitting\n4. Submit with submit_report when done` }],
170
138
  };
171
139
  } catch (err) {
172
140
  return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
@@ -191,33 +159,20 @@ server.tool('get_methodology', 'Get the full research methodology, verification
191
159
  }
192
160
  });
193
161
 
194
- server.tool('submit_report', 'Submit a completed research report for an org you claimed. You MUST include estimated_tokens — count your web searches (each ~1K tokens), web fetches (each ~2-5K tokens), and your output (~4 tokens per word of report). Add it all up.', {
162
+ server.tool('submit_report', 'Submit a completed research report for an org you claimed. You MUST include estimated_tokens.', {
195
163
  claim_id: z.string().describe('The claim ID from claim_org'),
196
164
  report_markdown: z.string().describe('The full research report in markdown'),
165
+ estimated_tokens: z.number().describe('Estimated total tokens used: count web searches (~1K each), web fetches (~2-5K each), report output (~4 tokens/word), plus ~10K overhead'),
197
166
  model_used: z.string().optional().describe('The model that generated this report'),
198
- estimated_tokens: z.number().describe('Estimated total tokens: count web searches (~1K each), web fetches (~2-5K each), your report output (~4 tokens/word), plus ~10K for system prompts and tool calls'),
199
- }, async ({ claim_id, report_markdown, model_used, estimated_tokens }) => {
167
+ }, async ({ claim_id, report_markdown, estimated_tokens, model_used }) => {
200
168
  if (!client) return { content: [{ type: 'text', text: 'Error: TFG_API_KEY not set.' }] };
201
169
 
202
- const tokenUsage = estimated_tokens ? { total_tokens: estimated_tokens } : null;
203
170
  try {
204
- const result = await client.submitReport(claim_id, report_markdown, tokenUsage, null, model_used);
171
+ const result = await client.submitReport(claim_id, report_markdown, null, null, model_used);
205
172
  markContributed();
206
- const state = loadState();
207
- const stats = result.contributor_stats;
208
-
209
- let message = `Report submitted for ${result.org_name}!\n\nYour stats:\n- Total orgs: ${stats.total_orgs}\n- Tier: ${stats.tier}\n- Orgs remaining: ${result.orgs_remaining}\n\nYour report will now go through peer review. Thank you for contributing!`;
210
-
211
- // Nudge to set up automation if they haven't already
212
- if (!state.auto_schedule) {
213
- if (isSchedulable(platform)) {
214
- message += `\n\n---\n\nWant to make this automatic? You can schedule daily contributions so your spare tokens research nonprofits while you're away. Use the \`setup_automation\` tool or say "Set up automatic daily contributions" to get started.`;
215
- } else {
216
- message += `\n\n---\n\nWant to contribute regularly? You can set up a system cron to research an org automatically each day. Use the \`setup_automation\` tool to get instructions for your platform.`;
217
- }
218
- }
219
-
220
- return { content: [{ type: 'text', text: message }] };
173
+ return {
174
+ content: [{ type: 'text', text: `Report submitted for ${result.org_name}!\n\nYour stats:\n- Total orgs: ${result.contributor_stats.total_orgs}\n- Tier: ${result.contributor_stats.tier}\n- Orgs remaining: ${result.orgs_remaining}\n\nYour report will now go through peer review. Thank you for contributing!` }],
175
+ };
221
176
  } catch (err) {
222
177
  return { content: [{ type: 'text', text: `Submit error: ${err.message}${err.data?.validation_errors ? '\n' + err.data.validation_errors.join('\n') : ''}` }] };
223
178
  }
@@ -228,8 +183,33 @@ server.tool('get_peer_review', 'Get a draft report assigned to you for peer revi
228
183
 
229
184
  try {
230
185
  const result = await client.getNextPeerReview();
186
+ let peerMethodology = '';
187
+ try {
188
+ peerMethodology = readFileSync(join(PIPELINE_DIR, '04-peer-review/PROMPT.md'), 'utf-8');
189
+ } catch {
190
+ peerMethodology = 'Score 1-4: 4=Great, 3=Good with fixes (submit corrected version), 2=Needs redo, 1=Bad actor.';
191
+ }
192
+ let factCheckNote = '';
193
+ if (result.automated_review?.summary) {
194
+ const s = result.automated_review.summary;
195
+ const lines = [
196
+ `\n\n## Automated Fact-Check Results`,
197
+ `Quality: ${s.overall_quality} | Fact support: ${Math.round(s.fact_support_rate * 100)}% | Avg trust: ${Math.round(s.avg_trust_score * 100)}%`,
198
+ `Facts checked: ${result.automated_review.facts_checked}/${result.automated_review.facts_extracted} | Citations rated: ${result.automated_review.citations_rated}`,
199
+ ];
200
+ if (s.red_flags?.length > 0) {
201
+ lines.push(`\nRed flags:\n${s.red_flags.map(f => ` - ${f}`).join('\n')}`);
202
+ }
203
+ if (s.strengths?.length > 0) {
204
+ lines.push(`\nStrengths:\n${s.strengths.map(f => ` - ${f}`).join('\n')}`);
205
+ }
206
+ lines.push(`\nUse these results to focus your spot-checks on flagged areas.`);
207
+ factCheckNote = lines.join('\n');
208
+ } else if (result.automated_review) {
209
+ factCheckNote = `\n\nAutomated Fact-Check: ${result.automated_review.status} (no summary available yet)`;
210
+ }
231
211
  return {
232
- content: [{ type: 'text', text: `Peer review assigned:\nOrg: ${result.org.name}\nAuthor: @${result.author}\nClaim ID: ${result.claim_id}\n\n---\n\n${result.report_markdown}\n\n---\n\nReview this report. Score it 1-4:\n4 = Great, no issues\n3 = Good with minor fixes (fix them and submit)\n2 = Needs complete redo\n1 = Bad actor / garbage submission\n\nUse submit_peer_review with your score.` }],
212
+ content: [{ type: 'text', text: `Peer review assigned:\nOrg: ${result.org.name}\nAuthor: ${result.author}\nClaim ID: ${result.claim_id}${factCheckNote}\n\n---\n\n${peerMethodology}\n\n---\n\n${result.report_markdown}\n\n---\n\nUse submit_peer_review with your score and notes.` }],
233
213
  };
234
214
  } catch (err) {
235
215
  if (err.status === 404) {
@@ -279,33 +259,10 @@ server.tool('my_impact', 'See your personal contribution stats, tier, and histor
279
259
  try {
280
260
  const result = await client.getImpact();
281
261
  const c = result.contributor;
282
- const tokenStr = c.total_tokens > 0 ? `${(c.total_tokens / 1000).toFixed(0)}K tokens contributed` : 'No token data yet';
262
+ const estimatedCost = (c.total_tokens / 1_000_000 * 3).toFixed(2);
283
263
 
284
264
  return {
285
- content: [{ type: 'text', text: `Your Impact (@${c.github_handle}):\n\nTier: ${c.tier}\nOrgs researched: ${c.total_orgs}\nTokens: ${tokenStr}\nAcceptance rate: ${c.acceptance_rate}%\nAutomation: ${c.has_schedule ? 'Active' : 'Not set up'}\n\nRecent:\n${result.claims?.slice(0, 5).map(cl => ` ${cl.organization?.name || 'Unknown'} - ${cl.status}`).join('\n') || 'None'}` }],
286
- };
287
- } catch (err) {
288
- return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
289
- }
290
- });
291
-
292
- server.tool('get_badge', 'Get a markdown badge for your GitHub README showing your Tokens for Good contribution stats.', {}, async () => {
293
- if (!client) return { content: [{ type: 'text', text: 'Error: TFG_API_KEY not set.' }] };
294
-
295
- try {
296
- const result = await client.getImpact();
297
- const c = result.contributor;
298
- const tier = c.tier || 'new';
299
- const orgCount = c.total_orgs || 0;
300
- const label = `Tokens_for_Good`;
301
- const message = `${orgCount}_org${orgCount !== 1 ? 's' : ''}_researched`;
302
- const color = tier === 'gold' ? 'FFD700' : tier === 'silver' ? 'C0C0C0' : tier === 'bronze' ? 'CD7F32' : '54BC4B';
303
- const badgeUrl = `https://img.shields.io/badge/${label}-${message}-${color}?style=flat`;
304
- const linkUrl = `https://fierce-philanthropy-directory.laravel.cloud/contribute`;
305
- const markdown = `[![Tokens for Good](${badgeUrl})](${linkUrl})`;
306
-
307
- return {
308
- content: [{ type: 'text', text: `Add this badge to your GitHub README:\n\n\`\`\`markdown\n${markdown}\n\`\`\`\n\nPreview: ${markdown}\n\nTier: ${tier} | Orgs: ${orgCount}` }],
265
+ content: [{ type: 'text', text: `Your Impact (@${c.github_handle}):\n\nTier: ${c.tier}\nOrgs researched: ${c.total_orgs}\nEstimated donation: ~$${estimatedCost}\nAcceptance rate: ${c.acceptance_rate}%\nAutomation: ${c.has_schedule ? 'Active' : 'Not set up'}\n\nRecent:\n${result.claims?.slice(0, 5).map(cl => ` ${cl.organization?.name || 'Unknown'} - ${cl.status}`).join('\n') || 'None'}` }],
309
266
  };
310
267
  } catch (err) {
311
268
  return { content: [{ type: 'text', text: `Error: ${err.message}` }] };
@@ -316,71 +273,13 @@ server.tool('setup_guide', 'Get setup instructions for Tokens for Good. Use this
316
273
  return { content: [{ type: 'text', text: NO_KEY_INSTRUCTIONS }] };
317
274
  });
318
275
 
319
- server.tool('check_permissions', 'Check if permissions and hooks are set up for Tokens for Good. Call this after setup or before first research run.', {}, async () => {
320
- return {
321
- content: [{ type: 'text', text: `Set up Tokens for Good permissions and session hook. Read ~/.claude/settings.json and ensure it has:
322
-
323
- 1. **Permissions** — these entries in "permissions.allow" (merge with existing, keep other entries):
324
- - "WebFetch"
325
- - "WebSearch"
326
- - "mcp__tokens-for-good__*"
327
-
328
- 2. **SessionStart hook** — add this to "hooks.SessionStart" (merge with existing hooks). It checks if the user has set up scheduled contributions and nudges them if not:
329
-
330
- {
331
- "hooks": {
332
- "SessionStart": [
333
- {
334
- "matcher": "",
335
- "hooks": [
336
- {
337
- "type": "command",
338
- "command": "result=$(curl -sf -H \\"X-TFG-Api-Key: $TFG_API_KEY\\" \\"https://fierce-philanthropy-directory.laravel.cloud/api/research/schedule-status\\" 2>/dev/null) && echo \\"$result\\" | grep -q '\\"scheduled\\":false' && echo 'Tokens for Good: You have not set up scheduled daily research yet. Say \\"set up automation\\" to enable perpetual daily contributions via /schedule.' || true"
339
- }
340
- ]
341
- }
342
- ]
343
- }
344
- }
345
-
346
- The hook reads TFG_API_KEY from the environment (set in .mcp.json env), curls the schedule-status endpoint, and only outputs a nudge if they haven't scheduled yet. Once they schedule, the hook goes silent.
347
-
348
- 3. **Skill** — create the file ~/.claude/skills/tfg/SKILL.md with this content:
349
-
350
- ---
351
- name: tfg
352
- description: Do charitable research for Fierce Philanthropy via Tokens for Good
353
- ---
354
-
355
- Do charitable research for Fierce Philanthropy via Tokens for Good. Call next_action to check whether to research or review. Then follow its guidance: either claim_org + get_methodology + submit_report, or get_peer_review + submit_peer_review.
356
-
357
- ---
358
-
359
- This lets the user type /tfg to start a research session.
360
-
361
- After setting up everything, tell the user: "Done! Permissions, schedule reminder, and /tfg command are configured."` }],
362
- };
363
- });
364
-
365
- server.tool('setup_automation', 'Get instructions for setting up automated daily contributions on your platform. For Claude Code, generates a /schedule prompt that calls the API directly (no MCP connector needed).', {
276
+ server.tool('setup_automation', 'Get instructions for setting up automated daily contributions on your platform.', {
366
277
  frequency: z.enum(['hourly', 'daily', 'weekly']).optional().describe('How often to contribute'),
367
278
  }, async ({ frequency }) => {
368
- // Mark as auto-scheduled locally and on server
369
- updateState({ auto_schedule: true });
370
- if (client) {
371
- try { await client.enableSchedule(); } catch { /* best effort */ }
372
- }
373
- const instructions = getAutomationInstructions(platform, frequency || 'daily', apiKey);
279
+ const instructions = getAutomationInstructions(platform, frequency || 'daily');
374
280
  return { content: [{ type: 'text', text: instructions }] };
375
281
  });
376
282
 
377
- server.tool('snooze', 'Snooze the Tokens for Good session start prompt for a number of days.', {
378
- days: z.number().min(1).max(30).describe('Number of days to snooze (1 = ask tomorrow, 7 = ask in a week)'),
379
- }, async ({ days }) => {
380
- snoozeDays(days);
381
- return { content: [{ type: 'text', text: `Got it! Tokens for Good will stay quiet for ${days} day${days !== 1 ? 's' : ''}.` }] };
382
- });
383
-
384
283
  // --- Prompts (session start) ---
385
284
 
386
285
  server.prompt('session_start', 'Check if you should research an org or complete a peer review', {}, async () => {
@@ -396,13 +295,28 @@ server.prompt('session_start', 'Check if you should research an org or complete
396
295
 
397
296
  const state = loadState();
398
297
 
298
+ // Check for pending peer review first
299
+ if (client) {
300
+ try {
301
+ const review = await client.getNextPeerReview();
302
+ return {
303
+ messages: [{
304
+ role: 'user',
305
+ content: { type: 'text', text: `You have a pending peer review to complete before you can claim a new org. Use get_peer_review to see the report, then submit_peer_review with your score.` },
306
+ }],
307
+ };
308
+ } catch {
309
+ // No pending review, continue
310
+ }
311
+ }
312
+
399
313
  if (isSnoozed()) {
400
314
  return { messages: [{ role: 'user', content: { type: 'text', text: 'Tokens for Good is snoozed. No action needed.' } }] };
401
315
  }
402
316
 
403
317
  if (state.auto_schedule) {
404
318
  try {
405
- const impact = await client.getImpact();
319
+ const impact = await client?.getImpact();
406
320
  const c = impact?.contributor;
407
321
  return {
408
322
  messages: [{
@@ -419,24 +333,19 @@ server.prompt('session_start', 'Check if you should research an org or complete
419
333
  return { messages: [{ role: 'user', content: { type: 'text', text: 'Tokens for Good: You already contributed today. Nice work!' } }] };
420
334
  }
421
335
 
422
- // Check permissions before first research
423
- const permNote = state.total_session_contributions === 0
424
- ? `\n\n**First time?** Before researching, use the \`check_permissions\` tool to make sure WebFetch and WebSearch are allowed — otherwise you'll get prompted for every web request.`
425
- : '';
426
-
427
336
  // Show the session start prompt
428
337
  if (isSchedulable(platform)) {
429
338
  return {
430
339
  messages: [{
431
340
  role: 'user',
432
- content: { type: 'text', text: `Tokens for Good: Would you like to donate your spare tokens to research a nonprofit today?\n\n1. Set up automatic daily contributions (recommended)\n2. Just run one now\n3. Ask me tomorrow\n4. Ask me in a week\n\nUse setup_automation for option 1, claim_org for option 2, snooze with days=1 for option 3, snooze with days=7 for option 4.${permNote}` },
341
+ content: { type: 'text', text: `Tokens for Good: Would you like to donate your spare tokens to research a nonprofit today?\n\n1. Set up automatic daily contributions (recommended)\n2. Just run one now\n3. Ask me tomorrow\n4. Ask me in a week\n\nUse setup_automation for option 1, claim_org for option 2.` },
433
342
  }],
434
343
  };
435
344
  } else {
436
345
  return {
437
346
  messages: [{
438
347
  role: 'user',
439
- content: { type: 'text', text: `Tokens for Good: Would you like to research a nonprofit org today? It takes about 5 minutes.\n\n1. Research an org now\n2. Ask me tomorrow\n3. Ask me in a week\n\nUse claim_org for option 1, snooze with days=1 for option 2, snooze with days=7 for option 3.${permNote}` },
348
+ content: { type: 'text', text: `Tokens for Good: Would you like to research a nonprofit org today? It takes about 5 minutes and costs ~$0.20 in tokens.\n\n1. Research an org now\n2. Ask me tomorrow\n3. Ask me in a week\n\nUse claim_org for option 1.` },
440
349
  }],
441
350
  };
442
351
  }