wogiflow 2.17.0 → 2.18.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-audit.md +212 -17
- package/.claude/commands/wogi-research.md +37 -0
- package/.claude/commands/wogi-review.md +200 -22
- package/.claude/commands/wogi-start.md +45 -0
- package/.claude/docs/claude-code-compatibility.md +46 -1
- package/.claude/docs/intent-grounded-review.md +209 -0
- package/.claude/settings.json +34 -1
- package/.workflow/agents/logic-adversary.md +8 -0
- package/.workflow/templates/claude-md.hbs +18 -0
- package/lib/installer.js +22 -0
- package/lib/utils.js +29 -3
- package/lib/workspace-changelog.js +2 -1
- package/lib/workspace-channel-server.js +4 -6
- package/lib/workspace-contracts.js +5 -4
- package/lib/workspace-events.js +8 -7
- package/lib/workspace-gates.js +4 -3
- package/lib/workspace-integration-tests.js +2 -1
- package/lib/workspace-intelligence.js +3 -2
- package/lib/workspace-locks.js +2 -1
- package/lib/workspace-messages.js +7 -6
- package/lib/workspace-routing.js +14 -26
- package/lib/workspace-session.js +7 -6
- package/lib/workspace-sync.js +9 -8
- package/package.json +4 -2
- package/scripts/base-workflow-step.js +1 -1
- package/scripts/flow +19 -0
- package/scripts/flow-adaptive-learning.js +1 -1
- package/scripts/flow-aggregate.js +2 -1
- package/scripts/flow-architect-pass.js +3 -3
- package/scripts/flow-archive-runs.js +372 -0
- package/scripts/flow-ask.js +1 -1
- package/scripts/flow-ast-grep.js +216 -0
- package/scripts/flow-audit-gates.js +1 -1
- package/scripts/flow-auto-learn.js +8 -11
- package/scripts/flow-bug.js +2 -2
- package/scripts/flow-capture-gate.js +644 -0
- package/scripts/flow-capture.js +4 -3
- package/scripts/flow-cli-flags.js +95 -0
- package/scripts/flow-community-sync.js +2 -1
- package/scripts/flow-community.js +6 -6
- package/scripts/flow-conclusion-classifier.js +310 -0
- package/scripts/flow-config-defaults.js +3 -3
- package/scripts/flow-constants.js +8 -11
- package/scripts/flow-context-scoring.js +1 -0
- package/scripts/flow-correction-detector.js +344 -3
- package/scripts/flow-damage-control.js +1 -1
- package/scripts/flow-decisions-merge.js +1 -0
- package/scripts/flow-done-gates.js +20 -0
- package/scripts/flow-done-report.js +2 -2
- package/scripts/flow-done.js +4 -4
- package/scripts/flow-epics.js +5 -11
- package/scripts/flow-health.js +145 -1
- package/scripts/flow-id.js +92 -0
- package/scripts/flow-io.js +15 -5
- package/scripts/flow-knowledge-router.js +2 -1
- package/scripts/flow-links.js +1 -1
- package/scripts/flow-log-manager.js +2 -1
- package/scripts/flow-logic-adversary.js +4 -4
- package/scripts/flow-long-input-cli.js +6 -0
- package/scripts/flow-long-input-stories.js +1 -1
- package/scripts/flow-loop-retry-learning.js +1 -1
- package/scripts/flow-mcp-capabilities.js +2 -3
- package/scripts/flow-mcp-docs.js +2 -1
- package/scripts/flow-memory-blocks.js +2 -1
- package/scripts/flow-memory-sync.js +1 -1
- package/scripts/flow-memory.js +767 -0
- package/scripts/flow-migrate-igr.js +1 -1
- package/scripts/flow-migrate.js +2 -1
- package/scripts/flow-model-adapter.js +1 -1
- package/scripts/flow-model-config.js +5 -1
- package/scripts/flow-model-profile.js +2 -1
- package/scripts/flow-orchestrate.js +3 -3
- package/scripts/flow-output.js +29 -0
- package/scripts/flow-parallel.js +10 -9
- package/scripts/flow-pattern-enforcer.js +2 -1
- package/scripts/flow-permissions-audit.js +124 -0
- package/scripts/flow-plugin-registry.js +2 -2
- package/scripts/flow-progress.js +5 -1
- package/scripts/flow-project-analyzer.js +1 -1
- package/scripts/flow-promote.js +510 -0
- package/scripts/flow-registries.js +86 -0
- package/scripts/flow-request-log.js +133 -0
- package/scripts/flow-research-protocol.js +0 -1
- package/scripts/flow-revision-tracker.js +2 -1
- package/scripts/flow-roadmap.js +2 -1
- package/scripts/flow-rules-sync.js +3 -7
- package/scripts/flow-session-end.js +3 -1
- package/scripts/flow-session-learning.js +6 -13
- package/scripts/flow-session-state.js +2 -2
- package/scripts/flow-setup-hooks.js +2 -1
- package/scripts/flow-skill-create.js +1 -1
- package/scripts/flow-skill-freshness.js +6 -7
- package/scripts/flow-skill-learn.js +1 -1
- package/scripts/flow-step-coverage.js +1 -1
- package/scripts/flow-step-security.js +1 -1
- package/scripts/flow-story.js +58 -10
- package/scripts/flow-sys.js +204 -0
- package/scripts/flow-task-hierarchy.js +88 -0
- package/scripts/flow-tech-debt.js +2 -1
- package/scripts/flow-test-api.js +1 -1
- package/scripts/flow-utils.js +60 -890
- package/scripts/hooks/core/bugfix-scope-gate.js +5 -4
- package/scripts/hooks/core/deploy-gate.js +1 -1
- package/scripts/hooks/core/pre-tool-helpers.js +72 -0
- package/scripts/hooks/core/pre-tool-orchestrator.js +442 -0
- package/scripts/hooks/core/routing-gate.js +8 -0
- package/scripts/hooks/core/session-end.js +28 -0
- package/scripts/hooks/entry/claude-code/pre-tool-use.js +48 -492
- package/scripts/hooks/entry/shared/hook-runner.js +1 -1
- package/scripts/registries/schema-registry.js +1 -1
- package/scripts/registries/service-registry.js +1 -1
package/scripts/flow-health.js
CHANGED
|
@@ -82,6 +82,123 @@ function checkClaudeCodeVersion() {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Normalize an MCP server config so two equivalent objects compare equal
|
|
87
|
+
* after JSON.stringify. Recursively sorts object keys and stringifies arrays
|
|
88
|
+
* in their original order (order is meaningful for args).
|
|
89
|
+
*
|
|
90
|
+
* @param {*} cfg - An MCP server config value (typically an object, but may be primitive)
|
|
91
|
+
* @returns {string} Canonical JSON representation suitable for equality comparison
|
|
92
|
+
*/
|
|
93
|
+
function normalizeMcpConfig(cfg) {
|
|
94
|
+
const sortKeys = (v) => {
|
|
95
|
+
if (Array.isArray(v)) return v.map(sortKeys);
|
|
96
|
+
if (v && typeof v === 'object') {
|
|
97
|
+
const out = {};
|
|
98
|
+
for (const k of Object.keys(v).sort()) {
|
|
99
|
+
out[k] = sortKeys(v[k]);
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
return v;
|
|
104
|
+
};
|
|
105
|
+
return JSON.stringify(sortKeys(cfg));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check MCP server definitions across the three Claude Code settings scopes:
|
|
110
|
+
* - user: ~/.claude/settings.json
|
|
111
|
+
* - project: <project>/.claude/settings.json
|
|
112
|
+
* - local: <project>/.claude/settings.local.json
|
|
113
|
+
*
|
|
114
|
+
* Mirrors the /doctor warning added in Claude Code 2.1.110: a server defined
|
|
115
|
+
* in multiple scopes with divergent config is almost always a mistake — the
|
|
116
|
+
* lowest-priority scope silently loses, and users debug ghost endpoints.
|
|
117
|
+
*
|
|
118
|
+
* Identical duplicate definitions are intentionally NOT flagged — some teams
|
|
119
|
+
* duplicate for portability. Only divergent configs surface.
|
|
120
|
+
*
|
|
121
|
+
* @param {Object} [opts]
|
|
122
|
+
* @param {string} [opts.userSettingsPath] - Override path to user scope (for tests)
|
|
123
|
+
* @param {string} [opts.projectSettingsPath] - Override path to project scope (for tests)
|
|
124
|
+
* @param {string} [opts.localSettingsPath] - Override path to local scope (for tests)
|
|
125
|
+
* @returns {{
|
|
126
|
+
* duplicates: Array<{name: string, scopes: string[]}>,
|
|
127
|
+
* uniqueServers: number,
|
|
128
|
+
* parseErrors: Array<{file: string, error: string}>,
|
|
129
|
+
* scopesChecked: number
|
|
130
|
+
* }}
|
|
131
|
+
*/
|
|
132
|
+
function checkMcpScopes(opts = {}) {
|
|
133
|
+
const os = require('node:os');
|
|
134
|
+
const scopes = [
|
|
135
|
+
{ file: opts.userSettingsPath || path.join(os.homedir(), '.claude', 'settings.json'), label: 'user' },
|
|
136
|
+
{ file: opts.projectSettingsPath || path.join(PROJECT_ROOT, '.claude', 'settings.json'), label: 'project' },
|
|
137
|
+
{ file: opts.localSettingsPath || path.join(PROJECT_ROOT, '.claude', 'settings.local.json'), label: 'local' }
|
|
138
|
+
];
|
|
139
|
+
|
|
140
|
+
const parseErrors = [];
|
|
141
|
+
const serverByName = new Map();
|
|
142
|
+
let scopesChecked = 0;
|
|
143
|
+
|
|
144
|
+
// Sentinel object for safeJsonParse so we can distinguish "file failed to
|
|
145
|
+
// parse" (returned sentinel) from "file parsed but had no mcpServers" (returned
|
|
146
|
+
// object without the key). safeJsonParse is mandated by security-patterns.md §2
|
|
147
|
+
// (prototype-pollution guard + non-object validation) — we layer a raw-read
|
|
148
|
+
// fallback on top to categorize the failure cause for health output.
|
|
149
|
+
const PARSE_SENTINEL = Object.create(null);
|
|
150
|
+
|
|
151
|
+
for (const scope of scopes) {
|
|
152
|
+
if (!fileExists(scope.file)) continue;
|
|
153
|
+
scopesChecked++;
|
|
154
|
+
const json = safeJsonParse(scope.file, PARSE_SENTINEL);
|
|
155
|
+
if (json === PARSE_SENTINEL) {
|
|
156
|
+
// safeJsonParse returned the sentinel — categorize: read failure vs JSON
|
|
157
|
+
// parse failure vs non-object vs prototype-pollution rejection.
|
|
158
|
+
let raw;
|
|
159
|
+
try {
|
|
160
|
+
raw = fs.readFileSync(scope.file, 'utf-8');
|
|
161
|
+
} catch (err) {
|
|
162
|
+
parseErrors.push({ file: scope.file, error: `read failed: ${err.message}` });
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
JSON.parse(raw);
|
|
167
|
+
// Raw parse succeeded — safeJsonParse rejected for structural reason
|
|
168
|
+
// (non-object top level, or dangerous __proto__/constructor keys).
|
|
169
|
+
parseErrors.push({ file: scope.file, error: 'rejected by safeJsonParse (non-object or prototype-pollution guard)' });
|
|
170
|
+
} catch (err) {
|
|
171
|
+
parseErrors.push({ file: scope.file, error: `invalid JSON: ${err.message}` });
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const mcp = json.mcpServers;
|
|
176
|
+
if (!mcp || typeof mcp !== 'object' || Array.isArray(mcp)) continue;
|
|
177
|
+
|
|
178
|
+
for (const name of Object.keys(mcp)) {
|
|
179
|
+
if (!serverByName.has(name)) serverByName.set(name, []);
|
|
180
|
+
serverByName.get(name).push({ scope: scope.label, config: mcp[name] });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const duplicates = [];
|
|
185
|
+
for (const [name, entries] of serverByName.entries()) {
|
|
186
|
+
if (entries.length < 2) continue;
|
|
187
|
+
const canonical = normalizeMcpConfig(entries[0].config);
|
|
188
|
+
const allSame = entries.every(e => normalizeMcpConfig(e.config) === canonical);
|
|
189
|
+
if (!allSame) {
|
|
190
|
+
duplicates.push({ name, scopes: entries.map(e => e.scope) });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
duplicates,
|
|
196
|
+
uniqueServers: serverByName.size,
|
|
197
|
+
parseErrors,
|
|
198
|
+
scopesChecked
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
85
202
|
function main() {
|
|
86
203
|
console.log(color('cyan', 'Wogi Flow Health Check'));
|
|
87
204
|
console.log('========================');
|
|
@@ -661,6 +778,29 @@ function main() {
|
|
|
661
778
|
}
|
|
662
779
|
}
|
|
663
780
|
|
|
781
|
+
// Check MCP server definitions across scopes (mirrors Claude Code 2.1.110 /doctor)
|
|
782
|
+
console.log('');
|
|
783
|
+
printSection('Checking MCP server scopes...');
|
|
784
|
+
|
|
785
|
+
const mcp = checkMcpScopes();
|
|
786
|
+
if (mcp.parseErrors.length > 0) {
|
|
787
|
+
for (const e of mcp.parseErrors) {
|
|
788
|
+
warn(`Could not parse ${e.file}: ${e.error}`);
|
|
789
|
+
}
|
|
790
|
+
warnings += mcp.parseErrors.length;
|
|
791
|
+
}
|
|
792
|
+
if (mcp.uniqueServers === 0) {
|
|
793
|
+
console.log(` ${color('dim', '○')} No MCP servers defined in user / project / local scopes`);
|
|
794
|
+
} else if (mcp.duplicates.length === 0) {
|
|
795
|
+
success(`No conflicting MCP server definitions across ${mcp.scopesChecked} scope(s)`);
|
|
796
|
+
} else {
|
|
797
|
+
for (const f of mcp.duplicates) {
|
|
798
|
+
warn(`MCP server "${f.name}" has divergent config in scopes: ${f.scopes.join(' + ')}`);
|
|
799
|
+
console.log(` ${color('dim', '→ Consolidate into a single scope; /doctor will flag this too')}`);
|
|
800
|
+
}
|
|
801
|
+
warnings += mcp.duplicates.length;
|
|
802
|
+
}
|
|
803
|
+
|
|
664
804
|
// Check .gitignore sync
|
|
665
805
|
console.log('');
|
|
666
806
|
printSection('Checking .gitignore sync...');
|
|
@@ -1159,4 +1299,8 @@ function run() {
|
|
|
1159
1299
|
}
|
|
1160
1300
|
}
|
|
1161
1301
|
|
|
1162
|
-
|
|
1302
|
+
if (require.main === module) {
|
|
1303
|
+
run();
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
module.exports = { checkMcpScopes, normalizeMcpConfig };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wogi Flow - Hash-Based ID Generation & Validation
|
|
5
|
+
*
|
|
6
|
+
* Generates collision-resistant 8-char hex IDs for work items:
|
|
7
|
+
* wf-XXXXXXXX (task), ep-XXXXXXXX (epic), ft-XXXXXXXX (feature), pl-XXXXXXXX (plan)
|
|
8
|
+
*
|
|
9
|
+
* Extracted from flow-utils.js (wf-94cc3b72 epic — flow-utils decomposition).
|
|
10
|
+
* Zero external deps beyond node:crypto — pure helpers.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const crypto = require('node:crypto');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generate a hash-based ID with a given prefix.
|
|
19
|
+
* Uses SHA256 of (seed + title + timestamp + randomBytes) for collision resistance.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} prefix - ID prefix (e.g., 'wf', 'ep', 'ft', 'pl')
|
|
22
|
+
* @param {string} seed - Seed string for the hash (e.g., '', 'epic-', 'feature-')
|
|
23
|
+
* @param {string} title - Title to include in hash input
|
|
24
|
+
* @returns {string} ID in format prefix-XXXXXXXX
|
|
25
|
+
*/
|
|
26
|
+
function generateHashId(prefix, seed, title) {
|
|
27
|
+
const randomHex = crypto.randomBytes(8).toString('hex');
|
|
28
|
+
const input = `${seed}${title}${Date.now()}${randomHex}`;
|
|
29
|
+
const hash = crypto.createHash('sha256').update(input).digest('hex').slice(0, 8);
|
|
30
|
+
return `${prefix}-${hash}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Generate a hash-based task ID
|
|
35
|
+
* Format: wf-XXXXXXXX (8-char hex hash)
|
|
36
|
+
* @example generateTaskId('Fix login bug') // => 'wf-a1b2c3d4'
|
|
37
|
+
*/
|
|
38
|
+
function generateTaskId(title) {
|
|
39
|
+
return generateHashId('wf', '', title);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Generate a hash-based epic ID (ep-XXXXXXXX) */
|
|
43
|
+
function generateEpicId(title) {
|
|
44
|
+
return generateHashId('ep', 'epic-', title);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Generate a hash-based feature ID (ft-XXXXXXXX) */
|
|
48
|
+
function generateFeatureId(title) {
|
|
49
|
+
return generateHashId('ft', 'feature-', title);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Generate a hash-based plan ID (pl-XXXXXXXX) */
|
|
53
|
+
function generatePlanId(title) {
|
|
54
|
+
return generateHashId('pl', 'plan-', title);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if a string is a valid task ID (old or new format)
|
|
59
|
+
* @returns {{ valid: boolean, format: 'hash' | 'legacy' | null }}
|
|
60
|
+
*/
|
|
61
|
+
function validateTaskId(id) {
|
|
62
|
+
if (!id || typeof id !== 'string') {
|
|
63
|
+
return { valid: false, format: null };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// New hash-based format: wf-XXXXXXXX
|
|
67
|
+
if (/^wf-[a-f0-9]{8}$/i.test(id)) {
|
|
68
|
+
return { valid: true, format: 'hash' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Legacy formats: TASK-XXX, BUG-XXX
|
|
72
|
+
if (/^(TASK|BUG)-\d{3,}$/i.test(id)) {
|
|
73
|
+
return { valid: true, format: 'legacy' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { valid: false, format: null };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Check if ID is in legacy format (for migration warnings) */
|
|
80
|
+
function isLegacyTaskId(id) {
|
|
81
|
+
return /^(TASK|BUG)-\d{3,}$/i.test(id);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
generateHashId,
|
|
86
|
+
generateTaskId,
|
|
87
|
+
generateEpicId,
|
|
88
|
+
generateFeatureId,
|
|
89
|
+
generatePlanId,
|
|
90
|
+
validateTaskId,
|
|
91
|
+
isLegacyTaskId,
|
|
92
|
+
};
|
package/scripts/flow-io.js
CHANGED
|
@@ -16,6 +16,15 @@ const { PROJECT_ROOT } = require('./flow-paths');
|
|
|
16
16
|
// Constants - Named values for magic numbers
|
|
17
17
|
// ============================================================
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Canonical DANGEROUS_KEYS — prototype-pollution guard constant used by
|
|
21
|
+
* JSON parse safety checks, plugin-name validation, frontmatter parsers.
|
|
22
|
+
* Consolidated 2026-04-15 per audit dup-002 (wf-2f6fbb12). All scripts/
|
|
23
|
+
* consumers should import from here; lib/ has its own local copy to avoid
|
|
24
|
+
* lib→scripts dependency per dual-repo-management.md.
|
|
25
|
+
*/
|
|
26
|
+
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
27
|
+
|
|
19
28
|
/** Default lock stale threshold (60 seconds) */
|
|
20
29
|
const LOCK_STALE_THRESHOLD_MS = 60000;
|
|
21
30
|
|
|
@@ -607,9 +616,9 @@ function cleanupStaleLocks(dirPath, staleMs = CLEANUP_LOCK_STALE_MS) {
|
|
|
607
616
|
try {
|
|
608
617
|
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
609
618
|
cleaned++;
|
|
610
|
-
} catch (
|
|
619
|
+
} catch (err) {
|
|
611
620
|
if (process.env.DEBUG) {
|
|
612
|
-
console.warn(`[DEBUG] cleanupStaleLocks: Could not force delete ${lockDir}: ${
|
|
621
|
+
console.warn(`[DEBUG] cleanupStaleLocks: Could not force delete ${lockDir}: ${err.message}`);
|
|
613
622
|
}
|
|
614
623
|
}
|
|
615
624
|
}
|
|
@@ -626,10 +635,10 @@ function cleanupStaleLocks(dirPath, staleMs = CLEANUP_LOCK_STALE_MS) {
|
|
|
626
635
|
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
627
636
|
cleaned++;
|
|
628
637
|
}
|
|
629
|
-
} catch (
|
|
638
|
+
} catch (err) {
|
|
630
639
|
// Directory gone or inaccessible - skip
|
|
631
|
-
if (
|
|
632
|
-
console.warn(`[DEBUG] cleanupStaleLocks: Could not stat ${lockDir}: ${
|
|
640
|
+
if (err.code !== 'ENOENT' && process.env.DEBUG) {
|
|
641
|
+
console.warn(`[DEBUG] cleanupStaleLocks: Could not stat ${lockDir}: ${err.message}`);
|
|
633
642
|
}
|
|
634
643
|
}
|
|
635
644
|
}
|
|
@@ -666,6 +675,7 @@ function sanitizeForContext(value, maxLen = 200) {
|
|
|
666
675
|
|
|
667
676
|
module.exports = {
|
|
668
677
|
// Constants
|
|
678
|
+
DANGEROUS_KEYS,
|
|
669
679
|
LOCK_STALE_THRESHOLD_MS,
|
|
670
680
|
CLEANUP_LOCK_STALE_MS,
|
|
671
681
|
LOCK_RETRY_DELAY_MS,
|
package/scripts/flow-links.js
CHANGED
|
@@ -25,7 +25,7 @@ const path = require('node:path');
|
|
|
25
25
|
const https = require('node:https');
|
|
26
26
|
const http = require('node:http');
|
|
27
27
|
const dns = require('dns');
|
|
28
|
-
const { getProjectRoot, colors: c, readJson, PATHS } = require('./flow-utils');
|
|
28
|
+
const { getProjectRoot, colors: c, readJson, PATHS, getTodayDate } = require('./flow-utils');
|
|
29
29
|
const { success: printSuccess } = require('./flow-output');
|
|
30
30
|
|
|
31
31
|
const LINKS_PATH = path.join(PATHS.workflow, 'links.yaml');
|
|
@@ -53,7 +53,7 @@ const gateTelemetry = require('./flow-gate-telemetry');
|
|
|
53
53
|
// ============================================================
|
|
54
54
|
|
|
55
55
|
const RUBRIC_DIR = path.join(PATHS.workflow, 'rubrics');
|
|
56
|
-
const DEFAULT_RUBRIC = 'logic-constitution-
|
|
56
|
+
const DEFAULT_RUBRIC = 'logic-constitution-v3';
|
|
57
57
|
const CALIBRATION_PATH = path.join(PATHS.state, 'adversary-calibration.json');
|
|
58
58
|
|
|
59
59
|
const VALID_OVERALL_VERDICTS = new Set([
|
|
@@ -84,7 +84,7 @@ const INTENT_ARTIFACTS = {
|
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* Load the Logic Constitution rubric by version identifier.
|
|
87
|
-
* @param {string} [version='logic-constitution-
|
|
87
|
+
* @param {string} [version='logic-constitution-v3']
|
|
88
88
|
* @returns {{ content: string, version: string, path: string }}
|
|
89
89
|
*/
|
|
90
90
|
function loadRubric(version = DEFAULT_RUBRIC) {
|
|
@@ -149,7 +149,7 @@ function readIntentArtifact(key) {
|
|
|
149
149
|
* @param {string} [opts.taskId]
|
|
150
150
|
* @param {number} [opts.round=1] - Iteration round number (≥1).
|
|
151
151
|
* @param {object} [opts.previousAdversaryOutput] - Prior round's parsed output when re-running.
|
|
152
|
-
* @param {string} [opts.rubricVersion='logic-constitution-
|
|
152
|
+
* @param {string} [opts.rubricVersion='logic-constitution-v3']
|
|
153
153
|
* @param {number} [opts.calibrationCount=3] - How many calibration examples to inject.
|
|
154
154
|
* @returns {{ systemPrompt:string, userPrompt:string, metadata:object }}
|
|
155
155
|
*/
|
|
@@ -317,7 +317,7 @@ All 10 principles must appear in the \`principles\` array, even if SKIPped.
|
|
|
317
317
|
* @param {Object} ctx
|
|
318
318
|
* @param {string} [ctx.taskId]
|
|
319
319
|
* @param {number} [ctx.round=1]
|
|
320
|
-
* @param {string} [ctx.rubricVersion='logic-constitution-
|
|
320
|
+
* @param {string} [ctx.rubricVersion='logic-constitution-v3']
|
|
321
321
|
* @returns {object} The validated adversary verdict.
|
|
322
322
|
*/
|
|
323
323
|
function parseAdversaryOutput(response, ctx = {}) {
|
|
@@ -20,6 +20,12 @@ const c = {
|
|
|
20
20
|
red: '\x1b[31m'
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
// Local helper — 5 call sites referenced printWarn without defining it
|
|
24
|
+
// (latent no-undef bug caught by eslint upgrade; wf-5a6df88a).
|
|
25
|
+
function printWarn(msg) {
|
|
26
|
+
console.warn(`${c.yellow}⚠ ${msg}${c.reset}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
/**
|
|
24
30
|
* CLI handler
|
|
25
31
|
*/
|
|
@@ -2150,7 +2150,7 @@ function cleanupTempFiles(digestId) {
|
|
|
2150
2150
|
if (active && active.session?.digest_id === digestId) {
|
|
2151
2151
|
try {
|
|
2152
2152
|
fs.unlinkSync(activeFile);
|
|
2153
|
-
} catch (
|
|
2153
|
+
} catch (_err) {
|
|
2154
2154
|
// Ignore errors unlinking active file - main cleanup succeeded
|
|
2155
2155
|
}
|
|
2156
2156
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('node:fs');
|
|
17
17
|
const path = require('node:path');
|
|
18
|
-
const { getConfig, getProjectRoot, colors, readJson, info, success, PATHS } = require('./flow-utils');
|
|
18
|
+
const { getConfig, getProjectRoot, colors, readJson, info, success, PATHS, getTodayDate } = require('./flow-utils');
|
|
19
19
|
const { FailureCategory, detectCategory } = require('./flow-failure-categories');
|
|
20
20
|
|
|
21
21
|
const LEARNING_LOG_PATH = path.join(PATHS.state, 'adaptive-learning.json');
|
|
@@ -32,10 +32,9 @@
|
|
|
32
32
|
|
|
33
33
|
const fs = require('node:fs');
|
|
34
34
|
const path = require('node:path');
|
|
35
|
-
const { PATHS, getConfig, safeJsonParse, readJson, writeJson, fileExists } = require('./flow-utils');
|
|
35
|
+
const { PATHS, getConfig, safeJsonParse, readJson, writeJson, fileExists, DANGEROUS_KEYS } = require('./flow-utils');
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
37
|
+
// Local DANGEROUS_KEYS consolidated to flow-io canonical (wf-2f6fbb12 / dup-002).
|
|
39
38
|
|
|
40
39
|
// ============================================================
|
|
41
40
|
// Constants
|
package/scripts/flow-mcp-docs.js
CHANGED
|
@@ -22,6 +22,7 @@ const path = require('node:path');
|
|
|
22
22
|
const memoryDb = require('./flow-memory-db');
|
|
23
23
|
const { getConfig, PROJECT_ROOT } = require('./flow-config-loader');
|
|
24
24
|
const { color } = require('./flow-output');
|
|
25
|
+
const { PATHS } = require('./flow-utils');
|
|
25
26
|
|
|
26
27
|
// Lazy-load to avoid circular dependency
|
|
27
28
|
let _syncDecisionsToRules = null;
|
|
@@ -469,7 +470,6 @@ module.exports = {
|
|
|
469
470
|
listCandidates,
|
|
470
471
|
promoteFact,
|
|
471
472
|
showStatus,
|
|
472
|
-
loadConfig
|
|
473
473
|
};
|
|
474
474
|
|
|
475
475
|
// Run CLI if executed directly
|