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
|
@@ -35,7 +35,6 @@ const {
|
|
|
35
35
|
} = require('./flow-utils')
|
|
36
36
|
const { color, success, warn, error } = require('./flow-output');;
|
|
37
37
|
|
|
38
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
39
38
|
const SESSION_FILE = path.join(PATHS.state, 'guided-edit-session.json');
|
|
40
39
|
|
|
41
40
|
// ============================================================
|
|
@@ -141,14 +140,14 @@ function findAffectedFiles(search, options = {}) {
|
|
|
141
140
|
|
|
142
141
|
// Try options first
|
|
143
142
|
if (options.srcDir) {
|
|
144
|
-
srcDir = path.isAbsolute(options.srcDir) ? options.srcDir : path.join(
|
|
143
|
+
srcDir = path.isAbsolute(options.srcDir) ? options.srcDir : path.join(PATHS.root, options.srcDir);
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
// Try config
|
|
148
147
|
if (!srcDir || !fs.existsSync(srcDir)) {
|
|
149
148
|
const configSrcDir = config.guidedEdit?.srcDir;
|
|
150
149
|
if (configSrcDir) {
|
|
151
|
-
const resolved = path.isAbsolute(configSrcDir) ? configSrcDir : path.join(
|
|
150
|
+
const resolved = path.isAbsolute(configSrcDir) ? configSrcDir : path.join(PATHS.root, configSrcDir);
|
|
152
151
|
if (fs.existsSync(resolved)) {
|
|
153
152
|
srcDir = resolved;
|
|
154
153
|
}
|
|
@@ -157,8 +156,8 @@ function findAffectedFiles(search, options = {}) {
|
|
|
157
156
|
|
|
158
157
|
// Fallback to src/ or project root
|
|
159
158
|
if (!srcDir || !fs.existsSync(srcDir)) {
|
|
160
|
-
const defaultSrc = path.join(
|
|
161
|
-
srcDir = fs.existsSync(defaultSrc) ? defaultSrc :
|
|
159
|
+
const defaultSrc = path.join(PATHS.root, 'src');
|
|
160
|
+
srcDir = fs.existsSync(defaultSrc) ? defaultSrc : PATHS.root;
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
const extensions = options.extensions || config.guidedEdit?.extensions || ['ts', 'tsx', 'js', 'jsx', 'vue', 'svelte'];
|
|
@@ -190,7 +189,7 @@ function findAffectedFiles(search, options = {}) {
|
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
results.push({
|
|
193
|
-
path: path.relative(
|
|
192
|
+
path: path.relative(PATHS.root, file),
|
|
194
193
|
absolutePath: file,
|
|
195
194
|
matchCount: matches.length,
|
|
196
195
|
matches: matches.slice(0, 5), // First 5 matches
|
package/scripts/flow-health.js
CHANGED
|
@@ -267,9 +267,9 @@ function main() {
|
|
|
267
267
|
printSection('Checking universal structure...');
|
|
268
268
|
|
|
269
269
|
const universalDirs = [
|
|
270
|
-
{ path:
|
|
271
|
-
{ path:
|
|
272
|
-
{ path:
|
|
270
|
+
{ path: PATHS.modelsDir, name: '.workflow/models' },
|
|
271
|
+
{ path: PATHS.bridges, name: '.workflow/bridges' },
|
|
272
|
+
{ path: PATHS.templates, name: '.workflow/templates' },
|
|
273
273
|
];
|
|
274
274
|
|
|
275
275
|
for (const dir of universalDirs) {
|
|
@@ -282,7 +282,7 @@ function main() {
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
// Check model registry
|
|
285
|
-
const registryPath = path.join(
|
|
285
|
+
const registryPath = path.join(PATHS.workflow, 'models', 'registry.json');
|
|
286
286
|
if (fileExists(registryPath)) {
|
|
287
287
|
const result = validateJson(registryPath);
|
|
288
288
|
if (result.valid) {
|
|
@@ -376,8 +376,7 @@ function main() {
|
|
|
376
376
|
|
|
377
377
|
if (fileExists(tsconfigPath)) {
|
|
378
378
|
try {
|
|
379
|
-
const
|
|
380
|
-
const tsconfig = JSON.parse(tsconfigContent);
|
|
379
|
+
const tsconfig = safeJsonParse(tsconfigPath, {});
|
|
381
380
|
const hasProjectRefs = Array.isArray(tsconfig.references) && tsconfig.references.length > 0;
|
|
382
381
|
const hasEmptyFiles = Array.isArray(tsconfig.files) && tsconfig.files.length === 0;
|
|
383
382
|
const isProjectRefsMode = hasProjectRefs && hasEmptyFiles;
|
|
@@ -153,6 +153,12 @@ function formatHookError(hookName, err, options = {}) {
|
|
|
153
153
|
*/
|
|
154
154
|
function logHookError(hookName, err, options = {}) {
|
|
155
155
|
const formatted = formatHookError(hookName, err, options);
|
|
156
|
+
// Debug env var hierarchy:
|
|
157
|
+
// DEBUG — General-purpose debug flag (used across ~30 scripts for verbose logging)
|
|
158
|
+
// WOGIFLOW_DEBUG — WogiFlow-specific debug flag (equivalent to DEBUG but namespaced)
|
|
159
|
+
// DEBUG_LSP — LSP-only debug flag (used exclusively in flow-lsp.js for LSP protocol debugging)
|
|
160
|
+
// DEBUG and WOGIFLOW_DEBUG are interchangeable; either enables verbose error output.
|
|
161
|
+
// DEBUG_LSP is independent — it controls only LSP stderr/parse-error logging.
|
|
156
162
|
const isDebug = process.env.DEBUG || process.env.WOGIFLOW_DEBUG;
|
|
157
163
|
|
|
158
164
|
if (isDebug) {
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wogi Flow - Hook Status Aggregator
|
|
3
|
+
*
|
|
4
|
+
* Pre-computes a single hook-status.json file containing all state
|
|
5
|
+
* that PreToolUse/PostToolUse hooks need. Hooks read 1 file instead
|
|
6
|
+
* of 6-8 separate reads per invocation.
|
|
7
|
+
*
|
|
8
|
+
* Writers: flow-start.js, task-completed.js, routing-gate.js, phase-gate.js
|
|
9
|
+
* Reader: pre-tool-use.js, post-tool-use.js
|
|
10
|
+
*
|
|
11
|
+
* IMPORTANT: This module must NOT require flow-utils at load time (circular dep risk).
|
|
12
|
+
* flow-utils does NOT require this module — keep it that way.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('node:fs');
|
|
16
|
+
const path = require('node:path');
|
|
17
|
+
|
|
18
|
+
// Lazy-load to avoid circular deps at module level
|
|
19
|
+
let _PATHS = null;
|
|
20
|
+
function getPATHS() {
|
|
21
|
+
if (!_PATHS) {
|
|
22
|
+
_PATHS = require('./flow-paths').PATHS;
|
|
23
|
+
}
|
|
24
|
+
return _PATHS;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Lazy-load safeJsonParseString to avoid circular deps
|
|
28
|
+
let _safeJsonParseString = null;
|
|
29
|
+
function getSafeJsonParseString() {
|
|
30
|
+
if (!_safeJsonParseString) {
|
|
31
|
+
_safeJsonParseString = require('./flow-io').safeJsonParseString;
|
|
32
|
+
}
|
|
33
|
+
return _safeJsonParseString;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const HOOK_STATUS_FILENAME = 'hook-status.json';
|
|
37
|
+
|
|
38
|
+
function getHookStatusPath() {
|
|
39
|
+
return path.join(getPATHS().state, HOOK_STATUS_FILENAME);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Read the current hook status (single file read).
|
|
44
|
+
* Uses safeJsonParseString for prototype pollution protection.
|
|
45
|
+
* Returns null if file doesn't exist, is corrupt, or stale vs config.json.
|
|
46
|
+
* @returns {Object|null}
|
|
47
|
+
*/
|
|
48
|
+
function readHookStatus() {
|
|
49
|
+
try {
|
|
50
|
+
const statusPath = getHookStatusPath();
|
|
51
|
+
const content = fs.readFileSync(statusPath, 'utf-8');
|
|
52
|
+
const parsed = getSafeJsonParseString()(content, null);
|
|
53
|
+
if (!parsed) return null;
|
|
54
|
+
|
|
55
|
+
// Staleness check: if config.json is newer than hook-status.json, invalidate
|
|
56
|
+
try {
|
|
57
|
+
const configPath = path.join(getPATHS().workflow, 'config.json');
|
|
58
|
+
const configMtime = fs.statSync(configPath).mtimeMs;
|
|
59
|
+
const statusMtime = fs.statSync(statusPath).mtimeMs;
|
|
60
|
+
if (configMtime > statusMtime) {
|
|
61
|
+
// Config changed after hook-status was written — refresh needed
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
} catch (_err) { /* config missing or stat failed — use cached status */ }
|
|
65
|
+
|
|
66
|
+
return parsed;
|
|
67
|
+
} catch (_err) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Write the hook status file atomically with PID-scoped temp file.
|
|
74
|
+
* @param {Object} status - The full status object
|
|
75
|
+
*/
|
|
76
|
+
function writeHookStatus(status) {
|
|
77
|
+
status.updatedAt = new Date().toISOString();
|
|
78
|
+
const statusPath = getHookStatusPath();
|
|
79
|
+
const tmpPath = statusPath + '.tmp.' + process.pid;
|
|
80
|
+
try {
|
|
81
|
+
fs.writeFileSync(tmpPath, JSON.stringify(status, null, 2));
|
|
82
|
+
fs.renameSync(tmpPath, statusPath);
|
|
83
|
+
} catch (_err) {
|
|
84
|
+
// Non-blocking — hooks fall back to individual reads
|
|
85
|
+
try { fs.unlinkSync(tmpPath); } catch (_e) { /* ignore */ }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build enforcement block from config (shared by createDefaultStatus + refreshHookStatus).
|
|
91
|
+
* @param {Object} config - Parsed config object
|
|
92
|
+
* @returns {Object} enforcement flags + componentReuse + phaseGate
|
|
93
|
+
*/
|
|
94
|
+
function buildEnforcementFromConfig(config) {
|
|
95
|
+
return {
|
|
96
|
+
enforcement: {
|
|
97
|
+
taskGating: config.enforcement?.taskGating?.enabled === true,
|
|
98
|
+
scopeGating: config.enforcement?.scopeGating?.enabled === true,
|
|
99
|
+
routingGate: config.enforcement?.routingGate?.enabled === true,
|
|
100
|
+
commitLogGate: config.enforcement?.commitLogGate?.enabled === true,
|
|
101
|
+
todoWriteGate: config.enforcement?.todoWriteGate?.enabled === true,
|
|
102
|
+
loopEnforcement: config.enforcement?.loopEnforcement?.enabled === true
|
|
103
|
+
},
|
|
104
|
+
componentReuse: config.componentReuse?.enabled === true,
|
|
105
|
+
// Correct path: hooks.rules.phaseGate.enabled (matches phase-gate.js:84)
|
|
106
|
+
phaseGate: config.hooks?.rules?.phaseGate?.enabled === true
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update specific fields in hook status (read-modify-write).
|
|
112
|
+
* Creates the file if it doesn't exist.
|
|
113
|
+
* Deep-merges known nested keys (enforcement, routing) to prevent clobbering.
|
|
114
|
+
* @param {Object} updates - Fields to merge into current status
|
|
115
|
+
*/
|
|
116
|
+
function updateHookStatus(updates) {
|
|
117
|
+
const current = readHookStatus() || createDefaultStatus();
|
|
118
|
+
// Deep-merge known nested keys to prevent shallow-assign clobbering
|
|
119
|
+
if (updates.enforcement) {
|
|
120
|
+
updates.enforcement = { ...current.enforcement, ...updates.enforcement };
|
|
121
|
+
}
|
|
122
|
+
if (updates.routing) {
|
|
123
|
+
updates.routing = { ...current.routing, ...updates.routing };
|
|
124
|
+
}
|
|
125
|
+
Object.assign(current, updates);
|
|
126
|
+
writeHookStatus(current);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create default hook status from current config + state.
|
|
131
|
+
* Called when hook-status.json doesn't exist yet.
|
|
132
|
+
* @returns {Object}
|
|
133
|
+
*/
|
|
134
|
+
function createDefaultStatus() {
|
|
135
|
+
let config = {};
|
|
136
|
+
try {
|
|
137
|
+
const { getConfig } = require('./flow-utils');
|
|
138
|
+
config = getConfig() || {};
|
|
139
|
+
} catch (_err) { /* ignore */ }
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
...buildEnforcementFromConfig(config),
|
|
143
|
+
activeTask: null,
|
|
144
|
+
phase: 'idle',
|
|
145
|
+
routing: {
|
|
146
|
+
pending: false,
|
|
147
|
+
cleared: false,
|
|
148
|
+
clearedAt: null
|
|
149
|
+
},
|
|
150
|
+
changedFiles: [],
|
|
151
|
+
updatedAt: new Date().toISOString()
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Refresh hook status from current config and state files.
|
|
157
|
+
* Called at session start and after config changes.
|
|
158
|
+
*/
|
|
159
|
+
function refreshHookStatus() {
|
|
160
|
+
let config = {};
|
|
161
|
+
try {
|
|
162
|
+
const { getConfig } = require('./flow-utils');
|
|
163
|
+
config = getConfig() || {};
|
|
164
|
+
} catch (_err) { /* ignore */ }
|
|
165
|
+
|
|
166
|
+
let activeTask = null;
|
|
167
|
+
try {
|
|
168
|
+
const { getReadyData } = require('./flow-utils');
|
|
169
|
+
const readyData = getReadyData();
|
|
170
|
+
if (readyData.inProgress && readyData.inProgress.length > 0) {
|
|
171
|
+
const task = readyData.inProgress[0];
|
|
172
|
+
activeTask = { id: task.id, title: task.title, routedAt: task.routedAt || null };
|
|
173
|
+
}
|
|
174
|
+
} catch (_err) { /* ignore */ }
|
|
175
|
+
|
|
176
|
+
let phase = 'idle';
|
|
177
|
+
try {
|
|
178
|
+
const { safeJsonParse } = require('./flow-utils');
|
|
179
|
+
const phasePath = path.join(getPATHS().state, 'workflow-phase.json');
|
|
180
|
+
const phaseData = safeJsonParse(phasePath, null);
|
|
181
|
+
if (phaseData) phase = phaseData.phase || 'idle';
|
|
182
|
+
} catch (_err) { /* ignore */ }
|
|
183
|
+
|
|
184
|
+
const status = {
|
|
185
|
+
...buildEnforcementFromConfig(config),
|
|
186
|
+
activeTask,
|
|
187
|
+
phase,
|
|
188
|
+
routing: {
|
|
189
|
+
pending: false,
|
|
190
|
+
cleared: false,
|
|
191
|
+
clearedAt: null
|
|
192
|
+
},
|
|
193
|
+
changedFiles: []
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
writeHookStatus(status);
|
|
197
|
+
return status;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Update just the active task field.
|
|
202
|
+
* @param {Object|null} task - { id, title, routedAt } or null
|
|
203
|
+
*/
|
|
204
|
+
function setActiveTask(task) {
|
|
205
|
+
updateHookStatus({ activeTask: task });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Update just the phase field.
|
|
210
|
+
* @param {string} phase
|
|
211
|
+
*/
|
|
212
|
+
function setPhase(phase) {
|
|
213
|
+
updateHookStatus({ phase });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Update routing state.
|
|
218
|
+
* @param {Object} routing - { pending, cleared, clearedAt }
|
|
219
|
+
*/
|
|
220
|
+
function setRouting(routing) {
|
|
221
|
+
updateHookStatus({ routing });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Add a file to the changed files list (deduped).
|
|
226
|
+
* @param {string} filePath
|
|
227
|
+
*/
|
|
228
|
+
function trackFile(filePath) {
|
|
229
|
+
const current = readHookStatus();
|
|
230
|
+
if (!current) return;
|
|
231
|
+
if (!current.changedFiles) current.changedFiles = [];
|
|
232
|
+
if (!current.changedFiles.includes(filePath)) {
|
|
233
|
+
current.changedFiles.push(filePath);
|
|
234
|
+
writeHookStatus(current);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Clear hook status on task completion.
|
|
240
|
+
*/
|
|
241
|
+
function clearOnTaskComplete() {
|
|
242
|
+
const current = readHookStatus() || createDefaultStatus();
|
|
243
|
+
current.activeTask = null;
|
|
244
|
+
current.phase = 'idle';
|
|
245
|
+
current.routing = { pending: false, cleared: false, clearedAt: null };
|
|
246
|
+
current.changedFiles = [];
|
|
247
|
+
writeHookStatus(current);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
module.exports = {
|
|
251
|
+
readHookStatus,
|
|
252
|
+
writeHookStatus,
|
|
253
|
+
updateHookStatus,
|
|
254
|
+
refreshHookStatus,
|
|
255
|
+
createDefaultStatus,
|
|
256
|
+
setActiveTask,
|
|
257
|
+
setPhase,
|
|
258
|
+
setRouting,
|
|
259
|
+
trackFile,
|
|
260
|
+
clearOnTaskComplete,
|
|
261
|
+
getHookStatusPath,
|
|
262
|
+
HOOK_STATUS_FILENAME
|
|
263
|
+
};
|
package/scripts/flow-hooks.js
CHANGED
|
@@ -18,28 +18,26 @@ const fs = require('node:fs');
|
|
|
18
18
|
const path = require('node:path');
|
|
19
19
|
const {
|
|
20
20
|
getProjectRoot,
|
|
21
|
-
getConfig
|
|
21
|
+
getConfig, PATHS
|
|
22
22
|
} = require('./flow-utils')
|
|
23
23
|
const { color, success, warn, error } = require('./flow-output');;
|
|
24
24
|
|
|
25
25
|
const { getAdapter, getAllAdapters, getAvailableAdapters } = require('./hooks/adapters');
|
|
26
|
-
const { readJson } = require('./flow-io');
|
|
27
|
-
|
|
28
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
26
|
+
const { readJson, safeJsonParse } = require('./flow-io');
|
|
29
27
|
|
|
30
28
|
/**
|
|
31
29
|
* Get installed WogiFlow version from package.json (canonical source)
|
|
32
30
|
* @returns {string} Version string or 'unknown'
|
|
33
31
|
*/
|
|
34
32
|
function getInstalledVersion() {
|
|
35
|
-
const pkgPath = path.join(
|
|
33
|
+
const pkgPath = path.join(PATHS.root, 'node_modules', 'wogiflow', 'package.json');
|
|
36
34
|
const pkg = readJson(pkgPath, null);
|
|
37
35
|
if (pkg) {
|
|
38
36
|
return pkg.version || 'unknown';
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
// Fallback: try settings.json (legacy)
|
|
42
|
-
const settingsPath = path.join(
|
|
40
|
+
const settingsPath = path.join(PATHS.root, '.claude', 'settings.json');
|
|
43
41
|
const settings = readJson(settingsPath, null);
|
|
44
42
|
if (settings) {
|
|
45
43
|
return settings._wogiFlowVersion || 'unknown';
|
|
@@ -113,7 +111,7 @@ function installForTarget(targetName) {
|
|
|
113
111
|
headers: config.httpHeaders || {},
|
|
114
112
|
allowedEnvVars: config.httpAllowedEnvVars || []
|
|
115
113
|
} : undefined;
|
|
116
|
-
const hooksConfig = adapter.generateConfig(config.rules,
|
|
114
|
+
const hooksConfig = adapter.generateConfig(config.rules, PATHS.root, transportConfig);
|
|
117
115
|
|
|
118
116
|
// For Claude Code, we need to merge into settings.local.json
|
|
119
117
|
if (targetName === 'claude-code') {
|
|
@@ -138,15 +136,7 @@ function installClaudeCodeHooks(adapter, hooksConfig) {
|
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
// Read existing config
|
|
141
|
-
let existingConfig = {};
|
|
142
|
-
if (fs.existsSync(configPath)) {
|
|
143
|
-
try {
|
|
144
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
145
|
-
existingConfig = JSON.parse(content);
|
|
146
|
-
} catch (err) {
|
|
147
|
-
warn(` Could not parse existing config, will create new`);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
139
|
+
let existingConfig = safeJsonParse(configPath, {});
|
|
150
140
|
|
|
151
141
|
// Check if we're overwriting non-Wogi hooks
|
|
152
142
|
if (existingConfig.hooks && !existingConfig._wogiFlowManaged) {
|
|
@@ -166,7 +156,7 @@ function installClaudeCodeHooks(adapter, hooksConfig) {
|
|
|
166
156
|
|
|
167
157
|
// Write config
|
|
168
158
|
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2));
|
|
169
|
-
success(`Hooks written to ${path.relative(
|
|
159
|
+
success(`Hooks written to ${path.relative(PATHS.root, configPath)}`);
|
|
170
160
|
|
|
171
161
|
return true;
|
|
172
162
|
}
|
|
@@ -250,8 +240,12 @@ function removeClaudeCodeHooks(adapter) {
|
|
|
250
240
|
}
|
|
251
241
|
|
|
252
242
|
try {
|
|
253
|
-
const
|
|
254
|
-
|
|
243
|
+
const config = safeJsonParse(configPath, null);
|
|
244
|
+
|
|
245
|
+
if (!config) {
|
|
246
|
+
warn(` Config file is corrupt or unreadable (skipping)`);
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
255
249
|
|
|
256
250
|
if (!config._wogiFlowManaged) {
|
|
257
251
|
warn(` Config not managed by Wogi Flow (skipping)`);
|
|
@@ -275,8 +269,7 @@ function removeClaudeCodeHooks(adapter) {
|
|
|
275
269
|
// Restore backup if exists
|
|
276
270
|
const backupPath = configPath + '.backup';
|
|
277
271
|
if (fs.existsSync(backupPath)) {
|
|
278
|
-
const
|
|
279
|
-
const backupConfig = JSON.parse(backupContent);
|
|
272
|
+
const backupConfig = safeJsonParse(backupPath, {});
|
|
280
273
|
if (backupConfig.hooks) {
|
|
281
274
|
const finalConfig = { ...config, hooks: backupConfig.hooks };
|
|
282
275
|
fs.writeFileSync(configPath, JSON.stringify(finalConfig, null, 2));
|
|
@@ -375,13 +368,8 @@ function checkIfInstalled(adapter) {
|
|
|
375
368
|
if (!fs.existsSync(configPath)) {
|
|
376
369
|
return false;
|
|
377
370
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const config = JSON.parse(content);
|
|
381
|
-
return config._wogiFlowManaged === true;
|
|
382
|
-
} catch (_err) {
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
371
|
+
const config = safeJsonParse(configPath, {});
|
|
372
|
+
return config._wogiFlowManaged === true;
|
|
385
373
|
}
|
|
386
374
|
return false;
|
|
387
375
|
}
|
|
@@ -434,7 +422,7 @@ async function testHook(hookName) {
|
|
|
434
422
|
|
|
435
423
|
const { spawn } = require('node:child_process');
|
|
436
424
|
const proc = spawn('node', [hookPath], {
|
|
437
|
-
cwd:
|
|
425
|
+
cwd: PATHS.root,
|
|
438
426
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
439
427
|
});
|
|
440
428
|
|
|
@@ -22,9 +22,9 @@ const { TIMEOUTS, LIMITS, BACKOFF } = require('./flow-constants');
|
|
|
22
22
|
class HttpClient {
|
|
23
23
|
constructor(baseUrl, options = {}) {
|
|
24
24
|
this.baseUrl = baseUrl;
|
|
25
|
-
this.defaultHeaders = options.headers
|
|
26
|
-
this.timeout = options.timeout
|
|
27
|
-
this.maxRetries = options.maxRetries
|
|
25
|
+
this.defaultHeaders = options.headers ?? {};
|
|
26
|
+
this.timeout = options.timeout ?? TIMEOUTS.HTTP_DEFAULT;
|
|
27
|
+
this.maxRetries = options.maxRetries ?? LIMITS.HTTP_MAX_RETRIES;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -55,10 +55,10 @@ class HttpClient {
|
|
|
55
55
|
port: url.port || (isHttps ? 443 : 80),
|
|
56
56
|
path: url.pathname + url.search,
|
|
57
57
|
headers,
|
|
58
|
-
timeout: options.timeout
|
|
58
|
+
timeout: options.timeout ?? this.timeout,
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
-
return this._executeWithRetry(lib, requestOptions, body, options.retries
|
|
61
|
+
return this._executeWithRetry(lib, requestOptions, body, options.retries ?? 0);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -147,7 +147,8 @@ class HttpClient {
|
|
|
147
147
|
* Sleep helper
|
|
148
148
|
*/
|
|
149
149
|
_sleep(ms) {
|
|
150
|
-
|
|
150
|
+
const { setTimeout: sleep } = require('node:timers/promises');
|
|
151
|
+
return sleep(ms);
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
// Convenience methods
|
|
@@ -222,7 +223,7 @@ class HttpClient {
|
|
|
222
223
|
* Simple fetch JSON helper (for one-off requests)
|
|
223
224
|
*/
|
|
224
225
|
async function fetchJson(url, options = {}) {
|
|
225
|
-
const client = new HttpClient(url, { timeout: options.timeout
|
|
226
|
+
const client = new HttpClient(url, { timeout: options.timeout ?? TIMEOUTS.HTTP_DEFAULT });
|
|
226
227
|
const parsedUrl = new URL(url);
|
|
227
228
|
const response = await client.get(parsedUrl.pathname + parsedUrl.search, {
|
|
228
229
|
headers: options.headers,
|
|
@@ -234,7 +235,7 @@ async function fetchJson(url, options = {}) {
|
|
|
234
235
|
* Simple post JSON helper (for one-off requests)
|
|
235
236
|
*/
|
|
236
237
|
async function postJson(url, body, options = {}) {
|
|
237
|
-
const client = new HttpClient(url, { timeout: options.timeout
|
|
238
|
+
const client = new HttpClient(url, { timeout: options.timeout ?? TIMEOUTS.HTTP_DEFAULT });
|
|
238
239
|
const parsedUrl = new URL(url);
|
|
239
240
|
const response = await client.post(parsedUrl.pathname + parsedUrl.search, body, {
|
|
240
241
|
headers: options.headers,
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
const fs = require('node:fs');
|
|
12
12
|
const path = require('node:path');
|
|
13
|
-
const readline = require('node:readline');
|
|
13
|
+
const readline = require('node:readline/promises');
|
|
14
14
|
const http = require('node:http');
|
|
15
15
|
const { HttpClient } = require('./flow-http-client');
|
|
16
16
|
const { URL, URLSearchParams } = require('node:url');
|
|
17
|
-
const { getProjectRoot, colors, safeJsonParse } = require('./flow-utils');
|
|
17
|
+
const { getProjectRoot, colors, safeJsonParse, PATHS } = require('./flow-utils');
|
|
18
18
|
const { error: errorMsg } = require('./flow-output');
|
|
19
19
|
|
|
20
20
|
// Import model registry for smart model selection
|
|
@@ -26,9 +26,7 @@ try {
|
|
|
26
26
|
// Registry not available, will use hardcoded models
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
const WORKFLOW_DIR = path.join(PROJECT_ROOT, '.workflow');
|
|
31
|
-
const CONFIG_PATH = path.join(WORKFLOW_DIR, 'config.json');
|
|
29
|
+
const CONFIG_PATH = path.join(PATHS.workflow, 'config.json');
|
|
32
30
|
|
|
33
31
|
const symbols = {
|
|
34
32
|
success: '✅',
|
|
@@ -96,12 +94,9 @@ async function prompt(question) {
|
|
|
96
94
|
output: process.stdout
|
|
97
95
|
});
|
|
98
96
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
resolve(answer.trim());
|
|
103
|
-
});
|
|
104
|
-
});
|
|
97
|
+
const answer = await rl.question(question);
|
|
98
|
+
rl.close();
|
|
99
|
+
return answer.trim();
|
|
105
100
|
}
|
|
106
101
|
|
|
107
102
|
class Spinner {
|
|
@@ -700,7 +695,7 @@ ${colors.cyan}╔═════════════════════
|
|
|
700
695
|
`);
|
|
701
696
|
|
|
702
697
|
// Check if workflow dir exists
|
|
703
|
-
if (!fs.existsSync(
|
|
698
|
+
if (!fs.existsSync(PATHS.workflow)) {
|
|
704
699
|
console.log(`${colors.red}${symbols.error} Wogi Flow not installed in this project.${colors.reset}`);
|
|
705
700
|
console.log(`Run /wogi-onboard first.`);
|
|
706
701
|
process.exit(1);
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
const fs = require('node:fs');
|
|
10
10
|
const path = require('node:path');
|
|
11
11
|
const { spawnSync } = require('node:child_process');
|
|
12
|
-
const { getProjectRoot, getConfig, error, success } = require('./flow-utils');
|
|
12
|
+
const { getProjectRoot, getConfig, error, success, PATHS } = require('./flow-utils');
|
|
13
13
|
|
|
14
|
-
const PROJECT_ROOT = getProjectRoot();
|
|
15
14
|
const TESTS = [];
|
|
16
15
|
let passed = 0;
|
|
17
16
|
let failed = 0;
|
|
@@ -45,7 +44,7 @@ async function run() {
|
|
|
45
44
|
// Tests
|
|
46
45
|
|
|
47
46
|
test('Config file exists', () => {
|
|
48
|
-
const configPath = path.join(
|
|
47
|
+
const configPath = path.join(PATHS.workflow, 'config.json');
|
|
49
48
|
if (!fs.existsSync(configPath)) {
|
|
50
49
|
throw new Error('config.json not found');
|
|
51
50
|
}
|
|
@@ -59,7 +58,7 @@ test('Config has hybrid section', () => {
|
|
|
59
58
|
});
|
|
60
59
|
|
|
61
60
|
test('Detection script exists and runs', () => {
|
|
62
|
-
const scriptPath = path.join(
|
|
61
|
+
const scriptPath = path.join(PATHS.root, 'scripts', 'flow-hybrid-detect.js');
|
|
63
62
|
if (!fs.existsSync(scriptPath)) {
|
|
64
63
|
throw new Error('flow-hybrid-detect.js not found');
|
|
65
64
|
}
|
|
@@ -77,56 +76,56 @@ test('Detection script exists and runs', () => {
|
|
|
77
76
|
});
|
|
78
77
|
|
|
79
78
|
test('Orchestrator script exists', () => {
|
|
80
|
-
const scriptPath = path.join(
|
|
79
|
+
const scriptPath = path.join(PATHS.root, 'scripts', 'flow-orchestrate.js');
|
|
81
80
|
if (!fs.existsSync(scriptPath)) {
|
|
82
81
|
throw new Error('flow-orchestrate.js not found');
|
|
83
82
|
}
|
|
84
83
|
});
|
|
85
84
|
|
|
86
85
|
test('Templates directory exists', () => {
|
|
87
|
-
const templatesDir = path.join(
|
|
86
|
+
const templatesDir = path.join(PATHS.root, 'templates', 'hybrid');
|
|
88
87
|
if (!fs.existsSync(templatesDir)) {
|
|
89
88
|
throw new Error('templates/hybrid directory not found');
|
|
90
89
|
}
|
|
91
90
|
});
|
|
92
91
|
|
|
93
92
|
test('Base template exists', () => {
|
|
94
|
-
const basePath = path.join(
|
|
93
|
+
const basePath = path.join(PATHS.root, 'templates', 'hybrid', '_base.md');
|
|
95
94
|
if (!fs.existsSync(basePath)) {
|
|
96
95
|
throw new Error('_base.md template not found');
|
|
97
96
|
}
|
|
98
97
|
});
|
|
99
98
|
|
|
100
99
|
test('Component template exists', () => {
|
|
101
|
-
const templatePath = path.join(
|
|
100
|
+
const templatePath = path.join(PATHS.root, 'templates', 'hybrid', 'create-component.md');
|
|
102
101
|
if (!fs.existsSync(templatePath)) {
|
|
103
102
|
throw new Error('create-component.md template not found');
|
|
104
103
|
}
|
|
105
104
|
});
|
|
106
105
|
|
|
107
106
|
test('State directory exists', () => {
|
|
108
|
-
const stateDir = path.join(
|
|
107
|
+
const stateDir = path.join(PATHS.workflow, 'state');
|
|
109
108
|
if (!fs.existsSync(stateDir)) {
|
|
110
109
|
throw new Error('.workflow/state directory not found');
|
|
111
110
|
}
|
|
112
111
|
});
|
|
113
112
|
|
|
114
113
|
test('Progress module exists', () => {
|
|
115
|
-
const scriptPath = path.join(
|
|
114
|
+
const scriptPath = path.join(PATHS.root, 'scripts', 'flow-progress.js');
|
|
116
115
|
if (!fs.existsSync(scriptPath)) {
|
|
117
116
|
throw new Error('flow-progress.js not found');
|
|
118
117
|
}
|
|
119
118
|
});
|
|
120
119
|
|
|
121
120
|
test('Templates module exists', () => {
|
|
122
|
-
const scriptPath = path.join(
|
|
121
|
+
const scriptPath = path.join(PATHS.root, 'scripts', 'flow-templates.js');
|
|
123
122
|
if (!fs.existsSync(scriptPath)) {
|
|
124
123
|
throw new Error('flow-templates.js not found');
|
|
125
124
|
}
|
|
126
125
|
});
|
|
127
126
|
|
|
128
127
|
test('Interactive setup exists', () => {
|
|
129
|
-
const scriptPath = path.join(
|
|
128
|
+
const scriptPath = path.join(PATHS.root, 'scripts', 'flow-hybrid-interactive.js');
|
|
130
129
|
if (!fs.existsSync(scriptPath)) {
|
|
131
130
|
throw new Error('flow-hybrid-interactive.js not found');
|
|
132
131
|
}
|
|
@@ -140,7 +139,7 @@ test('Slash command files exist', () => {
|
|
|
140
139
|
];
|
|
141
140
|
|
|
142
141
|
for (const cmd of commands) {
|
|
143
|
-
const cmdPath = path.join(
|
|
142
|
+
const cmdPath = path.join(PATHS.root, '.claude', 'commands', cmd);
|
|
144
143
|
if (!fs.existsSync(cmdPath)) {
|
|
145
144
|
throw new Error(`${cmd} not found`);
|
|
146
145
|
}
|
|
@@ -427,7 +427,7 @@ function loadRelevantTypes(projectRoot, filePath, options = {}) {
|
|
|
427
427
|
* @returns {Promise<string|null>} Formatted type information
|
|
428
428
|
*/
|
|
429
429
|
async function loadRelevantTypesWithLSP(projectRoot, filePath, options = {}) {
|
|
430
|
-
const { getConfig } = require('./flow-utils');
|
|
430
|
+
const { getConfig, PATHS } = require('./flow-utils');
|
|
431
431
|
const config = getConfig();
|
|
432
432
|
|
|
433
433
|
// Check if LSP is enabled
|