wogiflow 2.17.5 → 2.18.1
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.
|
@@ -74,6 +74,7 @@ flow parallel check # See available parallel tasks
|
|
|
74
74
|
| 2.5.0+ | 2.1.84+ | TaskCreated hook, YAML glob lists in rules, CLAUDE_STREAM_IDLE_TIMEOUT_MS, WorktreeCreate HTTP transport, idle-return prompt, MCP 2KB cap |
|
|
75
75
|
| 2.9.0+ | 2.1.90+ | --resume deferred-tool cache fix, MCP schema perf, PostToolUse format-on-save fix, PreToolUse exit-code-2 fix, .husky protected |
|
|
76
76
|
| 2.9.2+ | 2.1.97+ | Stop/SubagentStop long-session fix, subagent worktree cwd leak fix, refreshInterval status line, workspace.git_worktree, MCP HTTP/SSE leak fix, 429 backoff, compaction transcript dedup |
|
|
77
|
+
| 2.18.0+ | 2.1.108+ | ENABLE_PROMPT_CACHING_1H guidance, /recap awareness, /doctor MCP duplicate-scope mirror in `/wogi-health` |
|
|
77
78
|
|
|
78
79
|
### Environment Variables (2.1.19+)
|
|
79
80
|
|
|
@@ -363,6 +364,50 @@ await cancelTask('wf-123', 'superseded', false);
|
|
|
363
364
|
|
|
364
365
|
- **`/claude-api` skill updated for Managed Agents**: The `/claude-api` skill now covers Managed Agents (`/v1/agents`, `/v1/sessions`) alongside the Claude API. **Impact on WogiFlow**: Informational — WogiFlow's `claude-api` skill reference remains accurate.
|
|
365
366
|
|
|
367
|
+
### Features in 2.1.108+
|
|
368
|
+
|
|
369
|
+
- **`ENABLE_PROMPT_CACHING_1H` env var (RECOMMENDED for non-subscribers)**: Opts into **1-hour prompt-cache TTL** on **API key, Bedrock, Vertex, and Foundry** providers. Subscribers (Claude Pro, Max, Team, Enterprise via claude.ai OAuth) already get 1h TTL by default — this flag is a **no-op for them**. The complementary `FORCE_PROMPT_CACHING_5M` pins to 5min, and the older `ENABLE_PROMPT_CACHING_1H_BEDROCK` is deprecated but still honored. **Impact on WogiFlow (HIGH)**: WogiFlow sessions load a large, stable prefix every turn — CLAUDE.md (~300 lines), state files (`ready.json`, `decisions.md`, `app-map.md`), phase files, and pinned spec context. At the default 5min TTL, any pause longer than 5 minutes (user thinking, a long `flow` CLI run, a meeting mid-session) invalidates the cache and the next turn pays the full input-token cost again. At 1h TTL, the same prefix stays cached across those pauses, yielding **substantial token-cost reduction** on typical multi-hour WogiFlow work. **Action for API-key / Bedrock / Vertex / Foundry users**: `export ENABLE_PROMPT_CACHING_1H=1` in your shell profile. **Action for subscribers**: none (already enabled). **Risk**: none — if set on a subscriber account it is ignored; if set when not supported, it silently falls back.
|
|
370
|
+
|
|
371
|
+
- **`/recap` command and session recap feature**: Provides context when returning to a session. Configurable in `/config` and manually invocable with `/recap`. For users with telemetry disabled (Bedrock/Vertex/Foundry/`DISABLE_TELEMETRY`), recap is still enabled by default; opt out via `/config` or `CLAUDE_CODE_ENABLE_AWAY_SUMMARY=0`. **Overlap with WogiFlow**: `/wogi-morning`, `/wogi-session-end`, and `/wogi-pre-compact` already provide durable recap via state files. `/recap` is ephemeral (summarizes the current session); WogiFlow's state survives session exit. Use both: `/recap` for intra-session context, `/wogi-morning` for cross-session pickup.
|
|
372
|
+
|
|
373
|
+
- **Built-in slash commands via Skill tool**: Claude can now discover and invoke `/init`, `/review`, `/security-review` via the Skill tool. **Impact on WogiFlow**: No collision — all WogiFlow commands use the `wogi-*` prefix (`/wogi-review`, `/wogi-init`, `/wogi-review-fix`). Natural-language routing in CLAUDE.md directs "code review" phrases to `/wogi-review`, not the built-in `/review`. If a user explicitly types `/review`, Claude Code handles it natively — this is expected.
|
|
374
|
+
|
|
375
|
+
- **`/model` mid-conversation warning**: `/model` now warns before switching models mid-conversation, since the next response re-reads the full history uncached. **Impact on WogiFlow**: Relevant for hybrid mode (`/wogi-hybrid`) — switching the executor model via `/model` during hybrid execution wastes the cached context. WogiFlow's `/wogi-hybrid-setup` is the correct way to change executor models between sessions rather than mid-session.
|
|
376
|
+
|
|
377
|
+
- **`DISABLE_PROMPT_CACHING*` startup warning**: Claude Code now warns at startup when prompt caching is disabled via `DISABLE_PROMPT_CACHING*` env vars. **Impact on WogiFlow**: WogiFlow's heavy context prefix makes disabled caching **expensive**. This warning helps users who accidentally disabled caching (e.g., copy-pasted env from another project) spot the regression fast.
|
|
378
|
+
|
|
379
|
+
- **`/undo` alias for `/rewind`**: Typing `/undo` now aliases to `/rewind`. WogiFlow's `/wogi-pre-compact` and `/wogi-suspend` are complementary — `/undo`/`/rewind` rolls back message turns, while the WogiFlow flows preserve state across sessions.
|
|
380
|
+
|
|
381
|
+
- **Memory footprint reductions for file reads**: Language grammars now load on demand, reducing memory for file reads, edits, and syntax highlighting. **Impact on WogiFlow**: Long WogiFlow sessions (especially `/wogi-bulk-loop` continuous runs) use noticeably less RAM. No code change needed.
|
|
382
|
+
|
|
383
|
+
### Features in 2.1.110+
|
|
384
|
+
|
|
385
|
+
- **PreToolUse hook `additionalContext` preserved on tool failure (BUG FIX, GOOD NEWS)**: Previously, when a tool call failed, any `additionalContext` returned by PreToolUse hooks was **dropped**. Fixed in 2.1.110. **Impact on WogiFlow (HIGH)**: WogiFlow injects `additionalContext` in 8 places via `scripts/hooks/adapters/claude-code.js` (PreToolUse, UserPromptSubmit, SessionStart) for routing enforcement, phase-gate messages, component reuse hints, and session-start task context. Before this fix, if a guarded tool call failed, WogiFlow's context message vanished — producing "silent" hook behavior that was confusing to debug. After this fix, WogiFlow's hook messages are reliably delivered regardless of tool outcome. **Action**: none — automatic improvement after upgrade.
|
|
386
|
+
|
|
387
|
+
- **`/doctor` warns on duplicate MCP server definitions across scopes**: When the same MCP server is defined in user (`~/.claude/settings.json`), project (`.claude/settings.json`), and local (`.claude/settings.local.json`) scopes with different endpoints, `/doctor` now flags the conflict. **Impact on WogiFlow**: `/wogi-health` has a mirror check in `flow-health.js` that scans the same three scopes and reports duplicate MCP server names with divergent endpoints as a health finding (v2.18.0+).
|
|
388
|
+
|
|
389
|
+
- **PushNotification tool**: Claude can send mobile push notifications when Remote Control and "Push when Claude decides" config are enabled. **WogiFlow opportunity**: Long-running autonomous loops (`/wogi-bulk`, `/wogi-bulk-loop`) could emit a notification on completion, blocker, or extended hang. Tracked as a future enhancement; not auto-wired.
|
|
390
|
+
|
|
391
|
+
- **Bash tool timeout enforcement**: The Bash tool now enforces the documented maximum timeout (600000ms / 10min) instead of accepting arbitrarily large values. **Impact on WogiFlow**: No impact — all WogiFlow hook Bash timeouts are under 60s (verified across `.claude/settings.json` and `scripts/hooks/`).
|
|
392
|
+
|
|
393
|
+
- **stdio MCP servers no longer disconnect on stray non-JSON lines**: Fixed a regression from 2.1.105 where stdio MCP servers that print stray non-JSON lines to stdout were disconnected on the first stray line. **Impact on WogiFlow**: WogiFlow has no custom MCP servers in-repo. User-installed MCP servers (figma, atlassian, gmail) benefit automatically.
|
|
394
|
+
|
|
395
|
+
- **PermissionRequest hook `updatedInput` re-check**: Fixed PermissionRequest hooks returning `updatedInput` not being re-checked against `permissions.deny` rules; `setMode:'bypassPermissions'` updates now respect `disableBypassPermissionsMode`. **Impact on WogiFlow**: WogiFlow does not implement PermissionRequest hooks (only PermissionDenied for logging). Not affected.
|
|
396
|
+
|
|
397
|
+
- **`--resume`/`--continue` resurrects unexpired scheduled tasks**: Scheduled tasks (cron/CronCreate) now resume across session restarts. **Impact on WogiFlow**: WogiFlow does not currently use Claude Code's cron feature. Not affected; tracked as a future opportunity for automated maintenance tasks.
|
|
398
|
+
|
|
399
|
+
- **`/context`, `/exit`, `/reload-plugins` work from Remote Control (mobile/web) clients**: Remote Control users can now invoke these built-ins. **Impact on WogiFlow**: WogiFlow has no TTY-only code paths — all `/wogi-*` skills already work identically on Remote Control. Users can now do full WogiFlow-driven work from mobile/web.
|
|
400
|
+
|
|
401
|
+
- **`/tui` command and `tui` setting**: `/tui fullscreen` switches to flicker-free rendering in the same conversation. The focus view is now toggled separately with `/focus` (Ctrl+O now toggles verbose transcript only). **Impact on WogiFlow**: Documentation only — no runtime dependency on Ctrl+O. The WogiFlow statusline works identically in both TUI modes.
|
|
402
|
+
|
|
403
|
+
- **`autoScrollEnabled` config**: New setting to disable conversation auto-scroll in fullscreen mode. Purely UX — no WogiFlow impact.
|
|
404
|
+
|
|
405
|
+
- **Write tool reports IDE diff edits**: The Write tool now informs the model when the user edits the proposed content in the IDE diff before accepting. **Impact on WogiFlow**: Useful signal for learning — WogiFlow's `/wogi-correction` could eventually consume this to detect "user edited my output" events. Not auto-wired; tracked as an enhancement.
|
|
406
|
+
|
|
407
|
+
- **TRACEPARENT/TRACESTATE in SDK/headless sessions**: SDK and headless sessions now read W3C trace headers from the environment for distributed trace linking. **Impact on wogiflow-cloud**: Teams backend can propagate trace context from CI/CD pipelines into WogiFlow sessions for end-to-end observability. Tracked as a cloud opportunity.
|
|
408
|
+
|
|
409
|
+
- **Hardened "Open in editor" against command injection**: Security hardening for untrusted filenames. **Impact on WogiFlow**: Validates the same pattern in `.claude/rules/security/security-patterns.md` — external inputs going into shell commands must be validated. No WogiFlow code change needed.
|
|
410
|
+
|
|
366
411
|
### Simple Mode Naming Distinction
|
|
367
412
|
|
|
368
413
|
Claude Code's `CLAUDE_CODE_SIMPLE` environment variable (which enables a simplified tool set) is **unrelated** to WogiFlow's `loops.simpleMode` (a lightweight task completion loop using string detection). They are separate features that happen to share the word "simple":
|
|
@@ -497,4 +542,4 @@ Run `/keybindings` in Claude Code to customize your shortcuts.
|
|
|
497
542
|
|
|
498
543
|
---
|
|
499
544
|
|
|
500
|
-
*Last updated: 2026-04-
|
|
545
|
+
*Last updated: 2026-04-16*
|
package/.claude/settings.json
CHANGED
|
@@ -133,10 +133,43 @@
|
|
|
133
133
|
}
|
|
134
134
|
]
|
|
135
135
|
}
|
|
136
|
+
],
|
|
137
|
+
"TaskCreated": [
|
|
138
|
+
{
|
|
139
|
+
"hooks": [
|
|
140
|
+
{
|
|
141
|
+
"type": "command",
|
|
142
|
+
"command": "node scripts/hooks/entry/claude-code/task-created.js",
|
|
143
|
+
"timeout": 5
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"PermissionDenied": [
|
|
149
|
+
{
|
|
150
|
+
"hooks": [
|
|
151
|
+
{
|
|
152
|
+
"type": "command",
|
|
153
|
+
"command": "node scripts/hooks/entry/claude-code/permission-denied.js",
|
|
154
|
+
"timeout": 5
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"PreCompact": [
|
|
160
|
+
{
|
|
161
|
+
"hooks": [
|
|
162
|
+
{
|
|
163
|
+
"type": "command",
|
|
164
|
+
"command": "node scripts/hooks/entry/claude-code/pre-compact.js",
|
|
165
|
+
"timeout": 5
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
}
|
|
136
169
|
]
|
|
137
170
|
},
|
|
138
171
|
"_comment_dynamicHooks": "TaskCreated (2.1.84+) and PermissionDenied (2.1.88+) are added by postinstall.js when the CC version supports them. They must NOT be committed statically — CC rejects the entire settings file if it encounters an unknown hook event name.",
|
|
139
172
|
"_wogiFlowManaged": true,
|
|
140
|
-
"_wogiFlowVersion": "2.
|
|
173
|
+
"_wogiFlowVersion": "2.17.5",
|
|
141
174
|
"_comment": "Shared WogiFlow hook configuration. Committed to repo for team use. User-specific overrides go in settings.local.json."
|
|
142
175
|
}
|
|
@@ -131,6 +131,7 @@ npm install -D wogiflow && npx flow onboard
|
|
|
131
131
|
| `/wogi-register` | Register plugins for /wogi-start routing |
|
|
132
132
|
|
|
133
133
|
See `.claude/docs/commands.md` for complete command reference.
|
|
134
|
+
See `.claude/docs/claude-code-compatibility.md` for Claude Code version features, performance tips, and env vars (incl. **`ENABLE_PROMPT_CACHING_1H=1`** recommended for API-key / Bedrock / Vertex / Foundry users).
|
|
134
135
|
|
|
135
136
|
## Natural Language Command Detection
|
|
136
137
|
|
package/lib/installer.js
CHANGED
|
@@ -1030,6 +1030,27 @@ async function init(args) {
|
|
|
1030
1030
|
console.log(' /wogi-health - Check workflow health');
|
|
1031
1031
|
console.log(' /wogi-story - Create a new story');
|
|
1032
1032
|
console.log('');
|
|
1033
|
+
printPromptCachingTip();
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Print the ENABLE_PROMPT_CACHING_1H recommendation for non-subscriber Claude Code users.
|
|
1038
|
+
*
|
|
1039
|
+
* Claude Pro/Max/Team/Enterprise (OAuth via claude.ai) already get 1-hour prompt-cache TTL.
|
|
1040
|
+
* API-key, Bedrock, Vertex, and Foundry users default to 5min TTL — any pause longer than
|
|
1041
|
+
* 5min during a WogiFlow session invalidates the cached CLAUDE.md + state-file prefix and
|
|
1042
|
+
* re-pays the full input-token cost on the next turn. Enabling 1h TTL gives substantial
|
|
1043
|
+
* savings on typical multi-hour sessions.
|
|
1044
|
+
*
|
|
1045
|
+
* We surface this exactly once, during `flow init` onboarding. The full rationale lives in
|
|
1046
|
+
* `.claude/docs/claude-code-compatibility.md` under "Features in 2.1.108+".
|
|
1047
|
+
*/
|
|
1048
|
+
function printPromptCachingTip() {
|
|
1049
|
+
console.log('💡 Performance tip (API-key / Bedrock / Vertex / Foundry users):');
|
|
1050
|
+
console.log(' Set \x1b[33mENABLE_PROMPT_CACHING_1H=1\x1b[0m in your shell profile for 1-hour');
|
|
1051
|
+
console.log(' prompt-cache TTL (default is 5min). Claude subscribers already get 1h by default.');
|
|
1052
|
+
console.log(' Details: \x1b[36m.claude/docs/claude-code-compatibility.md\x1b[0m (Features in 2.1.108+)');
|
|
1053
|
+
console.log('');
|
|
1033
1054
|
}
|
|
1034
1055
|
|
|
1035
1056
|
/**
|
package/package.json
CHANGED
package/scripts/flow-health.js
CHANGED
|
@@ -82,6 +82,123 @@ function checkClaudeCodeVersion() {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Normalize an MCP server config so two equivalent objects compare equal
|
|
87
|
+
* after JSON.stringify. Recursively sorts object keys and stringifies arrays
|
|
88
|
+
* in their original order (order is meaningful for args).
|
|
89
|
+
*
|
|
90
|
+
* @param {*} cfg - An MCP server config value (typically an object, but may be primitive)
|
|
91
|
+
* @returns {string} Canonical JSON representation suitable for equality comparison
|
|
92
|
+
*/
|
|
93
|
+
function normalizeMcpConfig(cfg) {
|
|
94
|
+
const sortKeys = (v) => {
|
|
95
|
+
if (Array.isArray(v)) return v.map(sortKeys);
|
|
96
|
+
if (v && typeof v === 'object') {
|
|
97
|
+
const out = {};
|
|
98
|
+
for (const k of Object.keys(v).sort()) {
|
|
99
|
+
out[k] = sortKeys(v[k]);
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
return v;
|
|
104
|
+
};
|
|
105
|
+
return JSON.stringify(sortKeys(cfg));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check MCP server definitions across the three Claude Code settings scopes:
|
|
110
|
+
* - user: ~/.claude/settings.json
|
|
111
|
+
* - project: <project>/.claude/settings.json
|
|
112
|
+
* - local: <project>/.claude/settings.local.json
|
|
113
|
+
*
|
|
114
|
+
* Mirrors the /doctor warning added in Claude Code 2.1.110: a server defined
|
|
115
|
+
* in multiple scopes with divergent config is almost always a mistake — the
|
|
116
|
+
* lowest-priority scope silently loses, and users debug ghost endpoints.
|
|
117
|
+
*
|
|
118
|
+
* Identical duplicate definitions are intentionally NOT flagged — some teams
|
|
119
|
+
* duplicate for portability. Only divergent configs surface.
|
|
120
|
+
*
|
|
121
|
+
* @param {Object} [opts]
|
|
122
|
+
* @param {string} [opts.userSettingsPath] - Override path to user scope (for tests)
|
|
123
|
+
* @param {string} [opts.projectSettingsPath] - Override path to project scope (for tests)
|
|
124
|
+
* @param {string} [opts.localSettingsPath] - Override path to local scope (for tests)
|
|
125
|
+
* @returns {{
|
|
126
|
+
* duplicates: Array<{name: string, scopes: string[]}>,
|
|
127
|
+
* uniqueServers: number,
|
|
128
|
+
* parseErrors: Array<{file: string, error: string}>,
|
|
129
|
+
* scopesChecked: number
|
|
130
|
+
* }}
|
|
131
|
+
*/
|
|
132
|
+
function checkMcpScopes(opts = {}) {
|
|
133
|
+
const os = require('node:os');
|
|
134
|
+
const scopes = [
|
|
135
|
+
{ file: opts.userSettingsPath || path.join(os.homedir(), '.claude', 'settings.json'), label: 'user' },
|
|
136
|
+
{ file: opts.projectSettingsPath || path.join(PROJECT_ROOT, '.claude', 'settings.json'), label: 'project' },
|
|
137
|
+
{ file: opts.localSettingsPath || path.join(PROJECT_ROOT, '.claude', 'settings.local.json'), label: 'local' }
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const parseErrors = [];
|
|
141
|
+
const serverByName = new Map();
|
|
142
|
+
let scopesChecked = 0;
|
|
143
|
+
|
|
144
|
+
// Sentinel object for safeJsonParse so we can distinguish "file failed to
|
|
145
|
+
// parse" (returned sentinel) from "file parsed but had no mcpServers" (returned
|
|
146
|
+
// object without the key). safeJsonParse is mandated by security-patterns.md §2
|
|
147
|
+
// (prototype-pollution guard + non-object validation) — we layer a raw-read
|
|
148
|
+
// fallback on top to categorize the failure cause for health output.
|
|
149
|
+
const PARSE_SENTINEL = Object.create(null);
|
|
150
|
+
|
|
151
|
+
for (const scope of scopes) {
|
|
152
|
+
if (!fileExists(scope.file)) continue;
|
|
153
|
+
scopesChecked++;
|
|
154
|
+
const json = safeJsonParse(scope.file, PARSE_SENTINEL);
|
|
155
|
+
if (json === PARSE_SENTINEL) {
|
|
156
|
+
// safeJsonParse returned the sentinel — categorize: read failure vs JSON
|
|
157
|
+
// parse failure vs non-object vs prototype-pollution rejection.
|
|
158
|
+
let raw;
|
|
159
|
+
try {
|
|
160
|
+
raw = fs.readFileSync(scope.file, 'utf-8');
|
|
161
|
+
} catch (err) {
|
|
162
|
+
parseErrors.push({ file: scope.file, error: `read failed: ${err.message}` });
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
JSON.parse(raw);
|
|
167
|
+
// Raw parse succeeded — safeJsonParse rejected for structural reason
|
|
168
|
+
// (non-object top level, or dangerous __proto__/constructor keys).
|
|
169
|
+
parseErrors.push({ file: scope.file, error: 'rejected by safeJsonParse (non-object or prototype-pollution guard)' });
|
|
170
|
+
} catch (err) {
|
|
171
|
+
parseErrors.push({ file: scope.file, error: `invalid JSON: ${err.message}` });
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const mcp = json.mcpServers;
|
|
176
|
+
if (!mcp || typeof mcp !== 'object' || Array.isArray(mcp)) continue;
|
|
177
|
+
|
|
178
|
+
for (const name of Object.keys(mcp)) {
|
|
179
|
+
if (!serverByName.has(name)) serverByName.set(name, []);
|
|
180
|
+
serverByName.get(name).push({ scope: scope.label, config: mcp[name] });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const duplicates = [];
|
|
185
|
+
for (const [name, entries] of serverByName.entries()) {
|
|
186
|
+
if (entries.length < 2) continue;
|
|
187
|
+
const canonical = normalizeMcpConfig(entries[0].config);
|
|
188
|
+
const allSame = entries.every(e => normalizeMcpConfig(e.config) === canonical);
|
|
189
|
+
if (!allSame) {
|
|
190
|
+
duplicates.push({ name, scopes: entries.map(e => e.scope) });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
duplicates,
|
|
196
|
+
uniqueServers: serverByName.size,
|
|
197
|
+
parseErrors,
|
|
198
|
+
scopesChecked
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
85
202
|
function main() {
|
|
86
203
|
console.log(color('cyan', 'Wogi Flow Health Check'));
|
|
87
204
|
console.log('========================');
|
|
@@ -661,6 +778,29 @@ function main() {
|
|
|
661
778
|
}
|
|
662
779
|
}
|
|
663
780
|
|
|
781
|
+
// Check MCP server definitions across scopes (mirrors Claude Code 2.1.110 /doctor)
|
|
782
|
+
console.log('');
|
|
783
|
+
printSection('Checking MCP server scopes...');
|
|
784
|
+
|
|
785
|
+
const mcp = checkMcpScopes();
|
|
786
|
+
if (mcp.parseErrors.length > 0) {
|
|
787
|
+
for (const e of mcp.parseErrors) {
|
|
788
|
+
warn(`Could not parse ${e.file}: ${e.error}`);
|
|
789
|
+
}
|
|
790
|
+
warnings += mcp.parseErrors.length;
|
|
791
|
+
}
|
|
792
|
+
if (mcp.uniqueServers === 0) {
|
|
793
|
+
console.log(` ${color('dim', '○')} No MCP servers defined in user / project / local scopes`);
|
|
794
|
+
} else if (mcp.duplicates.length === 0) {
|
|
795
|
+
success(`No conflicting MCP server definitions across ${mcp.scopesChecked} scope(s)`);
|
|
796
|
+
} else {
|
|
797
|
+
for (const f of mcp.duplicates) {
|
|
798
|
+
warn(`MCP server "${f.name}" has divergent config in scopes: ${f.scopes.join(' + ')}`);
|
|
799
|
+
console.log(` ${color('dim', '→ Consolidate into a single scope; /doctor will flag this too')}`);
|
|
800
|
+
}
|
|
801
|
+
warnings += mcp.duplicates.length;
|
|
802
|
+
}
|
|
803
|
+
|
|
664
804
|
// Check .gitignore sync
|
|
665
805
|
console.log('');
|
|
666
806
|
printSection('Checking .gitignore sync...');
|
|
@@ -1159,4 +1299,8 @@ function run() {
|
|
|
1159
1299
|
}
|
|
1160
1300
|
}
|
|
1161
1301
|
|
|
1162
|
-
|
|
1302
|
+
if (require.main === module) {
|
|
1303
|
+
run();
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
module.exports = { checkMcpScopes, normalizeMcpConfig };
|
|
@@ -418,19 +418,104 @@ function generateWarningMessage(operation, filePath) {
|
|
|
418
418
|
return `Warning: ${operation === 'write' ? 'Creating' : 'Editing'} ${fileName} without an active task. Consider starting a task first.`;
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
+
// Rule / memory files where /wogi-decide is the idiomatic capture command.
|
|
422
|
+
// Intent artifacts (domain-model.md, user-journeys.md, glossary.md, product.md)
|
|
423
|
+
// are deliberately excluded — modifying those is product-design work and should
|
|
424
|
+
// flow through /wogi-story per the normal task lifecycle.
|
|
425
|
+
const RULE_FILE_BASENAMES = new Set([
|
|
426
|
+
'decisions.md',
|
|
427
|
+
'feedback-patterns.md',
|
|
428
|
+
'MEMORY.md'
|
|
429
|
+
]);
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Detect whether the given file path (or its basename) is a rule/memory file
|
|
433
|
+
* that should route to /wogi-decide for capture instead of /wogi-story.
|
|
434
|
+
*
|
|
435
|
+
* Also matches Claude Code auto-memory paths under `.claude/projects/*\/memory/`.
|
|
436
|
+
*/
|
|
437
|
+
function isRuleOrMemoryFile(filePath) {
|
|
438
|
+
if (!filePath || typeof filePath !== 'string') return false;
|
|
439
|
+
const base = path.basename(filePath);
|
|
440
|
+
if (RULE_FILE_BASENAMES.has(base)) return true;
|
|
441
|
+
if (filePath.includes(`${path.sep}.claude${path.sep}projects${path.sep}`) &&
|
|
442
|
+
filePath.includes(`${path.sep}memory${path.sep}`)) return true;
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Detect whether the current working directory (or its parents) contains a
|
|
448
|
+
* `.workspace/` directory — the marker for WogiFlow workspace mode where a
|
|
449
|
+
* manager repo coordinates worker repos. When present, the block message
|
|
450
|
+
* additionally suggests the workspace-coordination task pattern.
|
|
451
|
+
*
|
|
452
|
+
* Walks up at most 6 parents to avoid unbounded traversal.
|
|
453
|
+
*/
|
|
454
|
+
function isInWorkspaceMode(startDir) {
|
|
455
|
+
const start = startDir || process.cwd();
|
|
456
|
+
try {
|
|
457
|
+
let dir = start;
|
|
458
|
+
for (let i = 0; i < 6; i++) {
|
|
459
|
+
const candidate = path.join(dir, '.workspace');
|
|
460
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) {
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
const parent = path.dirname(dir);
|
|
464
|
+
if (parent === dir) break;
|
|
465
|
+
dir = parent;
|
|
466
|
+
}
|
|
467
|
+
} catch (_err) {
|
|
468
|
+
// Filesystem errors (permissions, etc.) → conservatively say no. The
|
|
469
|
+
// standard suggestions are still shown, so the user is never stranded.
|
|
470
|
+
}
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
|
|
421
474
|
/**
|
|
422
|
-
* Generate block message
|
|
475
|
+
* Generate block message (context-aware).
|
|
476
|
+
*
|
|
477
|
+
* The message adapts to two signals:
|
|
478
|
+
* 1. If the blocked path is a rule/memory file (e.g. decisions.md), the
|
|
479
|
+
* message leads with `/wogi-decide` — the idiomatic "from now on" rule
|
|
480
|
+
* capture command that doesn't require a code task.
|
|
481
|
+
* 2. If a `.workspace/` directory exists at cwd or a parent, the message
|
|
482
|
+
* adds a workspace-coordination task pattern.
|
|
483
|
+
*
|
|
484
|
+
* Enforcement behavior is unchanged — this only affects the text shown to
|
|
485
|
+
* the AI / user when the gate blocks. Intent artifacts (domain-model.md,
|
|
486
|
+
* user-journeys.md, glossary.md, product.md) deliberately do NOT trigger
|
|
487
|
+
* the rule-file branch — those still route to /wogi-story.
|
|
423
488
|
*/
|
|
424
489
|
function generateBlockMessage(operation, filePath) {
|
|
425
490
|
const fileName = filePath ? path.basename(filePath) : 'file';
|
|
426
|
-
|
|
491
|
+
const isRuleFile = isRuleOrMemoryFile(filePath);
|
|
492
|
+
const inWorkspace = isInWorkspaceMode();
|
|
493
|
+
|
|
494
|
+
const lines = [`Cannot ${operation} ${fileName} without an active task.`, ''];
|
|
427
495
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
496
|
+
if (isRuleFile) {
|
|
497
|
+
lines.push('For rule / memory capture without a code task:');
|
|
498
|
+
lines.push(' /wogi-decide "from now on, <your rule>"');
|
|
499
|
+
lines.push('');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (inWorkspace) {
|
|
503
|
+
lines.push('For workspace coordination (manager repo in workspace mode):');
|
|
504
|
+
lines.push(' /wogi-start "coordinate wf-XXXXXXXX in workspace"');
|
|
505
|
+
lines.push('');
|
|
506
|
+
}
|
|
432
507
|
|
|
433
|
-
|
|
508
|
+
lines.push('For a side thought or idea to save for later:');
|
|
509
|
+
lines.push(' /wogi-capture "your idea"');
|
|
510
|
+
lines.push('');
|
|
511
|
+
lines.push('Other options:');
|
|
512
|
+
lines.push(' /wogi-ready → see available tasks');
|
|
513
|
+
lines.push(' /wogi-start wf-XXXXXXXX → start an existing task');
|
|
514
|
+
lines.push(' /wogi-story "description" → create a new task');
|
|
515
|
+
lines.push('');
|
|
516
|
+
lines.push('Task gating is enforced when strictMode is enabled.');
|
|
517
|
+
|
|
518
|
+
return lines.join('\n');
|
|
434
519
|
}
|
|
435
520
|
|
|
436
521
|
module.exports = {
|