wogiflow 1.0.48 → 1.0.50

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.
Files changed (53) hide show
  1. package/.claude/commands/wogi-research.md +223 -0
  2. package/.claude/docs/commands.md +17 -0
  3. package/.workflow/bridges/base-bridge.js +124 -15
  4. package/.workflow/bridges/claude-bridge.js +50 -22
  5. package/.workflow/bridges/codex-bridge.js +26 -1
  6. package/.workflow/bridges/cursor-bridge.js +26 -1
  7. package/.workflow/bridges/gemini-bridge.js +4 -1
  8. package/.workflow/bridges/kimi-bridge.js +30 -1
  9. package/.workflow/bridges/opencode-bridge.js +26 -1
  10. package/.workflow/templates/agents-md.hbs +127 -0
  11. package/.workflow/templates/claude-md.hbs +66 -0
  12. package/.workflow/templates/codex-config.hbs +69 -0
  13. package/.workflow/templates/cursor-rules.mdc.hbs +142 -0
  14. package/.workflow/templates/gemini-md.hbs +334 -26
  15. package/.workflow/templates/opencode-agents-md.hbs +158 -0
  16. package/.workflow/templates/opencode-config.hbs +27 -0
  17. package/.workflow/templates/partials/auto-features.hbs +125 -0
  18. package/.workflow/templates/partials/enforcement-rules.hbs +164 -0
  19. package/.workflow/templates/partials/user-commands.hbs +154 -0
  20. package/.workflow/templates/research-report.md +153 -0
  21. package/README.md +170 -1589
  22. package/package.json +4 -4
  23. package/scripts/flow-import-profile +17 -8
  24. package/scripts/flow-operational-scanner.js +13 -8
  25. package/scripts/flow-parity-check.js +281 -0
  26. package/scripts/flow-prompt-composer.js +10 -0
  27. package/scripts/flow-research-protocol.js +1022 -0
  28. package/scripts/flow-strict-adherence.js +14 -9
  29. package/scripts/flow-utils.js +4 -0
  30. package/scripts/hooks/adapters/base-adapter.js +22 -2
  31. package/scripts/hooks/adapters/claude-code.js +14 -1
  32. package/scripts/hooks/adapters/gemini.js +368 -0
  33. package/scripts/hooks/adapters/index.js +99 -0
  34. package/scripts/hooks/adapters/opencode.js +317 -0
  35. package/scripts/hooks/core/constants.js +75 -0
  36. package/scripts/hooks/core/implementation-gate.js +15 -1
  37. package/scripts/hooks/core/index.js +21 -1
  38. package/scripts/hooks/core/research-gate.js +306 -0
  39. package/scripts/hooks/entry/claude-code/pre-tool-use.js +1 -1
  40. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +47 -2
  41. package/scripts/hooks/entry/cursor/after-file-edit.js +127 -0
  42. package/scripts/hooks/entry/cursor/before-shell.js +131 -0
  43. package/scripts/hooks/entry/cursor/session-start.js +125 -0
  44. package/scripts/hooks/entry/cursor/stop.js +135 -0
  45. package/scripts/hooks/entry/gemini-cli/after-tool.js +119 -0
  46. package/scripts/hooks/entry/gemini-cli/before-agent.js +140 -0
  47. package/scripts/hooks/entry/gemini-cli/before-tool.js +189 -0
  48. package/scripts/hooks/entry/gemini-cli/session-end.js +120 -0
  49. package/scripts/hooks/entry/gemini-cli/session-start.js +80 -0
  50. package/scripts/hooks/entry/opencode/prompt-append.js +76 -0
  51. package/scripts/hooks/entry/opencode/session-start.js +86 -0
  52. package/scripts/hooks/entry/opencode/tool-after.js +77 -0
  53. package/scripts/hooks/entry/opencode/tool-before.js +143 -0
@@ -0,0 +1,223 @@
1
+ # /wogi-research - Zero-Trust Research Protocol
2
+
3
+ Execute rigorous research before answering questions about capabilities, feasibility, or existence.
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ /wogi-research "Does X support Y?" # Standard depth
9
+ /wogi-research --quick "Simple question" # Quick check (5K tokens)
10
+ /wogi-research --deep "Architecture query" # Deep audit (50K tokens)
11
+ /wogi-research --exhaustive "Critical decision" # Full audit (100K tokens)
12
+ ```
13
+
14
+ ## When This is Required
15
+
16
+ This command is **automatically triggered** (when strict mode is enabled) for:
17
+
18
+ 1. **Capability Questions**: "Does X support Y?", "Can X do Y?"
19
+ 2. **Feasibility Questions**: "Is it possible to...", "Can we..."
20
+ 3. **Existence Questions**: "Is there a...", "Does X exist?"
21
+ 4. **Architecture Questions**: "How does X work?", "How is X structured?"
22
+ 5. **Integration Questions**: "How to integrate X with Y?"
23
+
24
+ ## Research Protocol Phases
25
+
26
+ ### Phase 1: Scope Mapping
27
+ - Identify all potentially relevant local files
28
+ - Identify external tools/libraries mentioned
29
+ - Generate search keywords
30
+ - Create `research-scope.json`
31
+
32
+ ### Phase 2: Local Evidence Gathering
33
+ - Read ALL files identified in scope (not just the first match)
34
+ - Extract relevant code snippets and documentation
35
+ - Log findings to research notes
36
+ - **DO NOT SKIP FILES** - partial reading leads to false conclusions
37
+
38
+ ### Phase 3: External Verification
39
+ - For each external tool/library:
40
+ - Web search: "[tool] documentation [feature] [current year]"
41
+ - Read official docs (top 3 results minimum)
42
+ - Extract quotes with URLs
43
+ - **ASSUME training data is 2+ years stale**
44
+
45
+ ### Phase 4: Assumption Check
46
+ - List ALL assumptions made during research
47
+ - Tag each assumption:
48
+ - `[VERIFIED]` with HIGH confidence + source
49
+ - `[UNVERIFIED]` with LOW confidence - **MUST be verified before proceeding**
50
+ - Loop back to Phase 2/3 for any unverified assumptions
51
+
52
+ ### Phase 5: Synthesis
53
+ - Generate research report with:
54
+ - Answer to original question
55
+ - Evidence chain (every claim → source)
56
+ - Confidence level (HIGH/MEDIUM/LOW)
57
+ - Caveats and uncertainties
58
+ - List of searches performed
59
+
60
+ ## Critical Rules
61
+
62
+ ### The Negative Evidence Rule
63
+
64
+ **FORBIDDEN conclusions:**
65
+ - "X is not supported"
66
+ - "There is no Y"
67
+ - "It doesn't exist"
68
+ - "X cannot do Y"
69
+
70
+ **REQUIRED format for negative claims:**
71
+ ```
72
+ I searched the following sources and found no evidence of X:
73
+ 1. [source 1] - searched for [terms]
74
+ 2. [source 2] - searched for [terms]
75
+ 3. [official docs URL] - no mention found
76
+
77
+ However, my search may be incomplete. Before concluding X doesn't exist:
78
+ - Check if there's a different name for this feature
79
+ - Verify with the latest official documentation
80
+ - Consider that the feature may be in development
81
+ ```
82
+
83
+ ### The Version Paranoia Rule
84
+
85
+ For ANY external tool (npm packages, CLIs, APIs, frameworks):
86
+ ```
87
+ ASSUME: Training data is 2+ years old
88
+ ACTION: ALWAYS web search "[tool] latest documentation [current year]"
89
+ BEFORE making capability claims
90
+ ```
91
+
92
+ ### The Assumption Stack
93
+
94
+ Before answering, explicitly list:
95
+ ```markdown
96
+ ## My Assumptions
97
+ 1. [VERIFY] Gemini CLI version supports hooks → Confidence: LOW (training data)
98
+ 2. [OK] Project uses JavaScript → Confidence: HIGH (read package.json)
99
+ 3. [VERIFY] settings.json format → Confidence: LOW (haven't read docs)
100
+ ```
101
+
102
+ Any assumption marked `[VERIFY]` with `LOW` confidence **MUST** be verified.
103
+
104
+ ## Evidence Chain Format
105
+
106
+ Every claim needs a traceable source:
107
+
108
+ ```markdown
109
+ | Claim | Source Type | Source Location | Confidence |
110
+ |-------|-------------|-----------------|------------|
111
+ | "Hooks are supported" | Live Docs | github.com/x/docs/hooks | HIGH |
112
+ | "Settings format is X" | File Read | .gemini/settings.json | HIGH |
113
+ | "Feature Y exists" | Training Data | None | LOW - VERIFY |
114
+ ```
115
+
116
+ ## Depth Tiers
117
+
118
+ | Depth | Token Budget | Actions | Use For |
119
+ |-------|--------------|---------|---------|
120
+ | `--quick` | 5K | 1-2 files, no web search | Simple factual lookups |
121
+ | (default) | 20K | All relevant files, 1 web search | Most questions |
122
+ | `--deep` | 50K | Full file audit, multiple web searches | Architecture/feasibility |
123
+ | `--exhaustive` | 100K+ | Everything + user confirmation gates | Production decisions |
124
+
125
+ ## Output
126
+
127
+ The command generates:
128
+
129
+ 1. **research-report.md** - Full research findings with citations
130
+ 2. **Console summary** - Key findings and confidence level
131
+ 3. **Cached verifications** - Stored in `.workflow/state/research-cache.json`
132
+
133
+ ## Configuration
134
+
135
+ In `.workflow/config.json`:
136
+
137
+ ```json
138
+ {
139
+ "research": {
140
+ "enabled": true,
141
+ "defaultDepth": "standard",
142
+ "strictMode": true,
143
+ "autoTrigger": true,
144
+ "maxTokensPerDepth": {
145
+ "quick": 5000,
146
+ "standard": 20000,
147
+ "deep": 50000,
148
+ "exhaustive": 100000
149
+ },
150
+ "requireCitations": true,
151
+ "cacheVerifications": true,
152
+ "cacheExpiryHours": 24,
153
+ "budgetMode": "soft",
154
+ "negativeEvidenceRule": true,
155
+ "assumptionTracking": true
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Examples
161
+
162
+ ### Example 1: Capability Question
163
+
164
+ ```
165
+ User: Does Gemini CLI support hooks?
166
+
167
+ /wogi-research "Does Gemini CLI support hooks?"
168
+ ```
169
+
170
+ Research output:
171
+ ```
172
+ ## Research Report
173
+
174
+ **Question:** Does Gemini CLI support hooks?
175
+ **Depth:** standard
176
+ **Confidence:** HIGH
177
+
178
+ ### Conclusion
179
+ Yes, Gemini CLI supports hooks since version X.
180
+
181
+ ### Evidence Chain
182
+ | Claim | Source | Confidence |
183
+ |-------|--------|------------|
184
+ | Hooks supported | https://github.com/gemini-cli/docs/hooks | HIGH |
185
+ | Configuration in .gemini/settings.json | File read | HIGH |
186
+
187
+ ### Searches Performed
188
+ 1. Web: "Gemini CLI hooks documentation 2026"
189
+ 2. Local: .gemini/settings.json
190
+ 3. Local: .gemini/**/*.md
191
+ ```
192
+
193
+ ### Example 2: Architecture Question
194
+
195
+ ```
196
+ User: How does the authentication flow work in this codebase?
197
+
198
+ /wogi-research --deep "How does the authentication flow work?"
199
+ ```
200
+
201
+ This will:
202
+ 1. Search for auth-related files
203
+ 2. Read all matches (not just first)
204
+ 3. Trace the flow through the codebase
205
+ 4. Generate a comprehensive report
206
+
207
+ ## Integration with Hooks
208
+
209
+ When `research.strictMode` is enabled and `research.autoTrigger` is true:
210
+ - Capability/feasibility questions automatically trigger research
211
+ - Claims without citations are flagged
212
+ - Negative claims require exhaustive search evidence
213
+
214
+ ## CLI Compatibility
215
+
216
+ This command works across all supported CLIs:
217
+ - Claude Code
218
+ - Gemini CLI
219
+ - Codex (OpenAI)
220
+ - OpenCode
221
+ - Cline/Cursor
222
+
223
+ State is stored in `.workflow/` for cross-CLI persistence.
@@ -141,6 +141,15 @@ When user types these commands, execute the corresponding action immediately.
141
141
  |---------|--------|
142
142
  | `/wogi-guided-edit` | Guide through multi-file changes step by step. Shows each edit for approval. |
143
143
 
144
+ ### Research Protocol (Zero-Trust)
145
+
146
+ | Command | Action |
147
+ |---------|--------|
148
+ | `/wogi-research [question]` | Execute rigorous research before answering capability/feasibility questions. Phases: scope mapping, evidence gathering, external verification, assumption check, synthesis. |
149
+ | `/wogi-research --quick [q]` | Quick research (5K tokens) - 1-2 files, no web search. |
150
+ | `/wogi-research --deep [q]` | Deep research (50K tokens) - full file audit, multiple web searches. |
151
+ | `/wogi-research --exhaustive [q]` | Exhaustive research (100K+ tokens) - everything + user confirmation gates. |
152
+
144
153
  ### Planning & Documentation
145
154
 
146
155
  | Command | Action |
@@ -312,6 +321,14 @@ npx flow onboard # Analyze existing project & set up context
312
321
  ./scripts/flow figma confirm <f> # Interactive confirmation
313
322
  ./scripts/flow figma generate # Generate code from decisions
314
323
  ./scripts/flow figma server # Start MCP server
324
+
325
+ # Research Protocol
326
+ ./scripts/flow research "q" # Execute research protocol
327
+ ./scripts/flow research --quick # Quick research (5K tokens)
328
+ ./scripts/flow research --deep # Deep research (50K tokens)
329
+ ./scripts/flow research --exhaustive # Full audit (100K+ tokens)
330
+ ./scripts/flow research cache # Show cached verifications
331
+ ./scripts/flow research cache clear # Clear verification cache
315
332
  ```
316
333
 
317
334
  ## Command Execution
@@ -109,26 +109,64 @@ class BaseBridge {
109
109
 
110
110
  /**
111
111
  * Safe JSON parse that checks for prototype pollution (fallback when flow-utils unavailable)
112
+ *
113
+ * NOTE: This is a fallback implementation. When flow-utils is available,
114
+ * safeJsonParse from flow-utils is used instead (it performs recursive
115
+ * validation of the parsed object). This fallback only does pre-parse
116
+ * string checking which is less comprehensive but better than nothing.
117
+ *
112
118
  * @param {string} content - JSON string to parse
113
119
  * @returns {Object|null} Parsed object or null if invalid
114
120
  */
115
121
  safeJsonParseContent(content) {
116
- // Check for prototype pollution attempts (case-insensitive)
117
- const contentLower = content.toLowerCase();
118
- if (contentLower.includes('__proto__') ||
119
- contentLower.includes('constructor') ||
120
- contentLower.includes('prototype')) {
121
- this.log('Warning: Potential prototype pollution detected in JSON');
122
+ if (!content || typeof content !== 'string') {
122
123
  return null;
123
124
  }
124
125
 
126
+ // Check for prototype pollution attempts
127
+ // These patterns look for dangerous keys that could be used for prototype pollution
128
+ const dangerousPatterns = [
129
+ /__proto__/i,
130
+ /"constructor"\s*:/,
131
+ /"prototype"\s*:/
132
+ ];
133
+
134
+ for (const pattern of dangerousPatterns) {
135
+ if (pattern.test(content)) {
136
+ this.log('Warning: Potential prototype pollution detected in JSON');
137
+ return null;
138
+ }
139
+ }
140
+
125
141
  try {
126
142
  const parsed = JSON.parse(content);
127
143
  if (parsed === null || typeof parsed !== 'object') {
128
144
  return null;
129
145
  }
146
+
147
+ // Additional check: verify no __proto__ keys in parsed result
148
+ // (JSON.parse could still create them in some edge cases)
149
+ const hasProtoKey = (obj) => {
150
+ if (!obj || typeof obj !== 'object') return false;
151
+ if (Object.prototype.hasOwnProperty.call(obj, '__proto__')) return true;
152
+ for (const value of Object.values(obj)) {
153
+ if (typeof value === 'object' && value !== null && hasProtoKey(value)) {
154
+ return true;
155
+ }
156
+ }
157
+ return false;
158
+ };
159
+
160
+ if (hasProtoKey(parsed)) {
161
+ this.log('Warning: __proto__ key detected in parsed JSON');
162
+ return null;
163
+ }
164
+
130
165
  return parsed;
131
- } catch {
166
+ } catch (err) {
167
+ if (process.env.DEBUG) {
168
+ this.log(`JSON parse error: ${err.message}`);
169
+ }
132
170
  return null;
133
171
  }
134
172
  }
@@ -164,7 +202,8 @@ class BaseBridge {
164
202
  ensureCliFolder() {
165
203
  const cliFolder = path.join(this.projectDir, this.getCliFolder());
166
204
  if (!fs.existsSync(cliFolder)) {
167
- fs.mkdirSync(cliFolder, { recursive: true });
205
+ // Use explicit permissions (0o755 = rwxr-xr-x) to avoid relying on umask
206
+ fs.mkdirSync(cliFolder, { recursive: true, mode: 0o755 });
168
207
  this.log(`Created ${this.getCliFolder()}/`);
169
208
  }
170
209
  }
@@ -181,9 +220,9 @@ class BaseBridge {
181
220
  return;
182
221
  }
183
222
 
184
- // Ensure target directory exists
223
+ // Ensure target directory exists with explicit permissions
185
224
  if (!fs.existsSync(targetSkillsDir)) {
186
- fs.mkdirSync(targetSkillsDir, { recursive: true });
225
+ fs.mkdirSync(targetSkillsDir, { recursive: true, mode: 0o755 });
187
226
  }
188
227
 
189
228
  // Copy skills
@@ -214,9 +253,9 @@ class BaseBridge {
214
253
  syncRules() {
215
254
  const rulesDir = path.join(this.projectDir, this.getRulesPath());
216
255
 
217
- // Ensure rules directory exists
256
+ // Ensure rules directory exists with explicit permissions
218
257
  if (!fs.existsSync(rulesDir)) {
219
- fs.mkdirSync(rulesDir, { recursive: true });
258
+ fs.mkdirSync(rulesDir, { recursive: true, mode: 0o755 });
220
259
  }
221
260
 
222
261
  // Copy any existing rules from .workflow/rules/ if present
@@ -244,9 +283,9 @@ class BaseBridge {
244
283
  // Knowledge files to sync
245
284
  const knowledgeFiles = ['stack.md', 'architecture.md', 'testing.md'];
246
285
 
247
- // Ensure CLI docs directory exists
286
+ // Ensure CLI docs directory exists with explicit permissions
248
287
  if (!fs.existsSync(cliDocsDir)) {
249
- fs.mkdirSync(cliDocsDir, { recursive: true });
288
+ fs.mkdirSync(cliDocsDir, { recursive: true, mode: 0o755 });
250
289
  }
251
290
 
252
291
  let syncedCount = 0;
@@ -359,6 +398,76 @@ class BaseBridge {
359
398
  return results;
360
399
  }
361
400
 
401
+ // ==================== Template Utility Methods ====================
402
+
403
+ /**
404
+ * Load a partial template from .workflow/templates/partials/
405
+ * @param {string} partialName - Name of the partial (without .hbs extension)
406
+ * @returns {string} Partial content or empty string if not found
407
+ */
408
+ loadPartial(partialName) {
409
+ const partialPath = path.join(
410
+ this.projectDir,
411
+ this.workflowDir,
412
+ 'templates',
413
+ 'partials',
414
+ `${partialName}.hbs`
415
+ );
416
+
417
+ try {
418
+ if (fs.existsSync(partialPath)) {
419
+ return fs.readFileSync(partialPath, 'utf-8');
420
+ }
421
+ } catch (err) {
422
+ this.log(`Warning: Could not load partial ${partialName}: ${err.message}`);
423
+ }
424
+ return '';
425
+ }
426
+
427
+ /**
428
+ * Process partial includes in template content
429
+ * Replaces {{> partial-name}} with the partial content
430
+ * @param {string} content - Template content
431
+ * @returns {string} Content with partials included
432
+ */
433
+ processPartials(content) {
434
+ // Match {{> partial-name}} pattern
435
+ const partialRegex = /\{\{>\s*([a-zA-Z0-9_-]+)\s*\}\}/g;
436
+
437
+ return content.replace(partialRegex, (match, partialName) => {
438
+ const partialContent = this.loadPartial(partialName);
439
+ if (!partialContent) {
440
+ this.log(`Warning: Partial not found: ${partialName}`);
441
+ return `<!-- Partial not found: ${partialName} -->`;
442
+ }
443
+ return partialContent;
444
+ });
445
+ }
446
+
447
+ /**
448
+ * Get nested value from config object using dot notation
449
+ * @param {Object} obj - The object to search
450
+ * @param {string} path - Dot-separated path (e.g., 'hooks.rules.taskGating')
451
+ * @returns {*} The value or undefined
452
+ */
453
+ getNestedValue(obj, path) {
454
+ if (!path || typeof path !== 'string') return undefined;
455
+
456
+ const parts = path.split('.');
457
+ let current = obj;
458
+
459
+ for (const part of parts) {
460
+ if (current === null || current === undefined) return undefined;
461
+ // Security: skip dangerous property names
462
+ if (part === '__proto__' || part === 'constructor' || part === 'prototype') {
463
+ return undefined;
464
+ }
465
+ current = current[part];
466
+ }
467
+
468
+ return current;
469
+ }
470
+
362
471
  // ==================== Utility Methods ====================
363
472
 
364
473
  /**
@@ -366,7 +475,7 @@ class BaseBridge {
366
475
  */
367
476
  copyDirRecursive(source, target) {
368
477
  if (!fs.existsSync(target)) {
369
- fs.mkdirSync(target, { recursive: true });
478
+ fs.mkdirSync(target, { recursive: true, mode: 0o755 });
370
479
  }
371
480
 
372
481
  const items = fs.readdirSync(source);
@@ -59,12 +59,21 @@ class ClaudeBridge extends BaseBridge {
59
59
 
60
60
  /**
61
61
  * Generate CLAUDE.md from Handlebars-like template
62
- * Supports: {{variable}}, {{config.path}}, {{#if}}, {{#each}}, {{/if}}, {{/each}}
62
+ * Supports: {{variable}}, {{config.path}}, {{#if}}, {{#each}}, {{/if}}, {{/each}}, {{> partial}}
63
63
  */
64
64
  generateFromTemplate(templatePath, config) {
65
- const template = fs.readFileSync(templatePath, 'utf-8');
65
+ let template;
66
+ try {
67
+ template = fs.readFileSync(templatePath, 'utf-8');
68
+ } catch (err) {
69
+ this.log(`Warning: Could not read template ${templatePath}: ${err.message}`);
70
+ return this.generateDefaultClaudeMd(config);
71
+ }
66
72
  let content = template;
67
73
 
74
+ // Process {{> partial}} includes first (before other processing)
75
+ content = this.processPartials(content);
76
+
68
77
  // Process {{#if config.path.to.value}}...{{/if}} blocks
69
78
  // Non-greedy match to handle nested conditions
70
79
  content = this.processConditionals(content, config);
@@ -340,24 +349,47 @@ Last synced: ${new Date().toISOString()}
340
349
  }
341
350
 
342
351
  /**
343
- * Generate settings.local.json with wildcard permissions
344
- * Claude Code 2.1.0+ supports wildcards like Bash(npm *)
352
+ * Generate settings.local.json with permissions
353
+ * NOTE: Requires Claude Code 2.1.7+ which fixed wildcard matching of shell operators.
354
+ * See security-patterns.md rule #6 for details.
345
355
  */
346
356
  generateSettings(config) {
347
357
  const projectDir = this.projectDir;
348
358
 
349
- // Base wildcard permissions - covers most common use cases
359
+ // Permission rules - balancing security with workflow convenience
360
+ // Wildcards are safe in 2.1.7+ (shell operators rejected)
350
361
  const wildcardPermissions = [
351
- // Package managers
352
- 'Bash(npm *)',
362
+ // Package managers - specific safe operations
363
+ 'Bash(npm install *)',
364
+ 'Bash(npm run *)',
365
+ 'Bash(npm test *)',
366
+ 'Bash(npm exec *)',
367
+ 'Bash(npm ci)',
368
+ 'Bash(npm audit *)',
369
+ 'Bash(npm outdated *)',
370
+ 'Bash(npm ls *)',
371
+ 'Bash(npm version *)',
353
372
  'Bash(npx *)',
354
- 'Bash(yarn *)',
355
- 'Bash(pnpm *)',
356
- 'Bash(pip *)',
357
- 'Bash(python *)',
358
- 'Bash(python3 *)',
359
-
360
- // Git operations
373
+ 'Bash(yarn install *)',
374
+ 'Bash(yarn add *)',
375
+ 'Bash(yarn remove *)',
376
+ 'Bash(yarn run *)',
377
+ 'Bash(yarn test *)',
378
+ 'Bash(yarn build *)',
379
+ 'Bash(yarn dev *)',
380
+ 'Bash(pnpm install *)',
381
+ 'Bash(pnpm add *)',
382
+ 'Bash(pnpm remove *)',
383
+ 'Bash(pnpm run *)',
384
+ 'Bash(pnpm test *)',
385
+ 'Bash(pnpm build *)',
386
+ 'Bash(pnpm dev *)',
387
+ 'Bash(pip install *)',
388
+ 'Bash(pip list *)',
389
+ 'Bash(python -m *)',
390
+ 'Bash(python3 -m *)',
391
+
392
+ // Git operations - all safe read/write operations
361
393
  'Bash(git status)',
362
394
  'Bash(git status *)',
363
395
  'Bash(git diff *)',
@@ -386,18 +418,14 @@ Last synced: ${new Date().toISOString()}
386
418
  'Bash(./scripts/flow *)',
387
419
  'Bash(./scripts/flow)',
388
420
 
389
- // Common utilities
421
+ // Safe read-only utilities
390
422
  'Bash(ls *)',
391
423
  'Bash(tree *)',
392
- 'Bash(cat *)',
393
- 'Bash(head *)',
394
- 'Bash(tail *)',
395
424
  'Bash(wc *)',
396
- 'Bash(grep *)',
397
- 'Bash(find *)',
398
425
  'Bash(chmod +x *)', // Only make executable, not arbitrary permissions
399
- 'Bash(node *)',
400
- 'Bash(bash *)',
426
+ 'Bash(node --check *)',
427
+ 'Bash(node --version)',
428
+ 'Bash(bash -n *)', // Syntax check only
401
429
  'Bash(open *)',
402
430
  'Bash(test *)',
403
431
 
@@ -47,6 +47,28 @@ class CodexBridge extends BaseBridge {
47
47
  return path.join('.codex', 'rules');
48
48
  }
49
49
 
50
+ /**
51
+ * Register template partials with Handlebars
52
+ */
53
+ registerPartials() {
54
+ if (!Handlebars) return;
55
+
56
+ const partialsDir = path.join(this.projectDir, this.workflowDir, 'templates', 'partials');
57
+ if (!fs.existsSync(partialsDir)) return;
58
+
59
+ try {
60
+ const partialFiles = fs.readdirSync(partialsDir).filter(f => f.endsWith('.hbs'));
61
+ for (const file of partialFiles) {
62
+ const partialName = path.basename(file, '.hbs');
63
+ const partialContent = fs.readFileSync(path.join(partialsDir, file), 'utf-8');
64
+ Handlebars.registerPartial(partialName, partialContent);
65
+ this.log(`Registered partial: ${partialName}`);
66
+ }
67
+ } catch (err) {
68
+ this.log(`Warning: Could not register partials: ${err.message}`);
69
+ }
70
+ }
71
+
50
72
  /**
51
73
  * Generate AGENTS.md content
52
74
  */
@@ -55,6 +77,9 @@ class CodexBridge extends BaseBridge {
55
77
 
56
78
  // Try to use Handlebars template with proper error handling
57
79
  if (Handlebars) {
80
+ // Register partials before compiling
81
+ this.registerPartials();
82
+
58
83
  try {
59
84
  const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'agents-md.hbs');
60
85
  if (fs.existsSync(templatePath)) {
@@ -68,7 +93,7 @@ class CodexBridge extends BaseBridge {
68
93
  }
69
94
  }
70
95
 
71
- // Fallback to inline generation
96
+ // Fallback to inline generation (includes partial content directly)
72
97
  return this.generateAgentsMdFallback(context);
73
98
  }
74
99
 
@@ -55,6 +55,28 @@ class CursorBridge extends BaseBridge {
55
55
  return path.join('.cursor', 'rules');
56
56
  }
57
57
 
58
+ /**
59
+ * Register template partials with Handlebars
60
+ */
61
+ registerPartials() {
62
+ if (!Handlebars) return;
63
+
64
+ const partialsDir = path.join(this.projectDir, this.workflowDir, 'templates', 'partials');
65
+ if (!fs.existsSync(partialsDir)) return;
66
+
67
+ try {
68
+ const partialFiles = fs.readdirSync(partialsDir).filter(f => f.endsWith('.hbs'));
69
+ for (const file of partialFiles) {
70
+ const partialName = path.basename(file, '.hbs');
71
+ const partialContent = fs.readFileSync(path.join(partialsDir, file), 'utf-8');
72
+ Handlebars.registerPartial(partialName, partialContent);
73
+ this.log(`Registered partial: ${partialName}`);
74
+ }
75
+ } catch (err) {
76
+ this.log(`Warning: Could not register partials: ${err.message}`);
77
+ }
78
+ }
79
+
58
80
  /**
59
81
  * Generate rules content (.mdc format with YAML frontmatter)
60
82
  */
@@ -63,6 +85,9 @@ class CursorBridge extends BaseBridge {
63
85
 
64
86
  // Try to use Handlebars template
65
87
  if (Handlebars) {
88
+ // Register partials before compiling
89
+ this.registerPartials();
90
+
66
91
  const templatePath = path.join(this.projectDir, this.workflowDir, 'templates', 'cursor-rules.mdc.hbs');
67
92
  if (fs.existsSync(templatePath)) {
68
93
  try {
@@ -75,7 +100,7 @@ class CursorBridge extends BaseBridge {
75
100
  }
76
101
  }
77
102
 
78
- // Fallback to inline generation
103
+ // Fallback to inline generation (includes partial content directly)
79
104
  return this.generateRulesFallback(context);
80
105
  }
81
106
 
@@ -70,7 +70,7 @@ class GeminiBridge extends BaseBridge {
70
70
 
71
71
  /**
72
72
  * Generate GEMINI.md from Handlebars-like template
73
- * Supports: {{variable}}, {{config.path}}, {{#if}}, {{#each}}, {{/if}}, {{/each}}
73
+ * Supports: {{variable}}, {{config.path}}, {{#if}}, {{#each}}, {{/if}}, {{/each}}, {{> partial}}
74
74
  */
75
75
  generateFromTemplate(templatePath, config) {
76
76
  let template;
@@ -82,6 +82,9 @@ class GeminiBridge extends BaseBridge {
82
82
  }
83
83
  let content = template;
84
84
 
85
+ // Process {{> partial}} includes first (before other processing)
86
+ content = this.processPartials(content);
87
+
85
88
  // Process {{#if config.path.to.value}}...{{/if}} blocks
86
89
  content = this.processConditionals(content, config);
87
90