@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.destDir, 'references');
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.6",
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",
@@ -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/docs-fetch.js` sniper utility precisely to rip documentation pages directly into context buffers.
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.
@@ -53,7 +53,8 @@
53
53
  "required": [
54
54
  "docs-fetch.js",
55
55
  "validate-docs.cjs",
56
- "browser-tool.cjs"
56
+ "browser-tool.cjs",
57
+ "web-search.cjs"
57
58
  ]
58
59
  },
59
60
  "agentReferences": {
@@ -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: Limit WebSearch calls to a maximum of 5 distinct queries to conserve context.
27
- Constraint 2: Utilize scripts/docs-fetch.js if official Github/Doc URLs are discovered.
28
- Constraint 3: Validate information via cross-referencing capabilities.
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