wogiflow 2.4.2 → 2.4.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/.claude/commands/wogi-start.md +124 -0
- package/.claude/docs/claude-code-compatibility.md +51 -0
- package/.claude/docs/explore-agents.md +11 -0
- package/.claude/settings.json +12 -1
- package/.workflow/models/registry.json +1 -1
- package/bin/flow +11 -1
- package/lib/workspace-contracts.js +599 -0
- package/lib/workspace-intelligence.js +600 -0
- package/lib/workspace-messages.js +441 -0
- package/lib/workspace-routing.js +485 -0
- package/lib/workspace-sync.js +339 -0
- package/lib/workspace.js +1073 -0
- package/package.json +4 -4
- package/scripts/MEMORY-ARCHITECTURE.md +1 -1
- package/scripts/base-workflow-step.js +136 -0
- package/scripts/flow-adaptive-learning.js +8 -9
- package/scripts/flow-aggregate.js +11 -6
- package/scripts/flow-api-index.js +4 -6
- package/scripts/flow-assumption-detector.js +0 -2
- package/scripts/flow-audit.js +15 -2
- package/scripts/flow-auto-context.js +8 -12
- package/scripts/flow-auto-learn.js +49 -49
- package/scripts/flow-background.js +5 -6
- package/scripts/flow-bridge-state.js +8 -10
- package/scripts/flow-bulk-loop.js +1 -3
- package/scripts/flow-bulk-orchestrator.js +1 -3
- package/scripts/flow-cascade-completion.js +0 -2
- package/scripts/flow-cascade.js +4 -4
- package/scripts/flow-checkpoint.js +10 -13
- package/scripts/flow-code-intelligence.js +10 -12
- package/scripts/flow-community-sync.js +4 -4
- package/scripts/flow-community.js +12 -20
- package/scripts/flow-config-defaults.js +28 -2
- package/scripts/flow-config-interactive.js +9 -5
- package/scripts/flow-config-loader.js +49 -92
- package/scripts/flow-config-substitution.js +0 -2
- package/scripts/flow-context-estimator.js +4 -4
- package/scripts/flow-context-init.js +10 -12
- package/scripts/flow-context-manager.js +0 -2
- package/scripts/flow-context-scoring.js +2 -2
- package/scripts/flow-contract-scan.js +6 -9
- package/scripts/flow-correct.js +29 -27
- package/scripts/flow-correction-detector.js +5 -1
- package/scripts/flow-damage-control.js +47 -54
- package/scripts/flow-decisions-merge.js +4 -14
- package/scripts/flow-diff.js +5 -8
- package/scripts/flow-done-gates.js +786 -0
- package/scripts/flow-done-report.js +123 -0
- package/scripts/flow-done.js +71 -717
- package/scripts/flow-entropy-monitor.js +1 -3
- package/scripts/flow-eval-calibration.js +257 -0
- package/scripts/flow-eval-judge.js +10 -1
- package/scripts/flow-eval.js +14 -5
- package/scripts/flow-extraction-review.js +1 -0
- package/scripts/flow-failure-categories.js +0 -2
- package/scripts/flow-figma-confirm.js +5 -9
- package/scripts/flow-figma-generate.js +8 -10
- package/scripts/flow-figma-index.js +8 -10
- package/scripts/flow-figma-match.js +3 -5
- package/scripts/flow-figma-mcp-server.js +2 -4
- package/scripts/flow-figma-orchestrator.js +2 -3
- package/scripts/flow-figma-registry.js +2 -3
- package/scripts/flow-framework-resolver.js +0 -2
- package/scripts/flow-function-index.js +4 -6
- package/scripts/flow-gate-confidence.js +2 -2
- package/scripts/flow-gitignore.js +0 -2
- package/scripts/flow-guided-edit.js +5 -6
- package/scripts/flow-health.js +5 -6
- package/scripts/flow-hook-errors.js +6 -0
- package/scripts/flow-hook-status.js +263 -0
- package/scripts/flow-hooks.js +17 -29
- package/scripts/flow-http-client.js +9 -8
- package/scripts/flow-hybrid-interactive.js +7 -12
- package/scripts/flow-hybrid-test.js +12 -13
- package/scripts/flow-instruction-richness.js +1 -1
- package/scripts/flow-io.js +21 -4
- package/scripts/flow-knowledge-router.js +9 -3
- package/scripts/flow-learning-orchestrator.js +318 -13
- package/scripts/flow-links.js +5 -7
- package/scripts/flow-long-input-association.js +275 -0
- package/scripts/flow-long-input-chunking.js +1 -0
- package/scripts/flow-long-input-cli.js +0 -2
- package/scripts/flow-long-input-complexity.js +0 -2
- package/scripts/flow-long-input-constants.js +0 -2
- package/scripts/flow-long-input-contradictions.js +351 -0
- package/scripts/flow-long-input-detection.js +0 -2
- package/scripts/flow-long-input-passes.js +885 -0
- package/scripts/flow-long-input-stories.js +1 -1
- package/scripts/flow-long-input-voice.js +0 -2
- package/scripts/flow-long-input.js +425 -3005
- package/scripts/flow-loop-retry-learning.js +2 -3
- package/scripts/flow-lsp.js +3 -3
- package/scripts/flow-mcp-docs.js +3 -4
- package/scripts/flow-memory-db.js +6 -8
- package/scripts/flow-memory-sync.js +18 -11
- package/scripts/flow-metrics.js +1 -2
- package/scripts/flow-model-adapter.js +2 -3
- package/scripts/flow-model-config.js +72 -104
- package/scripts/flow-model-router.js +2 -2
- package/scripts/flow-model-types.js +0 -2
- package/scripts/flow-multi-approach.js +5 -6
- package/scripts/flow-orchestrate-context.js +3 -7
- package/scripts/flow-orchestrate-rollback.js +3 -8
- package/scripts/flow-orchestrate-state.js +8 -14
- package/scripts/flow-orchestrate-templates.js +2 -6
- package/scripts/flow-orchestrate-validator.js +5 -9
- package/scripts/flow-orchestrate.js +126 -103
- package/scripts/flow-output.js +0 -2
- package/scripts/flow-parallel.js +1 -1
- package/scripts/flow-paths.js +23 -2
- package/scripts/flow-pattern-enforcer.js +30 -28
- package/scripts/flow-pattern-extractor.js +3 -4
- package/scripts/flow-pending.js +0 -2
- package/scripts/flow-permissions.js +2 -3
- package/scripts/flow-plugin-registry.js +10 -12
- package/scripts/flow-prd-manager.js +1 -1
- package/scripts/flow-progress.js +7 -9
- package/scripts/flow-prompt-composer.js +3 -3
- package/scripts/flow-prompt-template.js +2 -2
- package/scripts/flow-providers.js +7 -4
- package/scripts/flow-registry-manager.js +7 -12
- package/scripts/flow-regression.js +9 -11
- package/scripts/flow-roadmap.js +2 -2
- package/scripts/flow-run-trace.js +16 -15
- package/scripts/flow-safety.js +2 -5
- package/scripts/flow-scanner-base.js +5 -7
- package/scripts/flow-scenario-engine.js +1 -5
- package/scripts/flow-security.js +29 -0
- package/scripts/flow-session-end.js +32 -41
- package/scripts/flow-session-learning.js +53 -49
- package/scripts/flow-setup-hooks.js +2 -3
- package/scripts/flow-skill-create.js +7 -12
- package/scripts/flow-skill-generator.js +12 -16
- package/scripts/flow-skill-learn.js +17 -8
- package/scripts/flow-skill-matcher.js +1 -2
- package/scripts/flow-spec-generator.js +2 -4
- package/scripts/flow-stack-wizard.js +5 -7
- package/scripts/flow-standards-learner.js +35 -16
- package/scripts/flow-start.js +2 -0
- package/scripts/flow-stats-collector.js +2 -2
- package/scripts/flow-status.js +10 -10
- package/scripts/flow-statusline-setup.js +2 -2
- package/scripts/flow-step-changelog.js +2 -3
- package/scripts/flow-step-comments.js +66 -81
- package/scripts/flow-step-complexity.js +50 -70
- package/scripts/flow-step-coverage.js +3 -5
- package/scripts/flow-step-knowledge.js +2 -3
- package/scripts/flow-step-pr-tests.js +64 -74
- package/scripts/flow-step-regression.js +3 -5
- package/scripts/flow-step-review.js +86 -103
- package/scripts/flow-step-security.js +111 -121
- package/scripts/flow-step-silent-failures.js +56 -83
- package/scripts/flow-step-simplifier.js +52 -70
- package/scripts/flow-story.js +4 -7
- package/scripts/flow-strict-adherence.js +3 -4
- package/scripts/flow-task-checkpoint.js +36 -5
- package/scripts/flow-task-enforcer.js +2 -24
- package/scripts/flow-tech-debt.js +1 -1
- package/scripts/flow-template-extractor.js +1 -0
- package/scripts/flow-templates.js +11 -13
- package/scripts/flow-test-api.js +9 -13
- package/scripts/flow-test-discovery.js +1 -1
- package/scripts/flow-test-generate.js +5 -9
- package/scripts/flow-test-integrity.js +3 -7
- package/scripts/flow-test-ui.js +5 -9
- package/scripts/flow-testing-deps.js +1 -3
- package/scripts/flow-tiered-learning.js +4 -4
- package/scripts/flow-todowrite-sync.js +1 -1
- package/scripts/flow-tokens.js +0 -2
- package/scripts/flow-verification-profile.js +6 -10
- package/scripts/flow-verify.js +12 -16
- package/scripts/flow-version-check.js +4 -12
- package/scripts/flow-webmcp-generator.js +3 -5
- package/scripts/flow-workflow-steps.js +0 -2
- package/scripts/flow-workflow.js +9 -11
- package/scripts/hooks/adapters/claude-code.js +31 -0
- package/scripts/hooks/core/config-change.js +1 -0
- package/scripts/hooks/core/extension-registry.js +0 -2
- package/scripts/hooks/core/instructions-loaded.js +1 -1
- package/scripts/hooks/core/observation-capture.js +5 -5
- package/scripts/hooks/core/phase-gate.js +5 -0
- package/scripts/hooks/core/post-compact.js +1 -12
- package/scripts/hooks/core/research-gate.js +2 -12
- package/scripts/hooks/core/routing-gate.js +6 -0
- package/scripts/hooks/core/task-completed.js +12 -0
- package/scripts/hooks/core/task-created.js +83 -0
- package/scripts/hooks/core/worktree-lifecycle.js +1 -1
- package/scripts/hooks/entry/claude-code/config-change.js +6 -29
- package/scripts/hooks/entry/claude-code/instructions-loaded.js +5 -30
- package/scripts/hooks/entry/claude-code/post-compact.js +4 -31
- package/scripts/hooks/entry/claude-code/post-tool-use.js +121 -172
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +260 -361
- package/scripts/hooks/entry/claude-code/session-end.js +4 -28
- package/scripts/hooks/entry/claude-code/session-start.js +205 -243
- package/scripts/hooks/entry/claude-code/setup.js +8 -49
- package/scripts/hooks/entry/claude-code/stop.js +40 -72
- package/scripts/hooks/entry/claude-code/task-completed.js +4 -28
- package/scripts/hooks/entry/claude-code/task-created.js +15 -0
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +113 -195
- package/scripts/hooks/entry/claude-code/worktree-create.js +6 -25
- package/scripts/hooks/entry/claude-code/worktree-remove.js +6 -25
- package/scripts/hooks/entry/shared/hook-runner.js +99 -0
- package/scripts/hooks/entry/shared/read-stdin.js +0 -2
- package/scripts/postinstall.js +2 -0
- package/scripts/registries/api-registry.js +0 -2
- package/scripts/registries/component-registry.js +5 -9
- package/scripts/registries/contract-scanner.js +2 -9
- package/scripts/registries/function-registry.js +0 -2
- package/scripts/registries/schema-registry.js +14 -18
- package/scripts/registries/service-registry.js +23 -27
package/scripts/flow-verify.js
CHANGED
|
@@ -22,15 +22,12 @@
|
|
|
22
22
|
const fs = require('node:fs');
|
|
23
23
|
const path = require('node:path');
|
|
24
24
|
const { spawn, execSync } = require('node:child_process');
|
|
25
|
-
const { getProjectRoot, getConfig, colors: c, readJson } = require('./flow-utils');
|
|
25
|
+
const { getProjectRoot, getConfig, colors: c, readJson, PATHS } = require('./flow-utils');
|
|
26
26
|
const { success: printSuccess, error: printError } = require('./flow-output');
|
|
27
27
|
const { recordCommandResult } = require('./flow-metrics');
|
|
28
28
|
const { detectPackageManager, getExecCommand, getRunPrefix } = require('./flow-script-resolver');
|
|
29
29
|
const { CREDENTIAL_SCAN_PATTERNS } = require('./flow-security');
|
|
30
30
|
|
|
31
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
32
|
-
const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
|
|
33
|
-
|
|
34
31
|
/**
|
|
35
32
|
* Gate result structure
|
|
36
33
|
*/
|
|
@@ -197,7 +194,7 @@ const ERROR_PARSERS = {
|
|
|
197
194
|
if (match) {
|
|
198
195
|
const [, file, lineNum, col, message, rule] = match;
|
|
199
196
|
errors.push({
|
|
200
|
-
file: path.relative(
|
|
197
|
+
file: path.relative(PATHS.root, file),
|
|
201
198
|
line: parseInt(lineNum),
|
|
202
199
|
column: parseInt(col),
|
|
203
200
|
message: message.trim(),
|
|
@@ -220,7 +217,7 @@ const ERROR_PARSERS = {
|
|
|
220
217
|
if (match) {
|
|
221
218
|
const [, file, lineNum, col, severity, code, message] = match;
|
|
222
219
|
errors.push({
|
|
223
|
-
file: path.relative(
|
|
220
|
+
file: path.relative(PATHS.root, file),
|
|
224
221
|
line: parseInt(lineNum),
|
|
225
222
|
column: parseInt(col),
|
|
226
223
|
message,
|
|
@@ -250,7 +247,7 @@ const ERROR_PARSERS = {
|
|
|
250
247
|
const [, file, line, col] = match;
|
|
251
248
|
if (!file.includes('node_modules')) {
|
|
252
249
|
errors.push({
|
|
253
|
-
file: path.relative(
|
|
250
|
+
file: path.relative(PATHS.root, file),
|
|
254
251
|
line: parseInt(line),
|
|
255
252
|
column: parseInt(col),
|
|
256
253
|
message: 'Test assertion failed',
|
|
@@ -363,12 +360,11 @@ const ERROR_PARSERS = {
|
|
|
363
360
|
}
|
|
364
361
|
};
|
|
365
362
|
|
|
366
|
-
|
|
367
363
|
/**
|
|
368
364
|
* Detect which command to use based on installed packages
|
|
369
365
|
*/
|
|
370
366
|
function detectCommand(gate) {
|
|
371
|
-
const packageJsonPath = path.join(
|
|
367
|
+
const packageJsonPath = path.join(PATHS.root, 'package.json');
|
|
372
368
|
let deps = {};
|
|
373
369
|
|
|
374
370
|
if (fs.existsSync(packageJsonPath)) {
|
|
@@ -405,7 +401,7 @@ function runCommand(cmd, args, timeout = 120000) {
|
|
|
405
401
|
let stderrTruncated = false;
|
|
406
402
|
|
|
407
403
|
const proc = spawn(cmd, args, {
|
|
408
|
-
cwd:
|
|
404
|
+
cwd: PATHS.root,
|
|
409
405
|
timeout,
|
|
410
406
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
411
407
|
});
|
|
@@ -687,7 +683,7 @@ function generateFixSuggestions(gateName, errors) {
|
|
|
687
683
|
function getStagedFiles() {
|
|
688
684
|
try {
|
|
689
685
|
const output = execSync('git diff --cached --name-only', {
|
|
690
|
-
cwd:
|
|
686
|
+
cwd: PATHS.root,
|
|
691
687
|
encoding: 'utf-8'
|
|
692
688
|
});
|
|
693
689
|
return output.split('\n').filter(f => f.trim() && /\.(ts|tsx|js|jsx|json|env)$/i.test(f));
|
|
@@ -720,7 +716,7 @@ function findSourceFiles(dir, extensions, limit) {
|
|
|
720
716
|
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
721
717
|
|
|
722
718
|
const fullPath = path.join(currentDir, entry.name);
|
|
723
|
-
const relativePath = path.relative(
|
|
719
|
+
const relativePath = path.relative(PATHS.root, fullPath);
|
|
724
720
|
|
|
725
721
|
if (entry.isDirectory()) {
|
|
726
722
|
walk(fullPath, depth + 1);
|
|
@@ -756,7 +752,7 @@ function checkForSecrets(files) {
|
|
|
756
752
|
});
|
|
757
753
|
if (shouldIgnore) continue;
|
|
758
754
|
|
|
759
|
-
const filePath = path.join(
|
|
755
|
+
const filePath = path.join(PATHS.root, file);
|
|
760
756
|
if (!fs.existsSync(filePath)) continue;
|
|
761
757
|
|
|
762
758
|
try {
|
|
@@ -809,7 +805,7 @@ function checkForInjection(files) {
|
|
|
809
805
|
});
|
|
810
806
|
if (shouldIgnore) continue;
|
|
811
807
|
|
|
812
|
-
const filePath = path.join(
|
|
808
|
+
const filePath = path.join(PATHS.root, file);
|
|
813
809
|
if (!fs.existsSync(filePath)) continue;
|
|
814
810
|
|
|
815
811
|
try {
|
|
@@ -853,7 +849,7 @@ async function runSecurityChecks(gateResult) {
|
|
|
853
849
|
// Fall back to src directory if no staged files
|
|
854
850
|
// Use fs.readdirSync instead of shell find to prevent command injection
|
|
855
851
|
try {
|
|
856
|
-
const srcDir = path.join(
|
|
852
|
+
const srcDir = path.join(PATHS.root, 'src');
|
|
857
853
|
if (fs.existsSync(srcDir)) {
|
|
858
854
|
files = findSourceFiles(srcDir, ['.ts', '.tsx', '.js', '.jsx'], 100);
|
|
859
855
|
}
|
|
@@ -1075,7 +1071,7 @@ function formatResults(results, options = {}) {
|
|
|
1075
1071
|
* Save results to run artifacts
|
|
1076
1072
|
*/
|
|
1077
1073
|
function saveResults(runId, results) {
|
|
1078
|
-
const runDir = path.join(
|
|
1074
|
+
const runDir = path.join(PATHS.workflow, 'runs', runId);
|
|
1079
1075
|
if (!fs.existsSync(runDir)) {
|
|
1080
1076
|
return false;
|
|
1081
1077
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
const fs = require('node:fs');
|
|
17
17
|
const path = require('node:path');
|
|
18
18
|
const { execSync } = require('node:child_process');
|
|
19
|
-
const { PATHS, meetsVersion, readJson } = require('./flow-utils');
|
|
19
|
+
const { PATHS, meetsVersion, readJson, safeJsonParse } = require('./flow-utils');
|
|
20
20
|
|
|
21
21
|
const VERSION_CHECK_PATH = path.join(PATHS.state, '.version-check.json');
|
|
22
22
|
|
|
@@ -46,12 +46,7 @@ function getClaudeCodeVersion() {
|
|
|
46
46
|
* @returns {{ version: string, checkedAt: string, wogiflowVersion: string } | null}
|
|
47
47
|
*/
|
|
48
48
|
function readLastCheck() {
|
|
49
|
-
|
|
50
|
-
const content = fs.readFileSync(VERSION_CHECK_PATH, 'utf-8');
|
|
51
|
-
return JSON.parse(content);
|
|
52
|
-
} catch (err) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
49
|
+
return safeJsonParse(VERSION_CHECK_PATH, null);
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
/**
|
|
@@ -195,9 +190,8 @@ function checkWogiFlowUpdateOnce() {
|
|
|
195
190
|
if (localVersion === 'unknown') return null;
|
|
196
191
|
|
|
197
192
|
// Check cached result
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const data = JSON.parse(cached);
|
|
193
|
+
const data = safeJsonParse(UPDATE_CHECK_PATH, null);
|
|
194
|
+
if (data) {
|
|
201
195
|
const age = Date.now() - new Date(data.checkedAt).getTime();
|
|
202
196
|
if (age < UPDATE_CHECK_TTL_MS && data.localVersion === localVersion) {
|
|
203
197
|
// Still within TTL and same local version — return cached result
|
|
@@ -206,8 +200,6 @@ function checkWogiFlowUpdateOnce() {
|
|
|
206
200
|
}
|
|
207
201
|
return null;
|
|
208
202
|
}
|
|
209
|
-
} catch (err) {
|
|
210
|
-
// No cache or invalid — proceed with fresh check
|
|
211
203
|
}
|
|
212
204
|
|
|
213
205
|
// Fetch from npm
|
|
@@ -16,15 +16,13 @@
|
|
|
16
16
|
const fs = require('node:fs');
|
|
17
17
|
const path = require('node:path');
|
|
18
18
|
const crypto = require('node:crypto');
|
|
19
|
-
const { getProjectRoot, getConfig, color, success, warn, error } = require('./flow-utils');
|
|
19
|
+
const { getProjectRoot, getConfig, color, success, warn, error, PATHS } = require('./flow-utils');
|
|
20
20
|
const { readJson, writeJson, ensureDir, fileExists } = require('./flow-utils');
|
|
21
21
|
const { BaseScanner, PROJECT_ROOT } = require('./flow-scanner-base');
|
|
22
22
|
|
|
23
|
-
const
|
|
24
|
-
const STATE_DIR = path.join(WORKFLOW_DIR, 'state');
|
|
25
|
-
const WEBMCP_DIR = path.join(WORKFLOW_DIR, 'webmcp');
|
|
23
|
+
const WEBMCP_DIR = path.join(PATHS.workflow, 'webmcp');
|
|
26
24
|
const TOOLS_PATH = path.join(WEBMCP_DIR, 'tools.json');
|
|
27
|
-
const APP_MAP_PATH =
|
|
25
|
+
const APP_MAP_PATH = PATHS.appMap;
|
|
28
26
|
|
|
29
27
|
// ============================================================
|
|
30
28
|
// Configuration
|
|
@@ -15,8 +15,6 @@ const fs = require('node:fs');
|
|
|
15
15
|
const path = require('node:path');
|
|
16
16
|
const { getProjectRoot, colors, getConfig, invalidateConfigCache, writeJson, PATHS } = require('./flow-utils');
|
|
17
17
|
|
|
18
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
19
|
-
|
|
20
18
|
// ============================================================
|
|
21
19
|
// Step Registry - All available workflow steps
|
|
22
20
|
// ============================================================
|
package/scripts/flow-workflow.js
CHANGED
|
@@ -24,18 +24,16 @@
|
|
|
24
24
|
const fs = require('node:fs');
|
|
25
25
|
const path = require('node:path');
|
|
26
26
|
const { spawn } = require('node:child_process');
|
|
27
|
-
const { getProjectRoot, colors: c, readJson } = require('./flow-utils');
|
|
27
|
+
const { getProjectRoot, colors: c, readJson, PATHS } = require('./flow-utils');
|
|
28
28
|
const { success: printSuccess, error: printError } = require('./flow-output');
|
|
29
29
|
const { detectPackageManager } = require('./flow-script-resolver');
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
|
|
33
|
-
const WORKFLOWS_DIR = path.join(WORKFLOW_DIR, 'workflows');
|
|
31
|
+
const WORKFLOWS_DIR = path.join(PATHS.workflow, 'workflows');
|
|
34
32
|
|
|
35
33
|
/**
|
|
36
34
|
* Validate that a path is within the project root (prevent path traversal)
|
|
37
35
|
*/
|
|
38
|
-
function validatePathWithinProject(targetPath, baseRoot =
|
|
36
|
+
function validatePathWithinProject(targetPath, baseRoot = PATHS.root) {
|
|
39
37
|
const resolvedPath = path.resolve(baseRoot, targetPath);
|
|
40
38
|
const resolvedRoot = path.resolve(baseRoot);
|
|
41
39
|
|
|
@@ -62,11 +60,11 @@ const STEP_TYPES = {
|
|
|
62
60
|
* Detect project type (language, package manager)
|
|
63
61
|
* Returns { language, packageManager } with defaults to Node.js/npm
|
|
64
62
|
*/
|
|
65
|
-
function detectProjectType(projectRoot =
|
|
63
|
+
function detectProjectType(projectRoot = PATHS.root) {
|
|
66
64
|
// Validate projectRoot to prevent path traversal
|
|
67
|
-
const safeRoot = projectRoot ===
|
|
68
|
-
?
|
|
69
|
-
: validatePathWithinProject(projectRoot,
|
|
65
|
+
const safeRoot = projectRoot === PATHS.root
|
|
66
|
+
? PATHS.root
|
|
67
|
+
: validatePathWithinProject(projectRoot, PATHS.root);
|
|
70
68
|
|
|
71
69
|
// Check for Go
|
|
72
70
|
if (fs.existsSync(path.join(safeRoot, 'go.mod'))) {
|
|
@@ -95,7 +93,7 @@ function detectProjectType(projectRoot = PROJECT_ROOT) {
|
|
|
95
93
|
* Get quality gate command for an action (lint, test, build)
|
|
96
94
|
* Adapts to detected package manager and language
|
|
97
95
|
*/
|
|
98
|
-
function getQualityCommand(action, projectRoot =
|
|
96
|
+
function getQualityCommand(action, projectRoot = PATHS.root) {
|
|
99
97
|
const { language, packageManager } = detectProjectType(projectRoot);
|
|
100
98
|
|
|
101
99
|
const commands = {
|
|
@@ -312,7 +310,7 @@ function executeCommand(command, options = {}) {
|
|
|
312
310
|
const startTime = Date.now();
|
|
313
311
|
|
|
314
312
|
const proc = spawn('sh', ['-c', command], {
|
|
315
|
-
cwd: options.cwd ||
|
|
313
|
+
cwd: options.cwd || PATHS.root,
|
|
316
314
|
env: { ...process.env, ...options.env },
|
|
317
315
|
timeout: options.timeout || 60000
|
|
318
316
|
});
|
|
@@ -32,6 +32,7 @@ const HOOK_TIMEOUTS = {
|
|
|
32
32
|
STOP: 5, // Loop enforcement check
|
|
33
33
|
SESSION_END: 10, // Session cleanup/logging
|
|
34
34
|
TASK_COMPLETED: 10, // Post-task cleanup (Claude Code 2.1.33+)
|
|
35
|
+
TASK_CREATED: 5, // Task creation tracking (Claude Code 2.1.84+)
|
|
35
36
|
TEAMMATE_IDLE: 5, // Task dispatch for idle agents (Claude Code 2.1.33+)
|
|
36
37
|
CONFIG_CHANGE: 5, // Mid-session config change detection (Claude Code latest)
|
|
37
38
|
INSTRUCTIONS_LOADED: 5 // Instructions loaded event (Claude Code latest)
|
|
@@ -55,6 +56,7 @@ const CLAUDE_CODE_EVENTS = [
|
|
|
55
56
|
'ConfigChange', // Claude Code 2.1.63+ — mid-session config change detection
|
|
56
57
|
'InstructionsLoaded', // Claude Code latest — fires when CLAUDE.md/.claude/rules loaded
|
|
57
58
|
'PostCompact', // Claude Code 2.1.76+ — fires after context compaction completes
|
|
59
|
+
'TaskCreated', // Claude Code 2.1.84+ — fires when a task is created via TaskCreate
|
|
58
60
|
];
|
|
59
61
|
|
|
60
62
|
/**
|
|
@@ -67,6 +69,9 @@ const CLAUDE_CODE_EVENTS = [
|
|
|
67
69
|
// 'Notification', // Supported but not yet used by WogiFlow
|
|
68
70
|
// 'Elicitation', // Claude Code 2.1.76+ — intercept MCP elicitation requests before dialog
|
|
69
71
|
// 'ElicitationResult', // Claude Code 2.1.76+ — intercept/override elicitation responses before sending
|
|
72
|
+
// 'CwdChanged', // Claude Code 2.1.83+ — fires when working directory changes (e.g., direnv)
|
|
73
|
+
// 'FileChanged', // Claude Code 2.1.83+ — fires when watched files change on disk
|
|
74
|
+
// 'WorktreeCreate (http)', // Claude Code 2.1.84+ — WorktreeCreate now supports type:"http" transport
|
|
70
75
|
// ];
|
|
71
76
|
|
|
72
77
|
/**
|
|
@@ -202,6 +207,8 @@ class ClaudeCodeAdapter extends BaseAdapter {
|
|
|
202
207
|
return this.transformInstructionsLoaded(coreResult);
|
|
203
208
|
case 'PostCompact':
|
|
204
209
|
return this.transformPostCompact(coreResult);
|
|
210
|
+
case 'TaskCreated':
|
|
211
|
+
return this.transformTaskCreated(coreResult);
|
|
205
212
|
default:
|
|
206
213
|
return { continue: true };
|
|
207
214
|
}
|
|
@@ -504,6 +511,23 @@ Run: /wogi-start ${coreResult.nextTaskId}`;
|
|
|
504
511
|
};
|
|
505
512
|
}
|
|
506
513
|
|
|
514
|
+
/**
|
|
515
|
+
* Transform TaskCreated result (Claude Code 2.1.84+)
|
|
516
|
+
* Fires when a task is created via TaskCreate.
|
|
517
|
+
* Links native tasks to the active WogiFlow task for tracking.
|
|
518
|
+
*/
|
|
519
|
+
transformTaskCreated(coreResult) {
|
|
520
|
+
return {
|
|
521
|
+
continue: true,
|
|
522
|
+
...(coreResult.message && { systemMessage: coreResult.message }),
|
|
523
|
+
hookSpecificOutput: {
|
|
524
|
+
hookEventName: 'TaskCreated',
|
|
525
|
+
linked: coreResult.linked || false,
|
|
526
|
+
wogiTaskId: coreResult.wogiTaskId || null
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
507
531
|
/**
|
|
508
532
|
* Transform InstructionsLoaded result
|
|
509
533
|
* Fires when CLAUDE.md or .claude/rules/*.md files are loaded into context.
|
|
@@ -639,6 +663,13 @@ Run: /wogi-start ${coreResult.nextTaskId}`;
|
|
|
639
663
|
}];
|
|
640
664
|
}
|
|
641
665
|
|
|
666
|
+
// TaskCreated hook — link native tasks to WogiFlow task (Claude Code 2.1.84+)
|
|
667
|
+
if (rules.taskCreated?.enabled !== false) {
|
|
668
|
+
hooks.TaskCreated = [{
|
|
669
|
+
hooks: [hookEntry('TaskCreated', 'task-created.js', HOOK_TIMEOUTS.TASK_CREATED)]
|
|
670
|
+
}];
|
|
671
|
+
}
|
|
672
|
+
|
|
642
673
|
// WorktreeCreate hook — copy essential state to new worktree (Claude Code 2.1.50+)
|
|
643
674
|
if (rules.worktreeLifecycle?.enabled !== false) {
|
|
644
675
|
hooks.WorktreeCreate = [{
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
const fs = require('node:fs');
|
|
15
15
|
const path = require('node:path');
|
|
16
16
|
|
|
17
|
-
const { safeJsonParse } = require('../../flow-utils');
|
|
17
|
+
const { safeJsonParse, PATHS } = require('../../flow-utils');
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Check if new packages have been added since last scan.
|
|
@@ -266,6 +266,11 @@ async function captureObservation(options) {
|
|
|
266
266
|
return { skipped: true, reason: 'missing_tool_name' };
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
+
// Fast path: skip everything if no memory DB file exists (avoids getConfig() + WASM load)
|
|
270
|
+
if (!fs.existsSync(MEMORY_DB_PATH)) {
|
|
271
|
+
return { skipped: true, reason: 'no_memory_db' };
|
|
272
|
+
}
|
|
273
|
+
|
|
269
274
|
try {
|
|
270
275
|
// Load settings ONCE and use directly (avoids 4x redundant getConfig() calls)
|
|
271
276
|
const settings = getObservationSettings();
|
|
@@ -327,11 +332,6 @@ async function captureObservation(options) {
|
|
|
327
332
|
fullOutput = '[serialization failed]';
|
|
328
333
|
}
|
|
329
334
|
|
|
330
|
-
// Fast path: skip DB init if no database file exists yet (avoids 200-800ms WASM load)
|
|
331
|
-
if (!fs.existsSync(MEMORY_DB_PATH)) {
|
|
332
|
-
return { skipped: true, reason: 'no_memory_db' };
|
|
333
|
-
}
|
|
334
|
-
|
|
335
335
|
// Store observation
|
|
336
336
|
const db = getMemoryDb();
|
|
337
337
|
const result = await db.storeObservation({
|
|
@@ -119,6 +119,11 @@ function writePhaseState(state) {
|
|
|
119
119
|
fs.mkdirSync(dir, { recursive: true });
|
|
120
120
|
}
|
|
121
121
|
fs.writeFileSync(PHASE_FILE, JSON.stringify(state, null, 2) + '\n', 'utf-8');
|
|
122
|
+
// Update aggregated hook status
|
|
123
|
+
try {
|
|
124
|
+
const { setPhase } = require('../../flow-hook-status');
|
|
125
|
+
setPhase(state.phase);
|
|
126
|
+
} catch (_err) { /* non-blocking */ }
|
|
122
127
|
return true;
|
|
123
128
|
} catch (err) {
|
|
124
129
|
if (process.env.DEBUG) {
|
|
@@ -22,18 +22,7 @@
|
|
|
22
22
|
const path = require('node:path');
|
|
23
23
|
const fs = require('node:fs');
|
|
24
24
|
const { PATHS, safeJsonParse, getReadyData } = require('../../flow-utils');
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Sanitize a string value before injecting into AI context.
|
|
28
|
-
* Strips markdown heading markers and truncates to prevent prompt manipulation.
|
|
29
|
-
*
|
|
30
|
-
* @param {string} value - Raw string from state files
|
|
31
|
-
* @param {number} [maxLen=200] - Maximum length
|
|
32
|
-
* @returns {string} Sanitized string
|
|
33
|
-
*/
|
|
34
|
-
function sanitize(value, maxLen = 200) {
|
|
35
|
-
return String(value).replace(/^#+\s/gm, '').slice(0, maxLen);
|
|
36
|
-
}
|
|
25
|
+
const { sanitizeForContext: sanitize } = require('../../flow-io');
|
|
37
26
|
|
|
38
27
|
/**
|
|
39
28
|
* Handle PostCompact event.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
const fs = require('node:fs');
|
|
13
13
|
const path = require('node:path');
|
|
14
|
-
const { PATHS } = require('../../flow-utils');
|
|
14
|
+
const { PATHS, safeJsonParse } = require('../../flow-utils');
|
|
15
15
|
const {
|
|
16
16
|
checkResearchGate,
|
|
17
17
|
isResearchEnabled: _isResearchEnabled,
|
|
@@ -423,17 +423,7 @@ function getCachePath() {
|
|
|
423
423
|
*/
|
|
424
424
|
function readCache() {
|
|
425
425
|
const cachePath = getCachePath();
|
|
426
|
-
|
|
427
|
-
if (fs.existsSync(cachePath)) {
|
|
428
|
-
const raw = fs.readFileSync(cachePath, 'utf-8');
|
|
429
|
-
return JSON.parse(raw);
|
|
430
|
-
}
|
|
431
|
-
} catch (err) {
|
|
432
|
-
if (process.env.DEBUG) {
|
|
433
|
-
console.error(`[Research Cache] Read failed: ${err.message}`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
return { entries: {}, lastCleanup: null };
|
|
426
|
+
return safeJsonParse(cachePath, { entries: {}, lastCleanup: null });
|
|
437
427
|
}
|
|
438
428
|
|
|
439
429
|
/**
|
|
@@ -205,6 +205,12 @@ function clearRoutingPending() {
|
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
// Update aggregated hook status
|
|
209
|
+
try {
|
|
210
|
+
const { setRouting } = require('../../flow-hook-status');
|
|
211
|
+
setRouting({ pending: false, cleared: true, clearedAt: new Date().toISOString() });
|
|
212
|
+
} catch (_err) { /* non-blocking */ }
|
|
213
|
+
|
|
208
214
|
return { cleared: flagDeleted, reason: flagDeleted ? 'flag_cleared' : 'unlink_error' };
|
|
209
215
|
}
|
|
210
216
|
|
|
@@ -20,6 +20,7 @@ const fs = require('node:fs');
|
|
|
20
20
|
// Import from parent scripts directory
|
|
21
21
|
const { getConfig, PATHS, safeJsonParse, writeJson, withLock, validateTaskId, archiveCompletedTasksToLog } = require('../../flow-utils');
|
|
22
22
|
const { resetPhase, isPhaseGateEnabled } = require('./phase-gate');
|
|
23
|
+
const { clearOnTaskComplete } = require('../../flow-hook-status');
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Check if task completed handling is enabled
|
|
@@ -146,6 +147,17 @@ async function handleTaskCompleted(input) {
|
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
// Clear hook status on task completion (single aggregated state file)
|
|
151
|
+
if (result.completed) {
|
|
152
|
+
try {
|
|
153
|
+
clearOnTaskComplete();
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (process.env.DEBUG) {
|
|
156
|
+
console.error(`[Task Completed] Hook status clear failed: ${err.message}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
149
161
|
// Clear progress tracker state on task completion
|
|
150
162
|
if (result.completed) {
|
|
151
163
|
try {
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Task Created (Core Module)
|
|
5
|
+
*
|
|
6
|
+
* CLI-agnostic task creation tracking logic.
|
|
7
|
+
* Called when a native task is created via TaskCreate (Claude Code 2.1.84+).
|
|
8
|
+
*
|
|
9
|
+
* Handles:
|
|
10
|
+
* - Linking native Claude Code tasks to the active WogiFlow task
|
|
11
|
+
* - Tracking subtask creation for progress visibility
|
|
12
|
+
* - Logging task creation events to session state
|
|
13
|
+
*
|
|
14
|
+
* Returns a standardized result that adapters transform for specific CLIs.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const { getConfig, PATHS, safeJsonParse } = require('../../flow-utils');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if task created handling is enabled
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
function isTaskCreatedEnabled() {
|
|
25
|
+
const config = getConfig();
|
|
26
|
+
return config.hooks?.rules?.taskCreated?.enabled !== false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handle task creation event
|
|
31
|
+
* @param {Object} input - Parsed hook input from Claude Code
|
|
32
|
+
* @returns {Object} Core result
|
|
33
|
+
*/
|
|
34
|
+
async function handleTaskCreated(input) {
|
|
35
|
+
if (!isTaskCreatedEnabled()) {
|
|
36
|
+
return { enabled: false, message: null };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const result = {
|
|
40
|
+
enabled: true,
|
|
41
|
+
linked: false,
|
|
42
|
+
wogiTaskId: null,
|
|
43
|
+
message: null
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Find the active WogiFlow task to link against
|
|
48
|
+
const readyPath = path.join(PATHS.state, 'ready.json');
|
|
49
|
+
const ready = safeJsonParse(readyPath, { inProgress: [] });
|
|
50
|
+
|
|
51
|
+
if (Array.isArray(ready.inProgress) && ready.inProgress.length > 0) {
|
|
52
|
+
const activeTask = ready.inProgress[0];
|
|
53
|
+
const wogiTaskId = typeof activeTask === 'string' ? activeTask : activeTask?.id;
|
|
54
|
+
|
|
55
|
+
if (wogiTaskId) {
|
|
56
|
+
result.linked = true;
|
|
57
|
+
result.wogiTaskId = wogiTaskId;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Track creation in session state (fire-and-forget)
|
|
62
|
+
try {
|
|
63
|
+
const sessionStatePath = path.join(PATHS.state, 'session-state.json');
|
|
64
|
+
const sessionState = safeJsonParse(sessionStatePath, {});
|
|
65
|
+
if (!sessionState.nativeTasksCreated) {
|
|
66
|
+
sessionState.nativeTasksCreated = 0;
|
|
67
|
+
}
|
|
68
|
+
sessionState.nativeTasksCreated += 1;
|
|
69
|
+
sessionState.lastNativeTaskAt = new Date().toISOString();
|
|
70
|
+
|
|
71
|
+
const fs = require('node:fs');
|
|
72
|
+
fs.writeFileSync(sessionStatePath, JSON.stringify(sessionState, null, 2));
|
|
73
|
+
} catch (_err) {
|
|
74
|
+
// Non-critical — session state tracking is best-effort
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
result.message = `Task created handler error: ${err.message}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { handleTaskCreated, isTaskCreatedEnabled };
|
|
@@ -25,7 +25,7 @@ const CORE_STATE_FILES = ['ready.json', 'decisions.md'];
|
|
|
25
25
|
|
|
26
26
|
function getEssentialStateFiles() {
|
|
27
27
|
try {
|
|
28
|
-
const { getRegistryMapFiles } = require('../../flow-utils');
|
|
28
|
+
const { getRegistryMapFiles, PATHS } = require('../../flow-utils');
|
|
29
29
|
return [...CORE_STATE_FILES, ...getRegistryMapFiles()];
|
|
30
30
|
} catch (_err) {
|
|
31
31
|
return [...CORE_STATE_FILES, 'app-map.md', 'function-map.md', 'api-map.md'];
|
|
@@ -11,33 +11,10 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const { handleConfigChange } = require('../../core/config-change');
|
|
14
|
-
const {
|
|
15
|
-
const { readHookInput } = require('../shared/read-stdin');
|
|
14
|
+
const { runHook } = require('../shared/hook-runner');
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const input = parsedStdin || {};
|
|
23
|
-
|
|
24
|
-
// Extract the changed file path from the hook input
|
|
25
|
-
const filePath = input.file_path || input.filePath || '';
|
|
26
|
-
const projectRoot = input.cwd || process.cwd();
|
|
27
|
-
|
|
28
|
-
// Handle the config change
|
|
29
|
-
const result = handleConfigChange({ filePath, projectRoot });
|
|
30
|
-
|
|
31
|
-
// Transform to Claude Code format via adapter (consistent with other hooks)
|
|
32
|
-
const output = claudeCodeAdapter.transformResult('ConfigChange', result);
|
|
33
|
-
|
|
34
|
-
process.stdout.write(JSON.stringify(output));
|
|
35
|
-
process.exit(0);
|
|
36
|
-
} catch (err) {
|
|
37
|
-
// Never block on config change errors
|
|
38
|
-
process.stdout.write(JSON.stringify({ continue: true }));
|
|
39
|
-
process.exit(0);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
main();
|
|
16
|
+
runHook('ConfigChange', async ({ input }) => {
|
|
17
|
+
const filePath = input.file_path || input.filePath || '';
|
|
18
|
+
const projectRoot = input.cwd || process.cwd();
|
|
19
|
+
return handleConfigChange({ filePath, projectRoot });
|
|
20
|
+
}, { failMode: 'silent', useStdoutWrite: true });
|
|
@@ -14,34 +14,9 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
const { handleInstructionsLoaded } = require('../../core/instructions-loaded');
|
|
17
|
-
const {
|
|
18
|
-
const { readHookInput } = require('../shared/read-stdin');
|
|
17
|
+
const { runHook } = require('../shared/hook-runner');
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const { input: parsedStdin } = await readHookInput();
|
|
25
|
-
const input = parsedStdin || {};
|
|
26
|
-
const parsedInput = claudeCodeAdapter.parseInput(input);
|
|
27
|
-
const projectRoot = parsedInput.cwd || process.cwd();
|
|
28
|
-
|
|
29
|
-
// Handle the instructions loaded event
|
|
30
|
-
const result = handleInstructionsLoaded({ projectRoot });
|
|
31
|
-
|
|
32
|
-
// Transform to Claude Code format via adapter
|
|
33
|
-
const output = claudeCodeAdapter.transformResult('InstructionsLoaded', result);
|
|
34
|
-
|
|
35
|
-
process.stdout.write(JSON.stringify(output));
|
|
36
|
-
process.exit(0);
|
|
37
|
-
} catch (err) {
|
|
38
|
-
// Never block on errors
|
|
39
|
-
if (process.env.DEBUG) {
|
|
40
|
-
console.error(`[instructions-loaded] Error: ${err.message}`);
|
|
41
|
-
}
|
|
42
|
-
process.stdout.write(JSON.stringify({ continue: true }));
|
|
43
|
-
process.exit(0);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
main();
|
|
19
|
+
runHook('InstructionsLoaded', async ({ parsedInput }) => {
|
|
20
|
+
const projectRoot = parsedInput.cwd || process.cwd();
|
|
21
|
+
return handleInstructionsLoaded({ projectRoot });
|
|
22
|
+
}, { failMode: 'silent', useStdoutWrite: true });
|