@haposoft/cafekit 0.7.7 → 0.7.9

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
@@ -827,6 +827,43 @@ function copyRulesDirectory(platformKey, results, options = {}) {
827
827
  }
828
828
  }
829
829
 
830
+ // Ensure .gitignore configured at root
831
+ function ensureGitignore(results, options = {}) {
832
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
833
+ const header = '# CafeKit / Ecosystem';
834
+ const patterns = [
835
+ 'specs/_shared/',
836
+ 'plans/',
837
+ '!plans/templates/'
838
+ ];
839
+
840
+ if (!fs.existsSync(gitignorePath)) {
841
+ const content = ['# Git Ignore', '', header, ...patterns, ''].join('\n');
842
+ fs.writeFileSync(gitignorePath, content, 'utf8');
843
+ console.log(' ✓ .gitignore created at root');
844
+ results.copied++;
845
+ return;
846
+ }
847
+
848
+ const content = fs.readFileSync(gitignorePath, 'utf8');
849
+ const lines = content.split('\n').map(l => l.trim());
850
+ const missing = patterns.filter(p => !lines.includes(p));
851
+
852
+ if (missing.length > 0) {
853
+ let newContent = content;
854
+ if (!newContent.endsWith('\n')) newContent += '\n';
855
+ if (!content.includes(header)) newContent += `\n${header}\n`;
856
+
857
+ newContent += missing.join('\n') + '\n';
858
+ fs.writeFileSync(gitignorePath, newContent, 'utf8');
859
+ console.log(` ↻ .gitignore updated: added ${missing.join(', ')}`);
860
+ results.updated++;
861
+ } else {
862
+ console.log(' → .gitignore already up to date');
863
+ results.skipped++;
864
+ }
865
+ }
866
+
830
867
  // ═══════════════════════════════════════════════════════════
831
868
  // GEMINI CLI SETUP
832
869
  // ═══════════════════════════════════════════════════════════
@@ -1024,6 +1061,12 @@ async function main() {
1024
1061
  console.log();
1025
1062
  }
1026
1063
 
1064
+ // Ensure root .gitignore is configured correctly
1065
+ console.log('Root Configuration');
1066
+ console.log('-'.repeat(40));
1067
+ ensureGitignore(results, installerOptions);
1068
+ console.log();
1069
+
1027
1070
  // Setup Gemini CLI and API Key config
1028
1071
  await setupGeminiCLI(platforms);
1029
1072
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haposoft/cafekit",
3
- "version": "0.7.7",
3
+ "version": "0.7.9",
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.
@@ -50,6 +50,7 @@ Before concluding any brainstorm session, verify each measurement metric:
50
50
  1. **Engineering Trinity:** YAGNI, KISS, and DRY.
51
51
  2. **Brutal Honesty:** Interrogate assumptions. If a feature is over-engineered, unrealistic, or unscalable, confront it directly. Your value lies in preventing costly mistakes.
52
52
  3. **Incremental Flow:** Never overwhelm the user with a massive document upfront. Proceed step by step, section by section.
53
+ 4. **Web Search Protocol:** When needing to search the internet for references, benchmarks, or latest practices, ALWAYS use `node .claude/scripts/web-search.cjs "query"` first (Gemini Grounding). Use native WebSearch as secondary. Use `docs-fetch.js` only for known library docs.
53
54
 
54
55
  ## Ecosystem Alliances (Collaboration Tools)
55
56
 
@@ -11,6 +11,8 @@ Goal: Catch the mistakes AI-written code commonly makes — logic errors, securi
11
11
 
12
12
  You DO NOT fix code. You only READ, SCORE, and REPORT.
13
13
 
14
+ **Web Search Protocol:** When needing to verify security best practices or lookup CVE databases, ALWAYS use `node .claude/scripts/web-search.cjs "query"` first (Gemini Grounding). Use native WebSearch as secondary.
15
+
14
16
  ## Pre-Review: Blast Radius Check (MANDATORY)
15
17
 
16
18
  Before reading any specific logic, you MUST run a Dependency Scope Check (Blast Radius):
@@ -19,6 +19,7 @@ Any logic gaps must be clarified BEFORE typing, not discovered after bugs ship.
19
19
  - **Token efficiency**: Write concisely, report briefly, no prose.
20
20
  - **Surgical Reading (Large Files):** Never use blanket `Read` commands on files > 800 lines. Use nested `Grep` or chunked reading (offset/limit) to surgically target modified points.
21
21
  - **Component Scaffold Limit:** Any React/UI component file that exceeds 200 LOC must trigger a proactive modularization step (split into smaller child files).
22
+ - **Web Search Protocol:** When needing to search the internet, ALWAYS use `node .claude/scripts/web-search.cjs "query"` first (Gemini Grounding). Use native WebSearch as secondary. Use `docs-fetch.js` only for known library docs.
22
23
 
23
24
  ## Self-Check Checklist (Before Reporting Complete)
24
25
 
@@ -15,6 +15,7 @@ Unlike typical managers who report on "feelings" or conversational summaries, yo
15
15
  1. **Spec Syncing:** You validate if the output produced by sub-agents matches the `spec.json` requirements and the `design.md` architectural constraints.
16
16
  2. **Blocker Assassination:** You identify task stagnation (e.g., a spec stuck in 'in-progress' across multiple sessions) and force the immediate assignment of next-step actions.
17
17
  3. **Agile Aggregation:** When parallel sub-agents (like `god-developer` and `test-runner`) report completion, you sweep their logs, consolidate the facts, and generate a single authoritative **Feature Release Report**.
18
+ 4. **Web Search Protocol:** When needing to search for project management best practices, dependency updates, or changelog references, ALWAYS use `node .claude/scripts/web-search.cjs "query"` first (Gemini Grounding). Use native WebSearch as secondary.
18
19
 
19
20
  ## Execution Constraints
20
21
 
@@ -41,14 +41,20 @@ 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.
48
50
 
49
- ## Report Output Format
51
+ ## Report Output Routing
50
52
 
51
- Adhere to the exact hierarchical naming injection logic provided by the Hook system (`## Naming`). Generated file paths must encompass explicit broadcasting timestamp references.
53
+ Save research output based on context:
54
+ - **Feature research** (active spec exists) → `specs/<feature>/research.md`
55
+ - **System-wide research** (no active spec) → `specs/_shared/Research-<slug>-<date>.md`
56
+
57
+ Do NOT save to `plans/reports/` or `docs/`. All research belongs in `specs/`.
52
58
 
53
59
  ## Team Operations Mode
54
60
 
@@ -19,6 +19,7 @@ You DO NOT write implementation code. You produce Specifications that downstream
19
19
  - **The 5 Whys:** Dig past the surface request to find the REAL problem.
20
20
  - **80/20 MVP:** Identify the 20% of features that deliver 80% of value.
21
21
  - **Systems Thinking:** How does this feature connect to (or break) existing systems?
22
+ - **Web Search Protocol:** When needing to search the internet, ALWAYS use `node .claude/scripts/web-search.cjs "query"` first (Gemini Grounding). Use native WebSearch as secondary. Use `docs-fetch.js` only for known library docs.
22
23
 
23
24
  ## Pre-Completion Checklist
24
25
 
@@ -18,6 +18,7 @@ You are an award-caliber UI/UX designer. You merge aesthetic excellence with eng
18
18
  - **Micro-interactions:** Purposeful animations that enhance UX without performance cost.
19
19
  - **Accessibility:** WCAG 2.1 AA compliance as a baseline, not an afterthought.
20
20
  - **3D/WebGL:** Three.js scene composition, shader development (when appropriate).
21
+ - **Web Search Protocol:** When needing to search the internet for design trends, component libraries, or accessibility guides, ALWAYS use `node .claude/scripts/web-search.cjs "query"` first (Gemini Grounding). Use native WebSearch as secondary. Use `docs-fetch.js` only for known library docs.
21
22
 
22
23
  ## Design Workflow
23
24
 
@@ -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": {
@@ -5,8 +5,8 @@
5
5
  Every subagent prompt **must** include these three paths:
6
6
 
7
7
  - **Work Context** — the git root containing the target files
8
- - **Reports Directory** — `{work_context}/plans/reports/`
9
- - **Plans Directory** — `{work_context}/plans/`
8
+ - **Specs Directory** — `{work_context}/specs/`
9
+ - **Docs Directory** — `{work_context}/docs/`
10
10
 
11
11
  When CWD and work context differ (e.g., editing files in a sibling project), always use the **work context** paths.
12
12
 
@@ -14,8 +14,8 @@ When CWD and work context differ (e.g., editing files in a sibling project), alw
14
14
  Example prompt:
15
15
  "Resolve the date-parsing regression.
16
16
  Work context: /repos/billing-service
17
- Reports: /repos/billing-service/plans/reports/
18
- Plans: /repos/billing-service/plans/"
17
+ Specs: /repos/billing-service/specs/
18
+ Docs: /repos/billing-service/docs/"
19
19
  ```
20
20
 
21
21
  ---
@@ -97,11 +97,11 @@ Files to modify: [list]
97
97
  Files to read for context: [list]
98
98
  Acceptance criteria: [list]
99
99
  Constraints: [any relevant constraints]
100
- Plan reference: [phase file path if applicable]
100
+ Spec reference: [spec folder path if applicable]
101
101
 
102
102
  Work context: [project path]
103
- Reports: [reports path]
104
- Plans: [plans path]
103
+ Specs: [specs path]
104
+ Docs: [docs path]
105
105
  ```
106
106
 
107
107
  ### Do
@@ -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
 
@@ -36,6 +38,16 @@ Instruct the Researcher Subagent with this strict requirement:
36
38
  > "Sử dụng nguyên bản template tại `packages/spec/src/claude/skills/specs/templates/research.md`. Tuyệt đối không tự ý đẻ thêm các đề mục ngoài phạm vi file template này."
37
39
 
38
40
  ## Post-Execution
39
- Once the `researcher` completes the Task and returns the Markdown output:
40
- 1. Save the file cleanly using the naming path format injected by the Context Hook (typically `plans/reports/Report-<slug>-<date>.md`).
41
- 2. Conclude the workflow by providing the user with the file path.
41
+ Once the `researcher` completes the Task and returns the Markdown output, save it based on context:
42
+
43
+ ### Output Routing
44
+ | Context | Save to | Example |
45
+ |---|---|---|
46
+ | Active spec exists (`specs/<feature>/`) | `specs/<feature>/research.md` | `specs/auth-login/research.md` |
47
+ | No active spec (system-wide / general) | `specs/_shared/Research-<slug>-<date>.md` | `specs/_shared/Research-mv3-best-practices-2026-04-11.md` |
48
+
49
+ ### Rules
50
+ 1. **Feature research** → Always save inside the active spec folder. If `specs/<feature>/` doesn't exist yet, create it.
51
+ 2. **System-wide research** → Save to `specs/_shared/`. Create the directory if it doesn't exist.
52
+ 3. **Never** save to `plans/reports/` or `docs/`. All research belongs in `specs/`.
53
+ 4. Conclude the workflow by providing the user with the saved file path.
@@ -402,8 +402,8 @@ async function main() {
402
402
  const session = JSON.parse(fs.readFileSync(sessionPath, 'utf8'));
403
403
  const planPath = session.activePlan?.trim();
404
404
  if (planPath) {
405
- // Extract slug from path like "plans/260106-1554-statusline-visual"
406
- const match = planPath.match(/plans\/\d+-\d+-(.+?)(?:\/|$)/);
405
+ // Extract slug from path like "specs/auth-login" or legacy "plans/260106-1554-feature"
406
+ const match = planPath.match(/(?:specs|plans)\/(?:\d+-\d+-)?(.+?)(?:\/|$)/);
407
407
  activePlan = match ? match[1] : planPath.split('/').pop();
408
408
  }
409
409
  }