wogiflow 1.5.2 → 1.5.4

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.
@@ -305,6 +305,25 @@ You: [edits header.tsx] ← Tracked properly
305
305
  ```
306
306
 
307
307
  **When in doubt, use `/wogi-start`.** It will route appropriately.
308
+
309
+ ### When a Tool Call Is Blocked by the Routing Gate
310
+
311
+ If you attempt to use Bash or EnterPlanMode and receive a "BLOCKED" denial from WogiFlow's routing gate, here is the ONLY correct response:
312
+
313
+ 1. **Invoke `/wogi-start`** immediately with the user's original request:
314
+ ```
315
+ Skill(skill="wogi-start", args="<the user's original request>")
316
+ ```
317
+ 2. `/wogi-start` will classify the request and clear the routing gate
318
+ 3. After `/wogi-start` routes the request, you can use Bash/EnterPlanMode normally
319
+
320
+ **NEVER do these when blocked:**
321
+ - Suggest the user run commands manually in their terminal
322
+ - Try alternative tools to work around the block
323
+ - Explain the block to the user and ask them what to do
324
+ - Give up and say "I can't do that"
325
+
326
+ The routing gate exists because EVERY request must be classified by `/wogi-start` first. The block is not an error — it's a reminder that you skipped a mandatory step. Fix it by invoking `/wogi-start`.
308
327
  {{/if}}
309
328
 
310
329
  ## Session Startup
package/lib/installer.js CHANGED
@@ -583,6 +583,24 @@ function createCLIConfig(projectRoot, cliKey, config) {
583
583
 
584
584
  You are an AI development assistant using the WogiFlow methodology v1.0.
585
585
 
586
+ ---
587
+
588
+ ## Task Gating (MANDATORY — NO EXCEPTIONS)
589
+
590
+ **STOP. Before doing ANYTHING with a user message, you MUST route it through a \`/wogi-*\` command.**
591
+
592
+ **EVERY user message** MUST be routed through either:
593
+ 1. **A matching \`/wogi-*\` command** (e.g., "code review" → \`/wogi-review\`, "show tasks" → \`/wogi-ready\`)
594
+ 2. **\`/wogi-start\`** for everything else (the universal fallback router)
595
+
596
+ This applies to ALL message types — implementation requests, questions, exploration, research, operational requests. No exceptions.
597
+
598
+ **You do NOT handle requests directly.** Always invoke a \`/wogi-*\` command first. If you find yourself thinking "this is just a question, I can handle it directly" — that thought is the exact bypass this rule exists to prevent.
599
+
600
+ **Do NOT use EnterPlanMode directly.** Route through \`/wogi-start\` which will use plan mode internally when appropriate.
601
+
602
+ ---
603
+
586
604
  ## Quick Start
587
605
 
588
606
  \`\`\`bash
@@ -593,9 +611,12 @@ cat .workflow/state/ready.json # Check tasks
593
611
  ## Core Commands
594
612
 
595
613
  - \`/wogi-ready\` - Show available tasks
596
- - \`/wogi-start TASK-X\` - Start a task
614
+ - \`/wogi-start TASK-X\` - Start a task (or \`/wogi-start "description"\` to route any request)
615
+ - \`/wogi-story "title"\` - Create story with acceptance criteria
597
616
  - \`/wogi-status\` - Project overview
598
617
  - \`/wogi-health\` - Check workflow health
618
+ - \`/wogi-review\` - Code review
619
+ - \`/wogi-bug "description"\` - Report a bug
599
620
 
600
621
  Run \`flow bridge sync\` to regenerate this file with full template.
601
622
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -441,6 +441,117 @@ function main() {
441
441
  console.log(` ${color('yellow', '○')} .claude/settings.local.json not found (run 'flow bridge sync')`);
442
442
  }
443
443
 
444
+ // Check hook integrity
445
+ console.log('');
446
+ printSection('Checking hook integrity...');
447
+
448
+ const settingsLocalPath = path.join(PROJECT_ROOT, '.claude', 'settings.local.json');
449
+ if (fileExists(settingsLocalPath)) {
450
+ try {
451
+ const settings = safeJsonParse(settingsLocalPath, {});
452
+ const hooks = settings.hooks || {};
453
+
454
+ // Check PreToolUse matcher includes EnterPlanMode
455
+ const preToolHooks = hooks.PreToolUse || [];
456
+ let hasEnterPlanMode = false;
457
+ let hasCorrectMatcher = false;
458
+ let hookScriptsMissing = [];
459
+
460
+ for (const hookEntry of preToolHooks) {
461
+ const matcher = hookEntry.matcher || '';
462
+ if (matcher.includes('EnterPlanMode')) {
463
+ hasEnterPlanMode = true;
464
+ }
465
+ if (matcher.includes('Edit') && matcher.includes('Write') && matcher.includes('Bash') && matcher.includes('Skill')) {
466
+ hasCorrectMatcher = true;
467
+ }
468
+
469
+ // Check hook script files exist
470
+ for (const h of (hookEntry.hooks || [])) {
471
+ if (h.command) {
472
+ // Extract script path from command like: node "/path/to/script.js"
473
+ const scriptMatch = h.command.match(/node\s+"([^"]+)"/);
474
+ if (scriptMatch) {
475
+ const scriptPath = scriptMatch[1];
476
+ if (!fileExists(scriptPath)) {
477
+ hookScriptsMissing.push(scriptPath);
478
+ }
479
+ }
480
+ }
481
+ }
482
+ }
483
+
484
+ // Also check other hook types for missing scripts
485
+ for (const hookType of ['PostToolUse', 'UserPromptSubmit', 'SessionStart']) {
486
+ for (const hookEntry of (hooks[hookType] || [])) {
487
+ for (const h of (hookEntry.hooks || [])) {
488
+ if (h.command) {
489
+ const scriptMatch = h.command.match(/node\s+"([^"]+)"/);
490
+ if (scriptMatch && !fileExists(scriptMatch[1])) {
491
+ hookScriptsMissing.push(scriptMatch[1]);
492
+ }
493
+ }
494
+ }
495
+ }
496
+ }
497
+
498
+ if (hasEnterPlanMode) {
499
+ console.log(` ${color('green', '✓')} PreToolUse matcher includes EnterPlanMode`);
500
+ } else {
501
+ console.log(` ${color('red', '✗')} PreToolUse matcher MISSING EnterPlanMode — Claude can bypass /wogi-start`);
502
+ console.log(` ${color('dim', "→ Run 'flow bridge sync' to regenerate hooks")}`);
503
+ issues++;
504
+ }
505
+
506
+ if (hasCorrectMatcher) {
507
+ console.log(` ${color('green', '✓')} PreToolUse matcher has core tools (Edit|Write|Bash|Skill)`);
508
+ } else if (preToolHooks.length > 0) {
509
+ console.log(` ${color('yellow', '⚠')} PreToolUse matcher may be outdated — missing core tools`);
510
+ console.log(` ${color('dim', "→ Run 'flow bridge sync' to regenerate hooks")}`);
511
+ warnings++;
512
+ }
513
+
514
+ if (hookScriptsMissing.length > 0) {
515
+ console.log(` ${color('red', '✗')} ${hookScriptsMissing.length} hook script(s) MISSING:`);
516
+ for (const missing of hookScriptsMissing.slice(0, 5)) {
517
+ console.log(` - ${missing}`);
518
+ }
519
+ console.log(` ${color('dim', "→ Run 'npm install wogiflow' or 'flow init' to restore scripts")}`);
520
+ issues++;
521
+ } else if (preToolHooks.length > 0) {
522
+ console.log(` ${color('green', '✓')} All hook scripts exist`);
523
+ }
524
+ } catch (err) {
525
+ console.log(` ${color('yellow', '⚠')} Could not parse settings.local.json for hooks: ${err.message}`);
526
+ warnings++;
527
+ }
528
+ } else {
529
+ console.log(` ${color('yellow', '⚠')} .claude/settings.local.json not found — hooks not configured`);
530
+ console.log(` ${color('dim', "→ Run 'flow bridge sync' to generate hooks")}`);
531
+ warnings++;
532
+ }
533
+
534
+ // Check CLAUDE.md has routing instructions (not just product description)
535
+ if (fileExists(claudeMdPath)) {
536
+ try {
537
+ const claudeContent = fs.readFileSync(claudeMdPath, 'utf-8');
538
+ const hasRouting = claudeContent.includes('wogi-start') && (
539
+ claudeContent.includes('Task Gating') ||
540
+ claudeContent.includes('MUST route') ||
541
+ claudeContent.includes('MANDATORY')
542
+ );
543
+ if (hasRouting) {
544
+ console.log(` ${color('green', '✓')} CLAUDE.md contains routing instructions`);
545
+ } else {
546
+ console.log(` ${color('red', '✗')} CLAUDE.md has NO routing instructions — Claude will bypass /wogi-start`);
547
+ console.log(` ${color('dim', "→ Run 'flow bridge sync' to regenerate CLAUDE.md from template")}`);
548
+ issues++;
549
+ }
550
+ } catch (err) {
551
+ // Already warned about CLAUDE.md read failure above
552
+ }
553
+ }
554
+
444
555
  // Check git status
445
556
  console.log('');
446
557
  printSection('Checking git status...');
@@ -513,7 +513,7 @@ Run: /wogi-start ${coreResult.nextTaskId}`;
513
513
  // Task gating for Edit/Write + TodoWrite gating + Skill tracking + Bash strict adherence
514
514
  if (rules.taskGating?.enabled !== false || rules.todoWriteGate?.enabled !== false) {
515
515
  preToolUseMatchers.push({
516
- matcher: 'Edit|Write|TodoWrite|Skill|Bash',
516
+ matcher: 'Edit|Write|TodoWrite|Skill|Bash|EnterPlanMode',
517
517
  hooks: [{
518
518
  type: 'command',
519
519
  command: `node "${path.join(scriptsDir, 'pre-tool-use.js')}"`,
@@ -163,9 +163,11 @@ function isRoutingPending() {
163
163
  * @returns {{ allowed: boolean, blocked: boolean, reason: string, message: string|null }}
164
164
  */
165
165
  function checkRoutingGate(toolName) {
166
- // Only gate Bash calls
167
- if (toolName !== 'Bash') {
168
- return { allowed: true, blocked: false, reason: 'not_bash', message: null };
166
+ // Gate Bash and EnterPlanMode calls
167
+ // EnterPlanMode bypasses /wogi-start routing — must be blocked before routing
168
+ const GATED_TOOLS = new Set(['Bash', 'EnterPlanMode']);
169
+ if (!GATED_TOOLS.has(toolName)) {
170
+ return { allowed: true, blocked: false, reason: 'not_gated_tool', message: null };
169
171
  }
170
172
 
171
173
  // Check if routing gate is enabled
@@ -186,11 +188,21 @@ function checkRoutingGate(toolName) {
186
188
  }
187
189
 
188
190
  // Block: routing is pending and no active task
191
+ // NOTE: This message is shown to the AI as permissionDecisionReason.
192
+ // It must be prescriptive enough that the AI invokes /wogi-start instead of
193
+ // trying workarounds or suggesting the user run commands manually.
189
194
  return {
190
195
  allowed: false,
191
196
  blocked: true,
192
197
  reason: 'routing_pending',
193
- message: 'Route through a /wogi-* command first. Use /wogi-start to route your request.'
198
+ message: [
199
+ 'BLOCKED: You must route through /wogi-start before using Bash or EnterPlanMode.',
200
+ 'ACTION REQUIRED: Invoke the Skill tool with skill="wogi-start" and pass the user\'s request as args.',
201
+ 'Example: Skill(skill="wogi-start", args="<the user\'s original request>")',
202
+ '/wogi-start will classify the request (operational, exploration, implementation) and unblock the appropriate tools.',
203
+ 'Do NOT suggest the user run commands manually in their terminal.',
204
+ 'Do NOT try alternative approaches to bypass this gate.'
205
+ ].join(' ')
194
206
  };
195
207
  }
196
208
 
@@ -151,9 +151,9 @@ async function main() {
151
151
  }
152
152
  }
153
153
 
154
- // v6.0: Routing gate check (for Bash)
155
- // Blocks Bash calls when no /wogi-* command has been invoked first
156
- if (toolName === 'Bash') {
154
+ // v6.0: Routing gate check (for Bash and EnterPlanMode)
155
+ // Blocks Bash/EnterPlanMode calls when no /wogi-* command has been invoked first
156
+ if (toolName === 'Bash' || toolName === 'EnterPlanMode') {
157
157
  try {
158
158
  const routingResult = checkRoutingGate(toolName);
159
159
  if (routingResult.blocked) {
@@ -119,13 +119,22 @@ async function main() {
119
119
  // v6.0: Set routing-pending flag for routing gate enforcement
120
120
  // This blocks Bash calls until a /wogi-* skill is invoked
121
121
  // Skipped when an active task exists (follow-ups during tracked work are allowed)
122
- try {
123
- setRoutingPending();
124
- } catch (err) {
125
- // Non-blocking - don't fail the hook if routing gate fails (fail-open)
126
- if (process.env.DEBUG) {
127
- console.error(`[Hook] Routing gate set failed: ${err.message}`);
122
+ // v6.1: Also skip when the prompt IS a /wogi-* command — the user is already routing.
123
+ // When users type "/wogi-start ..." directly, Claude Code expands the skill inline
124
+ // (not through the Skill tool), so clearRoutingPending() in PreToolUse never fires.
125
+ // Setting the flag here would create an uncleable block.
126
+ const isWogiCommand = typeof prompt === 'string' && /^\/(wogi-\S+)/i.test(prompt.trim());
127
+ if (!isWogiCommand) {
128
+ try {
129
+ setRoutingPending();
130
+ } catch (err) {
131
+ // Non-blocking - don't fail the hook if routing gate fails (fail-open)
132
+ if (process.env.DEBUG) {
133
+ console.error(`[Hook] Routing gate set failed: ${err.message}`);
134
+ }
128
135
  }
136
+ } else if (process.env.DEBUG) {
137
+ console.error(`[Hook] Skipping routing flag — prompt is a /wogi-* command`);
129
138
  }
130
139
 
131
140
  // Check research gate first (before implementation gate)