wogiflow 2.0.0 → 2.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wogiflow",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "AI-powered development workflow management system with multi-model support",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -116,6 +116,8 @@ const PATHS = {
116
116
  epics: path.join(WORKFLOW_DIR, 'epics'),
117
117
  features: path.join(WORKFLOW_DIR, 'features'),
118
118
  plans: path.join(WORKFLOW_DIR, 'plans'),
119
+ // Scratch directory for temporary files (auto-cleaned at session end)
120
+ scratch: path.join(WORKFLOW_DIR, 'scratch'),
119
121
  // Additional workflow directories
120
122
  runs: path.join(WORKFLOW_DIR, 'runs'),
121
123
  checkpoints: path.join(WORKFLOW_DIR, 'checkpoints'),
@@ -119,17 +119,20 @@ function checkClaudeCodeVersionOnce() {
119
119
  return `Claude Code ${version} is below the minimum required version (${HARD_MIN.major}.${HARD_MIN.minor}.${HARD_MIN.patch}). WogiFlow hooks will not work correctly. Update with: npm install -g @anthropic-ai/claude-code@latest`;
120
120
  }
121
121
 
122
- // Soft gates — degrade gracefully, no warning
123
- // 2.1.50+: worktree hooks, agent isolation
124
- // 2.1.72+: ExitWorktree, effort levels, model param on Agent
125
- // 2.1.73+: SessionStart double-fire fix, hook context pollution fix, modelOverrides, subagent model fix
126
- // 2.1.75+: 1M context default (Max/Team/Enterprise), accurate token estimation,
127
- // async hook completion suppressed by default, hook source in permission prompts,
128
- // memory file last-modified timestamps
129
- // 2.1.76+: PostCompact hook (state recovery after compaction), Elicitation/ElicitationResult hooks
130
- // (MCP structured input), worktree.sparsePaths (sparse checkout for monorepos),
131
- // /effort slash command, deferred tools schema fix after compaction,
132
- // auto-compaction circuit breaker (3 retries), background agent partial results preserved
122
+ // Soft gates — generate informational warning listing disabled features
123
+ const SOFT_GATES = [
124
+ { version: [2, 1, 50], features: 'worktree hooks, agent isolation' },
125
+ { version: [2, 1, 72], features: 'ConfigChange/InstructionsLoaded hooks, effort levels' },
126
+ { version: [2, 1, 76], features: 'PostCompact hook (state recovery after compaction)' },
127
+ ];
128
+
129
+ const disabledFeatures = SOFT_GATES
130
+ .filter(gate => !meetsVersion(major, minor, patch, ...gate.version))
131
+ .map(gate => ` - ${gate.features} (requires ${gate.version.join('.')}+)`);
132
+
133
+ if (disabledFeatures.length > 0) {
134
+ return `Claude Code ${version} — some WogiFlow features are disabled:\n${disabledFeatures.join('\n')}\nUpdate for full functionality: npm i -g @anthropic-ai/claude-code@latest`;
135
+ }
133
136
 
134
137
  return null;
135
138
  }
@@ -796,6 +796,18 @@ function formatContextForInjection(context) {
796
796
  output += `\`\`\`bash\nunset CLAUDE_CODE_SIMPLE\n\`\`\`\n\n`;
797
797
  }
798
798
 
799
+ // Version compatibility warning (Claude Code below hard minimum or missing features)
800
+ if (ctx.versionWarning) {
801
+ output += `### Version Warning\n`;
802
+ output += `${ctx.versionWarning}\n\n`;
803
+ }
804
+
805
+ // WogiFlow npm update available
806
+ if (ctx.updateWarning) {
807
+ output += `### Update Available\n`;
808
+ output += `${ctx.updateWarning}\n\n`;
809
+ }
810
+
799
811
  // PRIORITY: Setup required - show first if needs setup
800
812
  if (ctx.setupRequired && ctx.setupRequired.needsSetup) {
801
813
  output += `### ⚠️ Setup Required\n`;
@@ -68,6 +68,38 @@ function handleSessionEnd(input) {
68
68
  result.logged = false;
69
69
  }
70
70
 
71
+ // Scratch directory cleanup — remove temp files created during session
72
+ try {
73
+ const fs = require('node:fs');
74
+ const path = require('node:path');
75
+ const scratchDir = path.join(PATHS.workflow, 'scratch');
76
+ if (fs.existsSync(scratchDir)) {
77
+ const files = fs.readdirSync(scratchDir);
78
+ let scratchCleaned = 0;
79
+ for (const file of files) {
80
+ if (file === '.gitkeep') continue; // Keep the directory marker
81
+ try {
82
+ const filePath = path.join(scratchDir, file);
83
+ const stat = fs.statSync(filePath);
84
+ if (stat.isFile()) {
85
+ fs.unlinkSync(filePath);
86
+ scratchCleaned++;
87
+ } else if (stat.isDirectory()) {
88
+ fs.rmSync(filePath, { recursive: true, force: true });
89
+ scratchCleaned++;
90
+ }
91
+ } catch (_err) {
92
+ // Skip files that can't be deleted
93
+ }
94
+ }
95
+ if (scratchCleaned > 0) {
96
+ result.scratchCleaned = scratchCleaned;
97
+ }
98
+ }
99
+ } catch (_err) {
100
+ // Non-critical — never block session end
101
+ }
102
+
71
103
  // State folder hygiene — clean stale/orphan files (fire-and-forget)
72
104
  try {
73
105
  const hygiene = cleanStaleFiles();
@@ -105,7 +105,8 @@ function createMinimalStructure() {
105
105
  WORKFLOW_DIR,
106
106
  STATE_DIR,
107
107
  path.join(WORKFLOW_DIR, 'changes'),
108
- path.join(WORKFLOW_DIR, 'specs')
108
+ path.join(WORKFLOW_DIR, 'specs'),
109
+ path.join(WORKFLOW_DIR, 'scratch')
109
110
  ];
110
111
 
111
112
  for (const dir of dirs) {
@@ -244,6 +245,82 @@ function copyDir(src, dest, mergeMode = false, depth = 0) {
244
245
  }
245
246
  }
246
247
 
248
+ /**
249
+ * Hook types and the minimum Claude Code version that supports them.
250
+ * Claude Code rejects the ENTIRE settings.json if any hook key is unrecognized,
251
+ * so we must exclude hooks that the installed version doesn't support.
252
+ */
253
+ const HOOK_VERSION_MAP = {
254
+ // Hooks available since 2.1.23 (HARD_MIN)
255
+ SessionStart: { major: 2, minor: 1, patch: 23 },
256
+ UserPromptSubmit: { major: 2, minor: 1, patch: 23 },
257
+ PreToolUse: { major: 2, minor: 1, patch: 23 },
258
+ PostToolUse: { major: 2, minor: 1, patch: 23 },
259
+ Stop: { major: 2, minor: 1, patch: 23 },
260
+ SessionEnd: { major: 2, minor: 1, patch: 23 },
261
+ TaskCompleted: { major: 2, minor: 1, patch: 23 },
262
+ // Hooks added in 2.1.50+
263
+ WorktreeCreate: { major: 2, minor: 1, patch: 50 },
264
+ WorktreeRemove: { major: 2, minor: 1, patch: 50 },
265
+ // Hooks added in 2.1.72+
266
+ ConfigChange: { major: 2, minor: 1, patch: 72 },
267
+ InstructionsLoaded: { major: 2, minor: 1, patch: 72 },
268
+ // Hooks added in 2.1.76+
269
+ PostCompact: { major: 2, minor: 1, patch: 76 },
270
+ };
271
+
272
+ /**
273
+ * Detect Claude Code version.
274
+ * @returns {{ major: number, minor: number, patch: number } | null}
275
+ */
276
+ function detectClaudeCodeVersion() {
277
+ try {
278
+ const output = execFileSync('claude', ['--version'], {
279
+ encoding: 'utf-8',
280
+ stdio: ['pipe', 'pipe', 'pipe'],
281
+ timeout: 5000
282
+ }).trim();
283
+ const match = output.match(/(\d+)\.(\d+)\.(\d+)/);
284
+ if (match) {
285
+ return { major: parseInt(match[1], 10), minor: parseInt(match[2], 10), patch: parseInt(match[3], 10) };
286
+ }
287
+ } catch (_err) {
288
+ // Claude CLI not available — can't detect version
289
+ }
290
+ return null;
291
+ }
292
+
293
+ /**
294
+ * Check if version A meets minimum version B.
295
+ * @returns {boolean}
296
+ */
297
+ function versionMeetsMinimum(version, minimum) {
298
+ if (version.major !== minimum.major) return version.major > minimum.major;
299
+ if (version.minor !== minimum.minor) return version.minor > minimum.minor;
300
+ return version.patch >= minimum.patch;
301
+ }
302
+
303
+ /**
304
+ * Remove hook types from settings.json that the installed Claude Code version doesn't support.
305
+ * Claude Code rejects the ENTIRE settings file if any hook key is unrecognized.
306
+ * @param {Object} settings - Parsed settings object (mutated in place)
307
+ * @param {{ major: number, minor: number, patch: number } | null} ccVersion - Detected version
308
+ * @returns {string[]} List of removed hook names
309
+ */
310
+ function stripUnsupportedHooks(settings, ccVersion) {
311
+ if (!settings || !settings.hooks || !ccVersion) return [];
312
+
313
+ const removed = [];
314
+ for (const hookName of Object.keys(settings.hooks)) {
315
+ const minVersion = HOOK_VERSION_MAP[hookName];
316
+ if (minVersion && !versionMeetsMinimum(ccVersion, minVersion)) {
317
+ delete settings.hooks[hookName];
318
+ removed.push(`${hookName} (requires ${minVersion.major}.${minVersion.minor}.${minVersion.patch}+)`);
319
+ }
320
+ }
321
+ return removed;
322
+ }
323
+
247
324
  /**
248
325
  * Rewrite hook command paths from local dev paths to package paths.
249
326
  * The package's settings.json uses local paths (node scripts/hooks/...)
@@ -319,6 +396,18 @@ function copyClaudeResources() {
319
396
  // Rewrite hook paths: package uses local dev paths (node scripts/hooks/...)
320
397
  // but user projects need package paths (node node_modules/wogiflow/scripts/hooks/...)
321
398
  rewriteHookPaths(ours);
399
+ // Strip hooks unsupported by the installed Claude Code version.
400
+ // Claude Code rejects the ENTIRE settings.json if any hook key is unrecognized,
401
+ // so we must exclude hooks that the version doesn't support.
402
+ const ccVersion = detectClaudeCodeVersion();
403
+ const removedHooks = stripUnsupportedHooks(ours, ccVersion);
404
+ if (removedHooks.length > 0) {
405
+ console.log(`[wogiflow] Claude Code ${ccVersion.major}.${ccVersion.minor}.${ccVersion.patch} detected. Excluded unsupported hooks:`);
406
+ for (const h of removedHooks) {
407
+ console.log(` - ${h}`);
408
+ }
409
+ console.log('[wogiflow] Update Claude Code for full functionality: npm i -g @anthropic-ai/claude-code@latest');
410
+ }
322
411
  // Always update hooks (core WogiFlow functionality)
323
412
  existing.hooks = ours.hooks;
324
413
  existing._wogiFlowManaged = true;