wogiflow 2.20.1 → 2.22.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-finalize.md +83 -0
- package/.claude/rules/_internal/self-maintenance.md +1 -1
- package/.claude/settings.json +1 -1
- package/lib/commands/login.js +1 -1
- package/lib/installer.js +5 -5
- package/lib/release-channel.js +1 -1
- package/lib/skill-registry.js +3 -3
- package/lib/workspace-events.js +1 -1
- package/lib/workspace-gates.js +2 -2
- package/lib/workspace-intelligence.js +1 -1
- package/lib/workspace-routing.js +1 -1
- package/lib/workspace.js +16 -17
- package/package.json +2 -2
- package/scripts/base-workflow-step.js +2 -2
- package/scripts/flow-adaptive-learning.js +6 -6
- package/scripts/flow-api-index.js +2 -2
- package/scripts/flow-architect-pass.js +1 -1
- package/scripts/flow-ask.js +1 -1
- package/scripts/flow-assumption-detector.js +1 -1
- package/scripts/flow-audit-gates.js +38 -12
- package/scripts/flow-audit.js +4 -4
- package/scripts/flow-auto-context.js +3 -3
- package/scripts/flow-background.js +1 -1
- package/scripts/flow-best-of-n.js +7 -7
- package/scripts/flow-bridge.js +3 -3
- package/scripts/flow-bug.js +2 -2
- package/scripts/flow-bulk-loop.js +7 -7
- package/scripts/flow-cascade-completion.js +2 -2
- package/scripts/flow-cascade.js +1 -1
- package/scripts/flow-checkpoint.js +2 -2
- package/scripts/flow-clarifying-questions.js +2 -2
- package/scripts/flow-cli.js +2 -2
- package/scripts/flow-code-intelligence.js +4 -4
- package/scripts/flow-community-sync.js +6 -6
- package/scripts/flow-community.js +1 -1
- package/scripts/flow-completion-truth-gate.js +161 -5
- package/scripts/flow-complexity.js +1 -1
- package/scripts/flow-config-defaults.js +16 -4
- package/scripts/flow-config-interactive.js +2 -2
- package/scripts/flow-config-loader.js +1 -1
- package/scripts/flow-config-migrate.js +5 -6
- package/scripts/flow-consistency-check.js +5 -5
- package/scripts/flow-context-compact/expander.js +1 -1
- package/scripts/flow-context-compact/index.js +2 -2
- package/scripts/flow-context-compact/section-extractor.js +3 -3
- package/scripts/flow-context-compact/summary-tree.js +1 -1
- package/scripts/flow-context-estimator.js +1 -1
- package/scripts/flow-context-gatherer.js +6 -6
- package/scripts/flow-context-generator.js +6 -6
- package/scripts/flow-context-init.js +2 -2
- package/scripts/flow-context-manager.js +1 -1
- package/scripts/flow-context-manifest.js +1 -1
- package/scripts/flow-context-monitor.js +5 -5
- package/scripts/flow-context-orchestrator.js +2 -2
- package/scripts/flow-context-scoring.js +4 -4
- package/scripts/flow-contract-scan.js +1 -1
- package/scripts/flow-correct.js +3 -3
- package/scripts/flow-damage-control.js +2 -2
- package/scripts/flow-deploy-gate.js +2 -2
- package/scripts/flow-deploy-history.js +1 -1
- package/scripts/flow-diff.js +3 -3
- package/scripts/flow-done-gates.js +1 -1
- package/scripts/flow-done.js +7 -7
- package/scripts/flow-durable-session.js +1 -1
- package/scripts/flow-entropy-monitor.js +3 -3
- package/scripts/flow-epics.js +5 -5
- package/scripts/flow-error-recovery.js +4 -4
- package/scripts/flow-eval-judge.js +5 -5
- package/scripts/flow-eval.js +7 -7
- package/scripts/flow-export-scanner.js +5 -5
- package/scripts/flow-extraction-review.js +1 -1
- package/scripts/flow-failure-learning.js +9 -9
- package/scripts/flow-feature.js +5 -5
- package/scripts/flow-figma-confirm.js +1 -1
- package/scripts/flow-figma-extract.js +2 -2
- package/scripts/flow-figma-index.js +2 -2
- package/scripts/flow-figma-match.js +1 -1
- package/scripts/flow-figma-mcp-server.js +3 -3
- package/scripts/flow-figma-orchestrator.js +1 -1
- package/scripts/flow-figma-registry.js +2 -2
- package/scripts/flow-function-index.js +2 -2
- package/scripts/flow-gate-confidence.js +2 -2
- package/scripts/flow-gate-telemetry.js +1 -1
- package/scripts/flow-gitignore.js +1 -1
- package/scripts/flow-guided-edit.js +3 -3
- package/scripts/flow-health.js +95 -8
- package/scripts/flow-hooks.js +3 -3
- package/scripts/flow-hybrid-detect.js +2 -2
- package/scripts/flow-hybrid-interactive.js +1 -1
- package/scripts/flow-hybrid-test.js +1 -1
- package/scripts/flow-hypothesis-generator.js +4 -4
- package/scripts/flow-instruction-richness.js +11 -11
- package/scripts/flow-intent-bootstrap.js +1 -1
- package/scripts/flow-intent-framing.js +1 -1
- package/scripts/flow-item-link.js +2 -2
- package/scripts/flow-knowledge-router.js +7 -7
- package/scripts/flow-knowledge-sync.js +3 -3
- package/scripts/flow-learning-orchestrator.js +1 -1
- package/scripts/flow-links.js +2 -2
- package/scripts/flow-log-manager.js +2 -2
- package/scripts/flow-logic-adversary.js +5 -4
- package/scripts/flow-long-input-chunking.js +1 -1
- package/scripts/flow-long-input-cli.js +3 -3
- package/scripts/flow-long-input.js +18 -18
- package/scripts/flow-loop-retry-learning.js +2 -2
- package/scripts/flow-lsp.js +4 -4
- package/scripts/flow-mcp-docs.js +1 -1
- package/scripts/flow-memory-blocks.js +5 -5
- package/scripts/flow-memory-compactor.js +3 -3
- package/scripts/flow-memory-db.js +4 -4
- package/scripts/flow-memory-sync.js +3 -3
- package/scripts/flow-metrics.js +2 -2
- package/scripts/flow-migrate-igr.js +2 -2
- package/scripts/flow-migrate.js +2 -2
- package/scripts/flow-model-adapter.js +4 -4
- package/scripts/flow-model-caller.js +8 -8
- package/scripts/flow-model-config.js +5 -5
- package/scripts/flow-model-profile.js +7 -7
- package/scripts/flow-model-router.js +5 -5
- package/scripts/flow-model-types.js +3 -3
- package/scripts/flow-models.js +8 -8
- package/scripts/flow-morning.js +1 -1
- package/scripts/flow-multi-approach.js +1 -1
- package/scripts/flow-orchestrate-context.js +2 -2
- package/scripts/flow-orchestrate-llm.js +4 -4
- package/scripts/flow-orchestrate-rollback.js +1 -1
- package/scripts/flow-orchestrate-state.js +6 -6
- package/scripts/flow-orchestrate-templates.js +1 -1
- package/scripts/flow-orchestrate-validation.js +2 -2
- package/scripts/flow-orchestrate-validator.js +1 -1
- package/scripts/flow-orchestrate.js +25 -25
- package/scripts/flow-parallel.js +1 -1
- package/scripts/flow-pattern-enforcer.js +7 -7
- package/scripts/flow-pattern-extractor.js +3 -3
- package/scripts/flow-peer-review.js +8 -8
- package/scripts/flow-pending.js +1 -1
- package/scripts/flow-permissions.js +1 -1
- package/scripts/flow-phased-task.js +1 -1
- package/scripts/flow-plan.js +1 -1
- package/scripts/flow-prd-manager.js +2 -2
- package/scripts/flow-product-scanner.js +2 -2
- package/scripts/flow-progress-tracker.js +2 -2
- package/scripts/flow-progress.js +1 -1
- package/scripts/flow-project-analyzer.js +3 -3
- package/scripts/flow-prompt-capture.js +2 -2
- package/scripts/flow-prompt-composer.js +3 -3
- package/scripts/flow-prompt-template.js +4 -4
- package/scripts/flow-providers.js +31 -23
- package/scripts/flow-queue.js +1 -1
- package/scripts/flow-registry-manager.js +4 -4
- package/scripts/flow-regression.js +1 -1
- package/scripts/flow-response-parser.js +1 -1
- package/scripts/flow-resume.js +1 -1
- package/scripts/flow-review-passes/index.js +2 -2
- package/scripts/flow-review-passes/integration.js +3 -3
- package/scripts/flow-review-passes/logic.js +3 -3
- package/scripts/flow-review-passes/security.js +2 -2
- package/scripts/flow-review-passes/structure.js +1 -1
- package/scripts/flow-review.js +11 -11
- package/scripts/flow-revision-tracker.js +2 -2
- package/scripts/flow-roadmap.js +2 -2
- package/scripts/flow-run-trace.js +1 -1
- package/scripts/flow-safety.js +3 -3
- package/scripts/flow-scanner-base.js +1 -1
- package/scripts/flow-scenario-engine.js +7 -7
- package/scripts/flow-schema-drift.js +4 -3
- package/scripts/flow-section-index.js +2 -2
- package/scripts/flow-section-resolver.js +4 -4
- package/scripts/flow-semantic-match.js +3 -3
- package/scripts/flow-session-end.js +56 -0
- package/scripts/flow-session-learning.js +2 -2
- package/scripts/flow-setup-hooks.js +1 -1
- package/scripts/flow-skill-create.js +3 -3
- package/scripts/flow-skill-freshness.js +2 -2
- package/scripts/flow-skill-generator.js +6 -6
- package/scripts/flow-skill-learn.js +7 -7
- package/scripts/flow-skill-matcher.js +2 -2
- package/scripts/flow-solution-optimizer.js +1 -1
- package/scripts/flow-spec-generator.js +5 -5
- package/scripts/flow-spec-verifier.js +2 -2
- package/scripts/flow-stack-wizard.js +6 -6
- package/scripts/flow-standards-checker.js +8 -8
- package/scripts/flow-standards-gate.js +4 -4
- package/scripts/flow-standards-learner.js +2 -2
- package/scripts/flow-start.js +9 -9
- package/scripts/flow-stats-collector.js +2 -2
- package/scripts/flow-status.js +1 -1
- package/scripts/flow-step-changelog.js +3 -3
- package/scripts/flow-step-complexity.js +1 -1
- package/scripts/flow-step-coverage.js +3 -3
- package/scripts/flow-step-knowledge.js +2 -2
- package/scripts/flow-step-pr-tests.js +2 -2
- package/scripts/flow-step-regression.js +3 -3
- package/scripts/flow-step-review.js +5 -5
- package/scripts/flow-story.js +2 -2
- package/scripts/flow-strict-adherence.js +2 -2
- package/scripts/flow-structure-sensor.js +283 -0
- package/scripts/flow-sync-anonymizer.js +3 -3
- package/scripts/flow-task-checkpoint.js +2 -2
- package/scripts/flow-task-classifier.js +2 -2
- package/scripts/flow-task-completion-summary.js +1 -1
- package/scripts/flow-task-enforcer.js +5 -5
- package/scripts/flow-tech-debt.js +3 -3
- package/scripts/flow-template-extractor.js +3 -3
- package/scripts/flow-templates.js +1 -1
- package/scripts/flow-test-api.js +12 -12
- package/scripts/flow-test-discovery.js +9 -9
- package/scripts/flow-test-generate.js +5 -5
- package/scripts/flow-test-integrity.js +3 -3
- package/scripts/flow-test-ui.js +8 -8
- package/scripts/flow-testing-deps.js +4 -4
- package/scripts/flow-tiered-learning.js +3 -3
- package/scripts/flow-todowrite-sync.js +1 -1
- package/scripts/flow-trap-zone.js +1 -1
- package/scripts/flow-verification-profile.js +9 -9
- package/scripts/flow-verify.js +2 -2
- package/scripts/flow-version-check.js +2 -2
- package/scripts/flow-webmcp-generator.js +3 -3
- package/scripts/flow-wiring-verifier.js +13 -13
- package/scripts/flow-worker-question-classifier.js +256 -0
- package/scripts/flow-workflow-steps.js +3 -3
- package/scripts/flow-workflow.js +1 -1
- package/scripts/flow-worktree.js +1 -1
- package/scripts/hooks/adapters/base-adapter.js +2 -2
- package/scripts/hooks/core/commit-log-gate.js +2 -2
- package/scripts/hooks/core/component-check.js +3 -3
- package/scripts/hooks/core/config-change.js +1 -1
- package/scripts/hooks/core/deploy-gate.js +2 -1
- package/scripts/hooks/core/git-safety-gate.js +1 -1
- package/scripts/hooks/core/instructions-loaded.js +1 -1
- package/scripts/hooks/core/loop-check.js +1 -1
- package/scripts/hooks/core/manager-boundary-gate.js +3 -2
- package/scripts/hooks/core/observation-capture.js +6 -6
- package/scripts/hooks/core/phase-gate.js +4 -4
- package/scripts/hooks/core/pre-compact.js +1 -1
- package/scripts/hooks/core/pre-tool-orchestrator.js +1 -1
- package/scripts/hooks/core/routing-gate.js +2 -84
- package/scripts/hooks/core/session-context.js +1 -1
- package/scripts/hooks/core/session-end.js +3 -3
- package/scripts/hooks/core/session-history.js +1 -1
- package/scripts/hooks/core/setup-handler.js +1 -1
- package/scripts/hooks/core/task-boundary-reset.js +2 -4
- package/scripts/hooks/core/task-completed.js +13 -7
- package/scripts/hooks/core/task-created.js +1 -1
- package/scripts/hooks/core/worktree-lifecycle.js +1 -1
- package/scripts/hooks/entry/claude-code/permission-denied.js +4 -2
- package/scripts/hooks/entry/claude-code/stop.js +60 -0
- package/scripts/hooks/entry/claude-code/user-prompt-submit.js +1 -1
- package/scripts/hooks/git/post-commit.js +1 -1
- package/scripts/postinstall.js +7 -7
- package/scripts/preuninstall.js +5 -5
- package/scripts/registries/component-registry.js +2 -2
- package/scripts/registries/contract-scanner.js +11 -11
- package/scripts/registries/schema-registry.js +5 -5
- package/scripts/registries/service-registry.js +9 -9
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
const { execSync } = require('node:child_process');
|
|
11
11
|
const path = require('node:path');
|
|
12
|
-
const {
|
|
12
|
+
const { PATHS } = require('./flow-utils');
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Run regression tests as a workflow step
|
|
@@ -37,7 +37,7 @@ async function run(options = {}) {
|
|
|
37
37
|
let parsed;
|
|
38
38
|
try {
|
|
39
39
|
parsed = JSON.parse(result);
|
|
40
|
-
} catch (
|
|
40
|
+
} catch (_err) {
|
|
41
41
|
// Non-JSON output means success with no issues
|
|
42
42
|
return { passed: true, message: 'Regression tests passed' };
|
|
43
43
|
}
|
|
@@ -67,7 +67,7 @@ async function run(options = {}) {
|
|
|
67
67
|
details: parsed.failures,
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
} catch (
|
|
70
|
+
} catch (_err) {
|
|
71
71
|
// Not JSON, treat as error
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -142,7 +142,7 @@ function printIssue(issue) {
|
|
|
142
142
|
/**
|
|
143
143
|
* Run multi-agent review (3 perspectives)
|
|
144
144
|
*/
|
|
145
|
-
async function runMultiAgentReview(files,
|
|
145
|
+
async function runMultiAgentReview(files, _config) {
|
|
146
146
|
const allIssues = [];
|
|
147
147
|
|
|
148
148
|
for (const file of files) {
|
|
@@ -165,7 +165,7 @@ async function runMultiAgentReview(files, config) {
|
|
|
165
165
|
]);
|
|
166
166
|
|
|
167
167
|
allIssues.push(...fileIssues);
|
|
168
|
-
} catch (
|
|
168
|
+
} catch (_err) {
|
|
169
169
|
// Skip unreadable files
|
|
170
170
|
}
|
|
171
171
|
}
|
|
@@ -176,7 +176,7 @@ async function runMultiAgentReview(files, config) {
|
|
|
176
176
|
/**
|
|
177
177
|
* Run simple review (single pass)
|
|
178
178
|
*/
|
|
179
|
-
async function runSimpleReview(files,
|
|
179
|
+
async function runSimpleReview(files, _config) {
|
|
180
180
|
const allIssues = [];
|
|
181
181
|
|
|
182
182
|
for (const file of files) {
|
|
@@ -187,7 +187,7 @@ async function runSimpleReview(files, config) {
|
|
|
187
187
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
188
188
|
const fileIssues = runBasicChecks(content, file);
|
|
189
189
|
allIssues.push(...fileIssues);
|
|
190
|
-
} catch (
|
|
190
|
+
} catch (_err) {
|
|
191
191
|
// Skip unreadable files
|
|
192
192
|
}
|
|
193
193
|
}
|
|
@@ -200,7 +200,7 @@ async function runSimpleReview(files, config) {
|
|
|
200
200
|
*/
|
|
201
201
|
function reviewArchitecture(content, fileName) {
|
|
202
202
|
const issues = [];
|
|
203
|
-
const
|
|
203
|
+
const _lines = content.split('\n');
|
|
204
204
|
|
|
205
205
|
// Check for god objects (too many methods/properties)
|
|
206
206
|
const methodCount = (content.match(/(?:function\s+\w+|(?:const|let|var)\s+\w+\s*=\s*(?:async\s*)?\()/g) || []).length;
|
package/scripts/flow-story.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
const fs = require('node:fs');
|
|
16
16
|
const path = require('node:path');
|
|
17
17
|
const {
|
|
18
|
-
getProjectRoot,
|
|
18
|
+
getProjectRoot: _getProjectRoot,
|
|
19
19
|
colors,
|
|
20
20
|
getConfig,
|
|
21
21
|
getConfigValue,
|
|
@@ -26,7 +26,7 @@ const {
|
|
|
26
26
|
safeJsonParse,
|
|
27
27
|
isPathWithinProject, PATHS
|
|
28
28
|
} = require('./flow-utils');
|
|
29
|
-
const { success, warn, error, info
|
|
29
|
+
const { success, warn, error, info } = require('./flow-output');
|
|
30
30
|
|
|
31
31
|
// Import context orchestrator for product context
|
|
32
32
|
let contextOrchestrator = null;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
const fs = require('node:fs');
|
|
21
21
|
const path = require('node:path');
|
|
22
22
|
const {
|
|
23
|
-
getProjectRoot,
|
|
23
|
+
getProjectRoot: _getProjectRoot,
|
|
24
24
|
safeJsonParse,
|
|
25
25
|
getConfig,
|
|
26
26
|
colors,
|
|
@@ -500,7 +500,7 @@ function reviewCode(options = {}) {
|
|
|
500
500
|
|
|
501
501
|
if (!isEnabled()) return { warnings };
|
|
502
502
|
|
|
503
|
-
const
|
|
503
|
+
const _standards = loadStandards();
|
|
504
504
|
const config = loadConfig();
|
|
505
505
|
|
|
506
506
|
// Only run in user review mode
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow — Structure-Change Sensor
|
|
5
|
+
*
|
|
6
|
+
* Detects folder-restructure patterns in a git diff range so /wogi-finalize
|
|
7
|
+
* and cross-repo audit workflows can flag merges that will produce
|
|
8
|
+
* modify/delete conflicts rather than routine content conflicts.
|
|
9
|
+
*
|
|
10
|
+
* Patterns detected (all evaluated against `git diff --name-status <range>`):
|
|
11
|
+
*
|
|
12
|
+
* 1. FOLDER_PER_COMPONENT — `X.tsx` deleted + `X/X.tsx` added
|
|
13
|
+
* 2. SPLIT_INTO_SUBMODULE — `X.ts` deleted + one or more `<dir>/X-*.ts`
|
|
14
|
+
* or `<dir>/<other>.ts` added at deeper depth
|
|
15
|
+
* 3. BARREL_INTRODUCTION — `X.ts` deleted + `X/index.ts` added
|
|
16
|
+
* 4. RENAME_NEW_HOME — single file deletion + single file addition,
|
|
17
|
+
* same basename, different directory
|
|
18
|
+
*
|
|
19
|
+
* The sensor is intentionally pattern-based and bounded: no full-diff parsing,
|
|
20
|
+
* no AST walks. Downstream callers (wogi-finalize merge-plan gate) use the
|
|
21
|
+
* output to drive a percentage threshold — when restructure-matched files
|
|
22
|
+
* exceed `restructureThreshold` (default 20%) of the changed set, a structural
|
|
23
|
+
* warning is surfaced.
|
|
24
|
+
*
|
|
25
|
+
* Usage (CLI):
|
|
26
|
+
* flow-structure-sensor.js <base>..<branch>
|
|
27
|
+
* → emits one JSON object on stdout
|
|
28
|
+
*
|
|
29
|
+
* Usage (module):
|
|
30
|
+
* const { detectStructureChanges } = require('./flow-structure-sensor');
|
|
31
|
+
* const result = detectStructureChanges({ base: 'master', branch: 'feature/x' });
|
|
32
|
+
*
|
|
33
|
+
* Exit codes:
|
|
34
|
+
* 0 — success (patterns may or may not be present; see result.warn)
|
|
35
|
+
* 1 — git invocation failed or invalid range
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
'use strict';
|
|
39
|
+
|
|
40
|
+
const { execFileSync } = require('node:child_process');
|
|
41
|
+
const path = require('node:path');
|
|
42
|
+
|
|
43
|
+
const DEFAULT_RESTRUCTURE_THRESHOLD = 0.20;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Run `git diff --name-status` and return the parsed entries.
|
|
47
|
+
* Each entry is { status: 'A'|'D'|'M'|'R100'|..., path: string, origPath?: string }.
|
|
48
|
+
*/
|
|
49
|
+
function getDiffEntries({ range, cwd }) {
|
|
50
|
+
const args = ['diff', '--name-status', range];
|
|
51
|
+
let output;
|
|
52
|
+
try {
|
|
53
|
+
output = execFileSync('git', args, {
|
|
54
|
+
cwd: cwd || process.cwd(),
|
|
55
|
+
encoding: 'utf-8',
|
|
56
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
57
|
+
});
|
|
58
|
+
} catch (err) {
|
|
59
|
+
throw new Error(`git diff failed: ${err.message || err}`);
|
|
60
|
+
}
|
|
61
|
+
const entries = [];
|
|
62
|
+
for (const line of output.split('\n')) {
|
|
63
|
+
if (!line.trim()) continue;
|
|
64
|
+
const parts = line.split('\t');
|
|
65
|
+
const status = parts[0];
|
|
66
|
+
if (!status) continue;
|
|
67
|
+
// R/C statuses carry two paths: origPath, newPath
|
|
68
|
+
if (/^R/.test(status) || /^C/.test(status)) {
|
|
69
|
+
if (parts.length >= 3) {
|
|
70
|
+
entries.push({ status, origPath: parts[1], path: parts[2] });
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
entries.push({ status, path: parts[1] });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return entries;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Analyze entries for restructure patterns.
|
|
81
|
+
* Returns:
|
|
82
|
+
* {
|
|
83
|
+
* range, totalChanged, restructureCount, ratio, warn,
|
|
84
|
+
* patterns: [{ type, deleted, added, basename }]
|
|
85
|
+
* }
|
|
86
|
+
*/
|
|
87
|
+
function classify(entries) {
|
|
88
|
+
const deletions = entries.filter((e) => e.status === 'D').map((e) => e.path);
|
|
89
|
+
const additions = entries.filter((e) => e.status === 'A').map((e) => e.path);
|
|
90
|
+
const renames = entries.filter((e) => /^R/.test(e.status));
|
|
91
|
+
|
|
92
|
+
const patterns = [];
|
|
93
|
+
const restructureFiles = new Set();
|
|
94
|
+
|
|
95
|
+
const deletedByBasename = new Map();
|
|
96
|
+
for (const p of deletions) {
|
|
97
|
+
const base = path.basename(p, path.extname(p));
|
|
98
|
+
if (!deletedByBasename.has(base)) deletedByBasename.set(base, []);
|
|
99
|
+
deletedByBasename.get(base).push(p);
|
|
100
|
+
}
|
|
101
|
+
const addedByBasename = new Map();
|
|
102
|
+
for (const p of additions) {
|
|
103
|
+
const base = path.basename(p, path.extname(p));
|
|
104
|
+
if (!addedByBasename.has(base)) addedByBasename.set(base, []);
|
|
105
|
+
addedByBasename.get(base).push(p);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 1. FOLDER_PER_COMPONENT: X.tsx deleted AND X/X.tsx (same basename/ext) added
|
|
109
|
+
for (const [base, dels] of deletedByBasename) {
|
|
110
|
+
const adds = addedByBasename.get(base) || [];
|
|
111
|
+
for (const del of dels) {
|
|
112
|
+
for (const add of adds) {
|
|
113
|
+
const delDir = path.dirname(del);
|
|
114
|
+
const addDir = path.dirname(add);
|
|
115
|
+
const delExt = path.extname(del);
|
|
116
|
+
const addExt = path.extname(add);
|
|
117
|
+
if (delExt !== addExt) continue;
|
|
118
|
+
// Folder-per-component: addDir = delDir + '/' + base
|
|
119
|
+
if (addDir === path.join(delDir, base)) {
|
|
120
|
+
patterns.push({ type: 'FOLDER_PER_COMPONENT', deleted: del, added: add, basename: base });
|
|
121
|
+
restructureFiles.add(del);
|
|
122
|
+
restructureFiles.add(add);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 2. SPLIT_INTO_SUBMODULE: X.ts deleted AND 2+ files added under a dir
|
|
129
|
+
// named X/ or at deeper depth carrying X's name as dir.
|
|
130
|
+
for (const [base, dels] of deletedByBasename) {
|
|
131
|
+
for (const del of dels) {
|
|
132
|
+
const delDir = path.dirname(del);
|
|
133
|
+
const delExt = path.extname(del);
|
|
134
|
+
const submoduleDir = path.join(delDir, base);
|
|
135
|
+
const adds = additions.filter(
|
|
136
|
+
(a) =>
|
|
137
|
+
path.extname(a) === delExt &&
|
|
138
|
+
(a.startsWith(submoduleDir + path.sep) || a.startsWith(submoduleDir + '/'))
|
|
139
|
+
);
|
|
140
|
+
// Need to see this as a split only if 2+ files land there — single file
|
|
141
|
+
// was already picked up as FOLDER_PER_COMPONENT above.
|
|
142
|
+
if (adds.length >= 2) {
|
|
143
|
+
patterns.push({ type: 'SPLIT_INTO_SUBMODULE', deleted: del, added: adds, basename: base });
|
|
144
|
+
restructureFiles.add(del);
|
|
145
|
+
for (const a of adds) restructureFiles.add(a);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 3. BARREL_INTRODUCTION: X.ts deleted AND X/index.ts added (any extension match)
|
|
151
|
+
for (const [base, dels] of deletedByBasename) {
|
|
152
|
+
for (const del of dels) {
|
|
153
|
+
const delDir = path.dirname(del);
|
|
154
|
+
const delExt = path.extname(del);
|
|
155
|
+
const barrelPath = path.join(delDir, base, `index${delExt}`);
|
|
156
|
+
if (additions.includes(barrelPath)) {
|
|
157
|
+
patterns.push({ type: 'BARREL_INTRODUCTION', deleted: del, added: barrelPath, basename: base });
|
|
158
|
+
restructureFiles.add(del);
|
|
159
|
+
restructureFiles.add(barrelPath);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 4. RENAME_NEW_HOME: git-reported renames across different directories
|
|
165
|
+
for (const r of renames) {
|
|
166
|
+
if (!r.origPath || !r.path) continue;
|
|
167
|
+
if (path.dirname(r.origPath) !== path.dirname(r.path)) {
|
|
168
|
+
patterns.push({
|
|
169
|
+
type: 'RENAME_NEW_HOME',
|
|
170
|
+
deleted: r.origPath,
|
|
171
|
+
added: r.path,
|
|
172
|
+
basename: path.basename(r.path, path.extname(r.path)),
|
|
173
|
+
});
|
|
174
|
+
restructureFiles.add(r.origPath);
|
|
175
|
+
restructureFiles.add(r.path);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const totalChanged = entries.length;
|
|
180
|
+
const ratio = totalChanged > 0 ? restructureFiles.size / totalChanged : 0;
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
totalChanged,
|
|
184
|
+
restructureCount: restructureFiles.size,
|
|
185
|
+
ratio,
|
|
186
|
+
patterns,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Public API — detect structure changes for a git range.
|
|
192
|
+
*
|
|
193
|
+
* @param {Object} opts
|
|
194
|
+
* @param {string} opts.range - Git range (e.g., "master..feature/foo" or a single SHA range).
|
|
195
|
+
* If opts.base+opts.branch given, they combine as "base..branch".
|
|
196
|
+
* @param {string} [opts.base]
|
|
197
|
+
* @param {string} [opts.branch]
|
|
198
|
+
* @param {string} [opts.cwd]
|
|
199
|
+
* @param {number} [opts.threshold=0.20] - Ratio above which warn=true.
|
|
200
|
+
* @returns {Object}
|
|
201
|
+
*/
|
|
202
|
+
function detectStructureChanges(opts = {}) {
|
|
203
|
+
const range = opts.range || (opts.base && opts.branch ? `${opts.base}..${opts.branch}` : null);
|
|
204
|
+
if (!range || typeof range !== 'string') {
|
|
205
|
+
return {
|
|
206
|
+
ok: false,
|
|
207
|
+
reason: 'missing-range',
|
|
208
|
+
totalChanged: 0,
|
|
209
|
+
restructureCount: 0,
|
|
210
|
+
ratio: 0,
|
|
211
|
+
warn: false,
|
|
212
|
+
patterns: [],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const threshold = typeof opts.threshold === 'number' ? opts.threshold : DEFAULT_RESTRUCTURE_THRESHOLD;
|
|
216
|
+
|
|
217
|
+
let entries;
|
|
218
|
+
try {
|
|
219
|
+
entries = getDiffEntries({ range, cwd: opts.cwd });
|
|
220
|
+
} catch (err) {
|
|
221
|
+
return {
|
|
222
|
+
ok: false,
|
|
223
|
+
reason: `git-failed: ${err.message}`,
|
|
224
|
+
range,
|
|
225
|
+
totalChanged: 0,
|
|
226
|
+
restructureCount: 0,
|
|
227
|
+
ratio: 0,
|
|
228
|
+
warn: false,
|
|
229
|
+
patterns: [],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const classified = classify(entries);
|
|
234
|
+
const warn = classified.ratio >= threshold && classified.restructureCount >= 2;
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
ok: true,
|
|
238
|
+
range,
|
|
239
|
+
threshold,
|
|
240
|
+
totalChanged: classified.totalChanged,
|
|
241
|
+
restructureCount: classified.restructureCount,
|
|
242
|
+
ratio: Number(classified.ratio.toFixed(4)),
|
|
243
|
+
warn,
|
|
244
|
+
patterns: classified.patterns,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ============================================================
|
|
249
|
+
// CLI
|
|
250
|
+
// ============================================================
|
|
251
|
+
|
|
252
|
+
function main() {
|
|
253
|
+
const argv = process.argv.slice(2);
|
|
254
|
+
if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h') {
|
|
255
|
+
console.log('Usage: flow-structure-sensor.js <range> [--threshold=N]');
|
|
256
|
+
console.log(' range — git diff range, e.g. "master..feature/x" or "<sha1>..<sha2>"');
|
|
257
|
+
console.log(' --threshold=N float 0–1, default 0.20');
|
|
258
|
+
process.exit(argv.length === 0 ? 1 : 0);
|
|
259
|
+
}
|
|
260
|
+
const range = argv[0];
|
|
261
|
+
let threshold = DEFAULT_RESTRUCTURE_THRESHOLD;
|
|
262
|
+
for (const a of argv.slice(1)) {
|
|
263
|
+
const m = a.match(/^--threshold=(.+)$/);
|
|
264
|
+
if (m) {
|
|
265
|
+
const n = Number(m[1]);
|
|
266
|
+
if (Number.isFinite(n)) threshold = n;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const result = detectStructureChanges({ range, threshold });
|
|
270
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
271
|
+
process.exit(result.ok ? 0 : 1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (require.main === module) {
|
|
275
|
+
main();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
module.exports = {
|
|
279
|
+
detectStructureChanges,
|
|
280
|
+
classify,
|
|
281
|
+
getDiffEntries,
|
|
282
|
+
DEFAULT_RESTRUCTURE_THRESHOLD,
|
|
283
|
+
};
|
|
@@ -103,14 +103,14 @@ function anonymizeBatch(records) {
|
|
|
103
103
|
*/
|
|
104
104
|
function createUploadPayload(params) {
|
|
105
105
|
const { records, capabilityScores, routingEffectiveness } = params;
|
|
106
|
-
const
|
|
106
|
+
const _config = getConfig();
|
|
107
107
|
|
|
108
108
|
// Get WogiFlow version from package.json
|
|
109
109
|
let version = 'unknown';
|
|
110
110
|
try {
|
|
111
111
|
const pkg = require(path.join(PATHS.root, 'package.json'));
|
|
112
112
|
version = pkg.version || 'unknown';
|
|
113
|
-
} catch (
|
|
113
|
+
} catch (_err) {
|
|
114
114
|
// Non-critical
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -209,7 +209,7 @@ function main() {
|
|
|
209
209
|
const sample = [
|
|
210
210
|
{
|
|
211
211
|
taskId: 'wf-abc123',
|
|
212
|
-
model: 'claude-opus-4-
|
|
212
|
+
model: 'claude-opus-4-7',
|
|
213
213
|
taskType: 'feature',
|
|
214
214
|
iterations: 2,
|
|
215
215
|
firstAttemptPass: true,
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
* Part of S1: Smart Context Compaction
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const _fs = require('node:fs');
|
|
18
18
|
const path = require('node:path');
|
|
19
19
|
const {
|
|
20
|
-
getConfig,
|
|
20
|
+
getConfig: _getConfig,
|
|
21
21
|
STATE_DIR,
|
|
22
22
|
readJson,
|
|
23
23
|
writeJson,
|
|
@@ -25,7 +25,7 @@ const {
|
|
|
25
25
|
PROJECT_ROOT,
|
|
26
26
|
readFile,
|
|
27
27
|
fileExists,
|
|
28
|
-
info,
|
|
28
|
+
info: _info,
|
|
29
29
|
warn,
|
|
30
30
|
parseFlags,
|
|
31
31
|
outputJson
|
|
@@ -113,7 +113,7 @@ const TASK_TYPE_PATTERNS = {
|
|
|
113
113
|
* @param {Object} options - Classification options
|
|
114
114
|
* @returns {Object} - Classification result with type and confidence
|
|
115
115
|
*/
|
|
116
|
-
function classifyTask(taskDescription, affectedFiles = [],
|
|
116
|
+
function classifyTask(taskDescription, affectedFiles = [], _options = {}) {
|
|
117
117
|
const descLower = taskDescription.toLowerCase();
|
|
118
118
|
const scores = {};
|
|
119
119
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const path = require('node:path');
|
|
13
13
|
const fs = require('node:fs');
|
|
14
14
|
const { execFileSync } = require('node:child_process');
|
|
15
|
-
const { getConfig, PATHS, safeJsonParse,
|
|
15
|
+
const { getConfig, PATHS, safeJsonParse, writeFile, ensureDir, isPathWithinProject, validateTaskId } = require('./flow-utils');
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Check if completion summaries are enabled in config
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
const fs = require('node:fs');
|
|
16
16
|
const path = require('node:path');
|
|
17
|
-
const { getConfig, getProjectRoot, writeJson, readJson, safeJsonParse
|
|
17
|
+
const { getConfig, getProjectRoot, writeJson, readJson, safeJsonParse } = require('./flow-utils');
|
|
18
18
|
const { getCommand } = require('./flow-script-resolver');
|
|
19
19
|
const { sanitizeShellArg } = require('./flow-security');
|
|
20
20
|
|
|
@@ -84,7 +84,7 @@ function isRecheckEnabled() {
|
|
|
84
84
|
* Returns { allowed: boolean, message: string }
|
|
85
85
|
*/
|
|
86
86
|
function canSkipCriterion(criterionId, approvalGiven = false) {
|
|
87
|
-
const
|
|
87
|
+
const _config = getConfig();
|
|
88
88
|
const session = getActiveLoop();
|
|
89
89
|
|
|
90
90
|
if (!session) {
|
|
@@ -150,7 +150,7 @@ function startLoop(taskId, acceptanceCriteria) {
|
|
|
150
150
|
|
|
151
151
|
// v2.0: Use durable session if enabled
|
|
152
152
|
if (config.durableSteps?.enabled !== false) {
|
|
153
|
-
const
|
|
153
|
+
const _session = durableSession.createDurableSession(taskId, 'task', acceptanceCriteria);
|
|
154
154
|
// Return backward-compatible format
|
|
155
155
|
return durableSession.getActiveLoop();
|
|
156
156
|
}
|
|
@@ -753,7 +753,7 @@ function getLoopStats() {
|
|
|
753
753
|
function verifyCriterion(criterion, context = {}) {
|
|
754
754
|
const { execSync, execFileSync } = require('node:child_process');
|
|
755
755
|
const { changedFiles = [], testResults = null, lintResults = null } = context;
|
|
756
|
-
const
|
|
756
|
+
const _config = getConfig();
|
|
757
757
|
const taskConfig = getTaskConfig();
|
|
758
758
|
const desc = criterion.description;
|
|
759
759
|
const descLower = desc.toLowerCase();
|
|
@@ -848,7 +848,7 @@ function verifyCriterion(criterion, context = {}) {
|
|
|
848
848
|
}
|
|
849
849
|
}
|
|
850
850
|
}
|
|
851
|
-
} catch (
|
|
851
|
+
} catch (_err) { /* ignore permission errors */ }
|
|
852
852
|
return results;
|
|
853
853
|
}
|
|
854
854
|
|
|
@@ -32,13 +32,13 @@ const {
|
|
|
32
32
|
colors: c,
|
|
33
33
|
safeJsonParse,
|
|
34
34
|
writeJson,
|
|
35
|
-
fileExists,
|
|
35
|
+
fileExists: _fileExists,
|
|
36
36
|
getProjectRoot,
|
|
37
37
|
ensureDir,
|
|
38
|
-
generateTaskId, PATHS,
|
|
38
|
+
generateTaskId, PATHS: _PATHS,
|
|
39
39
|
getTodayDate
|
|
40
40
|
} = require('./flow-utils');
|
|
41
|
-
const {
|
|
41
|
+
const { warn: printWarn } = require('./flow-output');
|
|
42
42
|
|
|
43
43
|
// ============================================================================
|
|
44
44
|
// Constants
|
|
@@ -289,7 +289,7 @@ function generateTemplate(file, type) {
|
|
|
289
289
|
const content = file.content;
|
|
290
290
|
const lines = content.split('\n');
|
|
291
291
|
const templateLines = [];
|
|
292
|
-
let
|
|
292
|
+
let _insideBody = false;
|
|
293
293
|
let braceDepth = 0;
|
|
294
294
|
let bodyStartDepth = 0;
|
|
295
295
|
let skipUntilCloseBrace = false;
|
|
@@ -379,10 +379,10 @@ function generateTemplate(file, type) {
|
|
|
379
379
|
/**
|
|
380
380
|
* Check if a line is structural (should always be kept)
|
|
381
381
|
*/
|
|
382
|
-
function isStructuralLine(trimmed,
|
|
382
|
+
function isStructuralLine(trimmed, _type) {
|
|
383
383
|
// Import statements
|
|
384
384
|
if (trimmed.startsWith('import ') || trimmed.startsWith('const ') && trimmed.includes('require(')) return true;
|
|
385
|
-
const {
|
|
385
|
+
const { } = require('./flow-utils');
|
|
386
386
|
if (trimmed.startsWith('from ')) return true;
|
|
387
387
|
|
|
388
388
|
// Export statements
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const fs = require('node:fs');
|
|
15
15
|
const path = require('node:path');
|
|
16
|
-
const {
|
|
16
|
+
const { readJson, info, PATHS } = require('./flow-utils');
|
|
17
17
|
|
|
18
18
|
const TEMPLATES_DIR = path.join(PATHS.root, 'templates', 'hybrid');
|
|
19
19
|
|
package/scripts/flow-test-api.js
CHANGED
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
const fs = require('node:fs');
|
|
20
20
|
const path = require('node:path');
|
|
21
21
|
const { spawn } = require('node:child_process');
|
|
22
|
-
const {
|
|
22
|
+
const { PATHS, ensureDir, safeJsonParse, safeJsonParseString } = require('./flow-utils');
|
|
23
23
|
const { getConfig } = require('./flow-config-loader');
|
|
24
24
|
const { loadProfile } = require('./flow-verification-profile');
|
|
25
25
|
|
|
26
26
|
let scenarioEngine;
|
|
27
27
|
try {
|
|
28
28
|
scenarioEngine = require('./flow-scenario-engine');
|
|
29
|
-
} catch (
|
|
29
|
+
} catch (_err) {
|
|
30
30
|
scenarioEngine = null;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -58,7 +58,7 @@ function parseAPIMap(apiMapPath) {
|
|
|
58
58
|
let content;
|
|
59
59
|
try {
|
|
60
60
|
content = fs.readFileSync(apiMapPath, 'utf-8');
|
|
61
|
-
} catch (
|
|
61
|
+
} catch (_err) {
|
|
62
62
|
return [];
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -137,7 +137,7 @@ function parseOpenAPISpec(specPath) {
|
|
|
137
137
|
let content;
|
|
138
138
|
try {
|
|
139
139
|
content = fs.readFileSync(specPath, 'utf-8');
|
|
140
|
-
} catch (
|
|
140
|
+
} catch (_err) {
|
|
141
141
|
return { endpoints: [], raw: null };
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -524,13 +524,13 @@ async function executeAPITest(testCase, baseUrl, options = {}) {
|
|
|
524
524
|
if (contentType.includes('application/json')) {
|
|
525
525
|
try {
|
|
526
526
|
body = await response.json();
|
|
527
|
-
} catch (
|
|
527
|
+
} catch (_err) {
|
|
528
528
|
body = null;
|
|
529
529
|
}
|
|
530
530
|
} else {
|
|
531
531
|
try {
|
|
532
532
|
body = await response.text();
|
|
533
|
-
} catch (
|
|
533
|
+
} catch (_err) {
|
|
534
534
|
body = null;
|
|
535
535
|
}
|
|
536
536
|
}
|
|
@@ -665,7 +665,7 @@ async function startAPIServer(command, baseUrl, timeout = SERVER_READY_TIMEOUT)
|
|
|
665
665
|
if (response.ok || response.status < 500) {
|
|
666
666
|
return { process: serverProcess, ready: true, error: null };
|
|
667
667
|
}
|
|
668
|
-
} catch (
|
|
668
|
+
} catch (_err) {
|
|
669
669
|
// Server not ready yet — continue polling
|
|
670
670
|
}
|
|
671
671
|
}
|
|
@@ -693,7 +693,7 @@ function stopAPIServer(serverProcess) {
|
|
|
693
693
|
if (serverProcess.pid) {
|
|
694
694
|
try {
|
|
695
695
|
process.kill(-serverProcess.pid, 'SIGTERM');
|
|
696
|
-
} catch (
|
|
696
|
+
} catch (_err) {
|
|
697
697
|
// Process group kill failed — try direct kill
|
|
698
698
|
try {
|
|
699
699
|
serverProcess.kill('SIGTERM');
|
|
@@ -708,12 +708,12 @@ function stopAPIServer(serverProcess) {
|
|
|
708
708
|
if (!serverProcess.killed) {
|
|
709
709
|
process.kill(-serverProcess.pid, 'SIGKILL');
|
|
710
710
|
}
|
|
711
|
-
} catch (
|
|
711
|
+
} catch (_err) {
|
|
712
712
|
// Already dead
|
|
713
713
|
}
|
|
714
714
|
}, 5000);
|
|
715
715
|
}
|
|
716
|
-
} catch (
|
|
716
|
+
} catch (_err) {
|
|
717
717
|
// Process already terminated
|
|
718
718
|
}
|
|
719
719
|
}
|
|
@@ -1049,7 +1049,7 @@ function loadOrGenerateScenarios(taskId, options = {}) {
|
|
|
1049
1049
|
let apiMapContent = null;
|
|
1050
1050
|
try {
|
|
1051
1051
|
apiMapContent = fs.readFileSync(apiMapPath, 'utf-8');
|
|
1052
|
-
} catch (
|
|
1052
|
+
} catch (_err) {
|
|
1053
1053
|
// No API map available
|
|
1054
1054
|
}
|
|
1055
1055
|
|
|
@@ -1105,7 +1105,7 @@ async function runScenarioTests(taskId, scenarios, options = {}) {
|
|
|
1105
1105
|
try {
|
|
1106
1106
|
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
1107
1107
|
report.reportPath = reportPath;
|
|
1108
|
-
} catch (
|
|
1108
|
+
} catch (_err) {
|
|
1109
1109
|
// Non-fatal
|
|
1110
1110
|
}
|
|
1111
1111
|
}
|