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.
- package/.workflow/templates/claude-md.hbs +19 -0
- package/lib/installer.js +22 -1
- package/package.json +1 -1
- package/scripts/flow-health.js +111 -0
- package/scripts/hooks/adapters/claude-code.js +1 -1
- package/scripts/hooks/core/routing-gate.js +16 -4
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +3 -3
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +15 -6
|
@@ -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
package/scripts/flow-health.js
CHANGED
|
@@ -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
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
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:
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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)
|