@haposoft/cafekit 0.7.6 → 0.7.8
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/bin/install.js
CHANGED
|
@@ -543,7 +543,7 @@ function copyPlatformFiles(platformKey, results, options = {}) {
|
|
|
543
543
|
// Copy agent reference manuals (debugger manuals, etc.) recursively
|
|
544
544
|
const refsSource = path.join(__dirname, '../src', platform.sourceDir, 'references');
|
|
545
545
|
if (fs.existsSync(refsSource)) {
|
|
546
|
-
const refsDest = path.join(platform.
|
|
546
|
+
const refsDest = path.join(platform.folder, 'references');
|
|
547
547
|
const refsExisted = fs.existsSync(refsDest);
|
|
548
548
|
copyRecursive(refsSource, refsDest, options);
|
|
549
549
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haposoft/cafekit",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8",
|
|
4
4
|
"description": "Spec-Driven Development workflow for AI coding assistants. Supports Claude Code and Antigravity with spec-first workflows plus Claude Code hapo: skills.",
|
|
5
5
|
"author": "Haposoft <nghialt@haposoft.com>",
|
|
6
6
|
"license": "MIT",
|
package/src/claude/CLAUDE.md
CHANGED
|
@@ -64,6 +64,18 @@ When triggering Python scripts located under `.claude/skills/`, you must invoke
|
|
|
64
64
|
|
|
65
65
|
*Note: If a skill script throws an error, do not abandon the task. try run with venv. if error again, try fix and run
|
|
66
66
|
|
|
67
|
+
## Web Search Protocol
|
|
68
|
+
|
|
69
|
+
When you need to search the internet for information (research, docs lookup, troubleshooting, latest updates, etc.), follow this priority chain:
|
|
70
|
+
|
|
71
|
+
| Priority | Tool | Command | When to use |
|
|
72
|
+
|----------|------|---------|-------------|
|
|
73
|
+
| 🥇 **P1** | `web-search.cjs` | `node .claude/scripts/web-search.cjs "query"` | **ALWAYS try first.** Works on ALL models via Gemini Google Search Grounding. Supports `--multi "q1" "q2"` for batch. Returns JSON with answer + sources. |
|
|
74
|
+
| 🥈 **P2** | `WebSearch` (native) | Use WebSearch tool directly | Secondary verification, or when P1 fails/unavailable. |
|
|
75
|
+
| 🥉 **P3** | `docs-fetch.js` | `node .claude/scripts/docs-fetch.js "library"` | Only when you already know a specific library and need its raw documentation. |
|
|
76
|
+
|
|
77
|
+
**IMPORTANT**: When the user asks you to find information, research a topic, look up documentation, or investigate anything that requires internet access, you MUST use the Web Search Protocol above. Do NOT reply with "I cannot search the web" — you have `web-search.cjs` available via Bash.
|
|
78
|
+
|
|
67
79
|
## Code Refactoring Triggers
|
|
68
80
|
|
|
69
81
|
- **Size Thresholds**: Automatically consider splitting code files that grow beyond 200 lines.
|
|
@@ -41,7 +41,9 @@ You possess extreme proficiency in:
|
|
|
41
41
|
- Segregating Stable Production Practices away from Toxic Experimental Paradigms.
|
|
42
42
|
- Sniffing out valid Adoption Patterns and real-world implementation trending.
|
|
43
43
|
- Forgiving nothing when crafting Trade-off computational matrices for thousands of competing libraries.
|
|
44
|
-
- Deploying the `scripts/
|
|
44
|
+
- **[PRIORITY 1]** Deploying `scripts/web-search.cjs` as the **PRIMARY search tool** for all web queries. Usage: `node .claude/scripts/web-search.cjs "query"` or `node .claude/scripts/web-search.cjs --multi "q1" "q2"`. Returns JSON with answer, sources, and citations via Gemini Google Search Grounding. ALWAYS attempt this first before any other search method.
|
|
45
|
+
- **[PRIORITY 2]** If WebSearch native tool is available, use it as secondary verification or when `web-search.cjs` fails.
|
|
46
|
+
- **[PRIORITY 3]** Deploying `scripts/docs-fetch.js` only when official Github/Doc URLs are already identified and you need to pull raw documentation content.
|
|
45
47
|
- Deploying Bash and raw Grep utilities to surgically dissect embedded Document architectures and internal file payloads to evaluate raw insights.
|
|
46
48
|
|
|
47
49
|
**ABSOLUTE IMMOVEABLE DIRECTIVE**: You are **STRICTLY PROHIBITED** from generating executable endpoint "Implementation Code". You exist ONLY to maneuver data streams, render synthesis Summary text, and return comprehensive Markdown documentation pathways to the main caller Agent.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Web Search via Gemini API + Google Search Grounding
|
|
4
|
+
* Fallback search tool for models without native WebSearch capability.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node web-search.cjs "your search query here"
|
|
8
|
+
* node web-search.cjs --multi "query1" "query2" "query3"
|
|
9
|
+
*
|
|
10
|
+
* Requires: GEMINI_API_KEY in .claude/.env or environment
|
|
11
|
+
*
|
|
12
|
+
* Output: JSON-structured search results with sources and citations.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const https = require('https');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// ENV Resolution: .claude/.env → process.env
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function resolveApiKey() {
|
|
23
|
+
// Priority 1: Already in environment
|
|
24
|
+
if (process.env.GEMINI_API_KEY) return process.env.GEMINI_API_KEY;
|
|
25
|
+
|
|
26
|
+
// Priority 2: Project-local .claude/.env
|
|
27
|
+
const envPaths = [
|
|
28
|
+
path.join(process.cwd(), '.claude', '.env'),
|
|
29
|
+
path.join(process.cwd(), '..', '.claude', '.env'),
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
for (const envPath of envPaths) {
|
|
33
|
+
try {
|
|
34
|
+
if (fs.existsSync(envPath)) {
|
|
35
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
36
|
+
const match = content.match(/^GEMINI_API_KEY=(.+)$/m);
|
|
37
|
+
if (match) return match[1].trim().replace(/^["']|["']$/g, '');
|
|
38
|
+
}
|
|
39
|
+
} catch { /* skip */ }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Gemini API Call with Google Search Grounding
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
function callGemini(apiKey, query, model) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const payload = JSON.stringify({
|
|
51
|
+
contents: [{ parts: [{ text: query }] }],
|
|
52
|
+
tools: [{ googleSearch: {} }],
|
|
53
|
+
generationConfig: {
|
|
54
|
+
temperature: 0.1,
|
|
55
|
+
maxOutputTokens: 4096,
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const options = {
|
|
60
|
+
hostname: 'generativelanguage.googleapis.com',
|
|
61
|
+
path: `/v1beta/models/${model}:generateContent?key=${apiKey}`,
|
|
62
|
+
method: 'POST',
|
|
63
|
+
headers: {
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const req = https.request(options, (res) => {
|
|
70
|
+
let data = '';
|
|
71
|
+
res.on('data', chunk => data += chunk);
|
|
72
|
+
res.on('end', () => {
|
|
73
|
+
try {
|
|
74
|
+
const json = JSON.parse(data);
|
|
75
|
+
if (json.error) {
|
|
76
|
+
reject(new Error(`Gemini API Error: ${json.error.message}`));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
resolve(json);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
reject(new Error(`Failed to parse Gemini response: ${e.message}`));
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
req.on('error', reject);
|
|
87
|
+
req.write(payload);
|
|
88
|
+
req.end();
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Parse Grounding Metadata → Structured Output
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
function parseResponse(geminiResponse, query) {
|
|
96
|
+
const candidate = geminiResponse.candidates?.[0];
|
|
97
|
+
if (!candidate) return { query, error: 'No candidates returned' };
|
|
98
|
+
|
|
99
|
+
const text = candidate.content?.parts?.map(p => p.text).join('\n') || '';
|
|
100
|
+
const meta = candidate.groundingMetadata || {};
|
|
101
|
+
|
|
102
|
+
// Extract source URLs from groundingChunks
|
|
103
|
+
const sources = (meta.groundingChunks || []).map(chunk => ({
|
|
104
|
+
title: chunk.web?.title || 'Unknown',
|
|
105
|
+
url: chunk.web?.uri || '',
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
// Extract search queries used by the model
|
|
109
|
+
const searchQueries = meta.webSearchQueries || [];
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
query,
|
|
113
|
+
answer: text,
|
|
114
|
+
searchQueriesUsed: searchQueries,
|
|
115
|
+
sources,
|
|
116
|
+
sourceCount: sources.length,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Main
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
async function main() {
|
|
124
|
+
const args = process.argv.slice(2);
|
|
125
|
+
const isMulti = args[0] === '--multi';
|
|
126
|
+
const queries = isMulti ? args.slice(1) : [args.join(' ')];
|
|
127
|
+
|
|
128
|
+
if (queries.length === 0 || (queries.length === 1 && !queries[0])) {
|
|
129
|
+
console.error(JSON.stringify({
|
|
130
|
+
error: 'Usage: node web-search.cjs "your query" | node web-search.cjs --multi "q1" "q2"'
|
|
131
|
+
}));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const apiKey = resolveApiKey();
|
|
136
|
+
if (!apiKey) {
|
|
137
|
+
console.error(JSON.stringify({
|
|
138
|
+
error: 'GEMINI_API_KEY not found. Set it in .claude/.env or environment variable.'
|
|
139
|
+
}));
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Use model from env or default to gemini-2.5-flash
|
|
144
|
+
const model = process.env.SEARCH_MODEL || 'gemini-2.5-flash';
|
|
145
|
+
|
|
146
|
+
const results = [];
|
|
147
|
+
|
|
148
|
+
for (const query of queries) {
|
|
149
|
+
try {
|
|
150
|
+
const raw = await callGemini(apiKey, query, model);
|
|
151
|
+
results.push(parseResponse(raw, query));
|
|
152
|
+
} catch (err) {
|
|
153
|
+
results.push({ query, error: err.message });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Output as JSON for agent consumption
|
|
158
|
+
const output = isMulti ? results : results[0];
|
|
159
|
+
console.log(JSON.stringify(output, null, 2));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
main();
|
|
@@ -23,9 +23,11 @@ Call the `TaskCreate` tool to spin up the `researcher` subagent.
|
|
|
23
23
|
**Instructions to pass to Researcher:**
|
|
24
24
|
```text
|
|
25
25
|
Conduct comprehensive research on: [topic]
|
|
26
|
-
Constraint 1:
|
|
27
|
-
Constraint 2:
|
|
28
|
-
Constraint 3:
|
|
26
|
+
Constraint 1: ALWAYS use `node .claude/scripts/web-search.cjs "query"` as PRIMARY search method (supports --multi for batch). This uses Gemini Google Search Grounding and returns JSON with answer + sources.
|
|
27
|
+
Constraint 2: Use native WebSearch tool as secondary verification or when web-search.cjs fails.
|
|
28
|
+
Constraint 3: Use scripts/docs-fetch.js ONLY when official Github/Doc URLs are already identified.
|
|
29
|
+
Constraint 4: Limit total search calls to a maximum of 5 distinct queries to conserve context.
|
|
30
|
+
Constraint 5: Validate information via cross-referencing capabilities.
|
|
29
31
|
Output Format: Must strictly follow the 'Standard Research Report' layout.
|
|
30
32
|
```
|
|
31
33
|
|