wogiflow 2.0.0 → 2.1.0
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/.claude/commands/wogi-bulk.md +18 -3
- package/.claude/commands/wogi-pending.md +72 -0
- package/.claude/commands/wogi-start.md +61 -2
- package/.claude/commands/wogi-test.md +12 -0
- package/.workflow/templates/claude-md.hbs +1 -0
- package/package.json +1 -1
- package/scripts/flow +7 -0
- package/scripts/flow-config-defaults.js +28 -1
- package/scripts/flow-constants.js +2 -2
- package/scripts/flow-contract-scan.js +144 -0
- package/scripts/flow-paths.js +2 -0
- package/scripts/flow-pending.js +153 -0
- package/scripts/flow-test-ui.js +1 -1
- package/scripts/flow-version-check.js +14 -11
- package/scripts/hooks/core/session-context.js +12 -0
- package/scripts/hooks/core/session-end.js +32 -0
- package/scripts/hooks/core/task-completed.js +13 -0
- package/scripts/postinstall.js +90 -1
- package/scripts/registries/contract-scanner.js +766 -0
|
@@ -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();
|
|
@@ -251,6 +251,19 @@ async function handleTaskCompleted(input) {
|
|
|
251
251
|
} catch (_err) {
|
|
252
252
|
// Non-critical - registry manager may not be available
|
|
253
253
|
}
|
|
254
|
+
// Check pending queue — notify user if items are waiting
|
|
255
|
+
try {
|
|
256
|
+
const { getPendingCount } = require('../../flow-pending');
|
|
257
|
+
const pendingCount = getPendingCount();
|
|
258
|
+
if (pendingCount > 0) {
|
|
259
|
+
result.pendingQueue = {
|
|
260
|
+
count: pendingCount,
|
|
261
|
+
message: `You have ${pendingCount} pending item${pendingCount !== 1 ? 's' : ''} queued. Run /wogi-pending --list to review, or I'll process them next.`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
} catch (_err) {
|
|
265
|
+
// Non-critical — pending module may not be available
|
|
266
|
+
}
|
|
254
267
|
} catch (err) {
|
|
255
268
|
result.message = `Task completed handler error: ${err.message}`;
|
|
256
269
|
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -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;
|