@claude-flow/cli 3.10.10 → 3.10.12
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/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +10 -8
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/mcp.d.ts.map +1 -1
- package/dist/src/commands/mcp.js +14 -0
- package/dist/src/commands/mcp.js.map +1 -1
- package/dist/src/init/settings-generator.js +1 -1
- package/dist/src/init/statusline-generator.js +2 -2
- package/dist/src/mcp-tools/agent-execute-core.d.ts +2 -2
- package/dist/src/mcp-tools/agent-execute-core.d.ts.map +1 -1
- package/dist/src/mcp-tools/agent-execute-core.js +8 -5
- package/dist/src/mcp-tools/agent-execute-core.js.map +1 -1
- package/dist/src/mcp-tools/agent-tools.js +14 -14
- package/dist/src/mcp-tools/agent-tools.js.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.d.ts +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +176 -7
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/system-tools.js +5 -2
- package/dist/src/mcp-tools/system-tools.js.map +1 -1
- package/dist/src/runtime/parent-death-watchdog.d.ts +42 -0
- package/dist/src/runtime/parent-death-watchdog.d.ts.map +1 -0
- package/dist/src/runtime/parent-death-watchdog.js +70 -0
- package/dist/src/runtime/parent-death-watchdog.js.map +1 -0
- package/dist/src/ruvector/codemods/engine.d.ts +45 -0
- package/dist/src/ruvector/codemods/engine.d.ts.map +1 -0
- package/dist/src/ruvector/codemods/engine.js +291 -0
- package/dist/src/ruvector/codemods/engine.js.map +1 -0
- package/dist/src/ruvector/codemods/scope-analysis.d.ts +29 -0
- package/dist/src/ruvector/codemods/scope-analysis.d.ts.map +1 -0
- package/dist/src/ruvector/codemods/scope-analysis.js +162 -0
- package/dist/src/ruvector/codemods/scope-analysis.js.map +1 -0
- package/dist/src/ruvector/enhanced-model-router.d.ts +29 -8
- package/dist/src/ruvector/enhanced-model-router.d.ts.map +1 -1
- package/dist/src/ruvector/enhanced-model-router.js +104 -81
- package/dist/src/ruvector/enhanced-model-router.js.map +1 -1
- package/dist/src/ruvector/q-learning-router.d.ts +14 -1
- package/dist/src/ruvector/q-learning-router.d.ts.map +1 -1
- package/dist/src/ruvector/q-learning-router.js +36 -8
- package/dist/src/ruvector/q-learning-router.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/scripts/benchmark-codemods.mjs +127 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Provides intelligent hooks functionality via MCP protocol
|
|
4
4
|
*/
|
|
5
5
|
import { mkdirSync, writeFileSync, existsSync, readFileSync, statSync, unlinkSync, readdirSync } from 'fs';
|
|
6
|
+
import * as nodeFs from 'fs';
|
|
6
7
|
import { dirname, join, resolve } from 'path';
|
|
7
8
|
import { getProjectCwd } from './types.js';
|
|
8
9
|
import { validateIdentifier, validateText, validatePath } from './validate-input.js';
|
|
@@ -793,7 +794,7 @@ export const hooksPostCommand = {
|
|
|
793
794
|
};
|
|
794
795
|
export const hooksRoute = {
|
|
795
796
|
name: 'hooks_route',
|
|
796
|
-
description: 'Get a 3-tier routing recommendation for a task: Tier 1 (
|
|
797
|
+
description: 'Get a 3-tier routing recommendation for a task: Tier 1 (deterministic codemod, ~0ms / $0 — for var-to-const, remove-console, add-logging), Tier 2 (Haiku — simple), Tier 3 (Sonnet/Opus — complex). Use this BEFORE spawning an agent to avoid sending simple transforms to Sonnet. Native tools have no equivalent — Claude Code does not introspect its own model-selection cost. Returns the recommended model + a `[CODEMOD_AVAILABLE]` literal when a deterministic codemod can fully apply the edit (then call hooks_codemod). Use when native Bash hooks (via Claude Code\'s settings.json) are wrong because you need Ruflo-side state — pattern persistence, neural training signals, model-routing learning, cost tracking, audit chain. For one-off shell commands, plain Bash hooks are fine.',
|
|
797
798
|
inputSchema: {
|
|
798
799
|
type: 'object',
|
|
799
800
|
properties: {
|
|
@@ -1118,24 +1119,26 @@ export const hooksPreTask = {
|
|
|
1118
1119
|
: descLower.includes('simple') || descLower.includes('fix') || description.length < 50
|
|
1119
1120
|
? 'low'
|
|
1120
1121
|
: 'medium';
|
|
1121
|
-
// Enhanced model routing with
|
|
1122
|
+
// Enhanced model routing with deterministic Tier-1 codemods (ADR-026, ADR-143)
|
|
1122
1123
|
let modelRouting;
|
|
1123
1124
|
try {
|
|
1124
1125
|
const { getEnhancedModelRouter } = await import('../ruvector/enhanced-model-router.js');
|
|
1125
1126
|
const router = getEnhancedModelRouter();
|
|
1126
1127
|
const routeResult = await router.route(description, { filePath });
|
|
1127
1128
|
if (routeResult.tier === 1) {
|
|
1128
|
-
//
|
|
1129
|
+
// Deterministic codemod can apply this edit with $0 / no LLM (ADR-143)
|
|
1130
|
+
const intentType = routeResult.codemodIntent?.type ?? routeResult.agentBoosterIntent?.type;
|
|
1129
1131
|
modelRouting = {
|
|
1130
1132
|
tier: 1,
|
|
1131
|
-
handler: '
|
|
1133
|
+
handler: 'codemod',
|
|
1132
1134
|
canSkipLLM: true,
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
+
deterministic: true,
|
|
1136
|
+
codemodIntent: intentType,
|
|
1137
|
+
intentDescription: routeResult.codemodIntent?.description ?? routeResult.agentBoosterIntent?.description,
|
|
1135
1138
|
confidence: routeResult.confidence,
|
|
1136
1139
|
estimatedLatencyMs: routeResult.estimatedLatencyMs,
|
|
1137
1140
|
estimatedCost: routeResult.estimatedCost,
|
|
1138
|
-
recommendation: `[
|
|
1141
|
+
recommendation: `[CODEMOD_AVAILABLE] Skip LLM — call hooks_codemod with intent="${intentType}" (deterministic, $0)`,
|
|
1139
1142
|
};
|
|
1140
1143
|
}
|
|
1141
1144
|
else {
|
|
@@ -3896,6 +3899,170 @@ export const hooksModelStats = {
|
|
|
3896
3899
|
};
|
|
3897
3900
|
},
|
|
3898
3901
|
};
|
|
3902
|
+
// Supported source extensions for codemods.
|
|
3903
|
+
const CODEMOD_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts']);
|
|
3904
|
+
const CODEMOD_MAX_FILES = 2000;
|
|
3905
|
+
function codemodLangForExt(abs) {
|
|
3906
|
+
const ext = abs.slice(abs.lastIndexOf('.')).toLowerCase();
|
|
3907
|
+
if (ext === '.tsx')
|
|
3908
|
+
return 'tsx';
|
|
3909
|
+
if (ext === '.jsx')
|
|
3910
|
+
return 'jsx';
|
|
3911
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs')
|
|
3912
|
+
return 'javascript';
|
|
3913
|
+
return 'typescript';
|
|
3914
|
+
}
|
|
3915
|
+
// Deterministic codemod execution — the real Tier-1 path (ADR-143)
|
|
3916
|
+
export const hooksCodemod = {
|
|
3917
|
+
name: 'hooks_codemod',
|
|
3918
|
+
description: 'Apply a deterministic, $0 (no-LLM) code transform — the real Tier-1 execution path (ADR-143). Supported intents: var-to-const, remove-console, add-logging. Uses the TypeScript compiler with formatting-preserving edits (comments/whitespace survive). Targets: raw `code` (returns transformed text, writes nothing) | a single `file` | a `files` array | a `glob` pattern (batch — applies the intent across every match in one $0 call). Files are rewritten in place unless `dryRun`. Intents that need reasoning — add-types, add-error-handling, async-await — are NOT supported here; route those to a model via hooks_model-route. Use when hooks_pre-task / hooks_route returned [CODEMOD_AVAILABLE].',
|
|
3919
|
+
inputSchema: {
|
|
3920
|
+
type: 'object',
|
|
3921
|
+
properties: {
|
|
3922
|
+
intent: { type: 'string', enum: ['var-to-const', 'remove-console', 'add-logging'], description: 'Deterministic codemod to apply' },
|
|
3923
|
+
file: { type: 'string', description: 'Path to a single existing source file to transform in place' },
|
|
3924
|
+
files: { type: 'array', items: { type: 'string' }, description: 'Multiple file paths to transform in one batch call' },
|
|
3925
|
+
glob: { type: 'string', description: 'Glob pattern (relative to project root, e.g. "src/**/*.ts") — applies the intent to every matching source file' },
|
|
3926
|
+
code: { type: 'string', description: 'Raw source to transform instead of files (returns transformed code, writes nothing)' },
|
|
3927
|
+
language: { type: 'string', enum: ['javascript', 'typescript', 'jsx', 'tsx'], description: 'Language hint for raw code (default typescript; inferred from extension for files)' },
|
|
3928
|
+
dryRun: { type: 'boolean', description: 'Report what would change without writing files' },
|
|
3929
|
+
},
|
|
3930
|
+
required: ['intent'],
|
|
3931
|
+
},
|
|
3932
|
+
handler: async (params) => {
|
|
3933
|
+
const intent = params.intent;
|
|
3934
|
+
const file = params.file;
|
|
3935
|
+
const files = Array.isArray(params.files) ? params.files : undefined;
|
|
3936
|
+
const glob = params.glob;
|
|
3937
|
+
const rawCode = params.code;
|
|
3938
|
+
const dryRun = params.dryRun === true;
|
|
3939
|
+
const langParam = params.language;
|
|
3940
|
+
const { applyCodemod, isDeterministicCodemod } = await import('../ruvector/codemods/engine.js');
|
|
3941
|
+
if (!isDeterministicCodemod(intent)) {
|
|
3942
|
+
return {
|
|
3943
|
+
success: false,
|
|
3944
|
+
error: `"${intent}" is not a deterministic codemod. Route it to a model via hooks_model-route (Tier 2/3).`,
|
|
3945
|
+
};
|
|
3946
|
+
}
|
|
3947
|
+
// Mode A: transform raw code (never touches disk)
|
|
3948
|
+
if (typeof rawCode === 'string') {
|
|
3949
|
+
const language = langParam ?? 'typescript';
|
|
3950
|
+
const r = applyCodemod(intent, rawCode, { language });
|
|
3951
|
+
return {
|
|
3952
|
+
success: r.success, intent, mode: 'code', changed: r.changed, edits: r.edits,
|
|
3953
|
+
output: r.output, language: r.language, reason: r.reason, cost: 0, tier: 1,
|
|
3954
|
+
};
|
|
3955
|
+
}
|
|
3956
|
+
const cwd = getProjectCwd();
|
|
3957
|
+
// Resolve the target file set (single / array / glob), with path containment.
|
|
3958
|
+
const resolveTargets = () => {
|
|
3959
|
+
const out = new Set();
|
|
3960
|
+
const addRaw = (p) => {
|
|
3961
|
+
const v = validatePath(p, 'path');
|
|
3962
|
+
if (!v.valid)
|
|
3963
|
+
return v.error;
|
|
3964
|
+
const abs = resolve(cwd, v.sanitized);
|
|
3965
|
+
if (!abs.startsWith(cwd))
|
|
3966
|
+
return `path escapes project root: ${p}`;
|
|
3967
|
+
out.add(abs);
|
|
3968
|
+
return undefined;
|
|
3969
|
+
};
|
|
3970
|
+
if (file) {
|
|
3971
|
+
const e = addRaw(file);
|
|
3972
|
+
if (e)
|
|
3973
|
+
return { abs: [], truncated: false, error: e };
|
|
3974
|
+
}
|
|
3975
|
+
if (files)
|
|
3976
|
+
for (const p of files) {
|
|
3977
|
+
const e = addRaw(p);
|
|
3978
|
+
if (e)
|
|
3979
|
+
return { abs: [], truncated: false, error: e };
|
|
3980
|
+
}
|
|
3981
|
+
if (glob) {
|
|
3982
|
+
if (glob.includes('..'))
|
|
3983
|
+
return { abs: [], truncated: false, error: 'glob must not contain ".."' };
|
|
3984
|
+
// fs.globSync is Node 22+; @types/node here predates it, so type it locally.
|
|
3985
|
+
const globSync = nodeFs.globSync;
|
|
3986
|
+
if (typeof globSync !== 'function') {
|
|
3987
|
+
return { abs: [], truncated: false, error: 'glob requires Node 22+ (fs.globSync unavailable); pass `files[]` instead' };
|
|
3988
|
+
}
|
|
3989
|
+
let matches = [];
|
|
3990
|
+
try {
|
|
3991
|
+
matches = globSync(glob, { cwd });
|
|
3992
|
+
}
|
|
3993
|
+
catch (err) {
|
|
3994
|
+
return { abs: [], truncated: false, error: `glob failed: ${err.message}` };
|
|
3995
|
+
}
|
|
3996
|
+
for (const m of matches) {
|
|
3997
|
+
const abs = resolve(cwd, m);
|
|
3998
|
+
if (abs.startsWith(cwd) && CODEMOD_EXTENSIONS.has(abs.slice(abs.lastIndexOf('.')).toLowerCase())) {
|
|
3999
|
+
out.add(abs);
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
const all = [...out];
|
|
4004
|
+
const truncated = all.length > CODEMOD_MAX_FILES;
|
|
4005
|
+
return { abs: truncated ? all.slice(0, CODEMOD_MAX_FILES) : all, truncated };
|
|
4006
|
+
};
|
|
4007
|
+
const targets = resolveTargets();
|
|
4008
|
+
if (targets.error)
|
|
4009
|
+
return { success: false, error: targets.error };
|
|
4010
|
+
if (targets.abs.length === 0) {
|
|
4011
|
+
return { success: false, error: 'No target files. Provide `code`, `file`, `files[]`, or a matching `glob`.' };
|
|
4012
|
+
}
|
|
4013
|
+
// Apply to each file.
|
|
4014
|
+
const results = [];
|
|
4015
|
+
let filesChanged = 0, totalEdits = 0, failures = 0, skipped = 0;
|
|
4016
|
+
for (const abs of targets.abs) {
|
|
4017
|
+
const rel = abs.startsWith(cwd) ? abs.slice(cwd.length).replace(/^[/\\]/, '') : abs;
|
|
4018
|
+
if (!existsSync(abs)) {
|
|
4019
|
+
results.push({ file: rel, success: false, reason: 'not found' });
|
|
4020
|
+
failures++;
|
|
4021
|
+
continue;
|
|
4022
|
+
}
|
|
4023
|
+
if (!CODEMOD_EXTENSIONS.has(abs.slice(abs.lastIndexOf('.')).toLowerCase())) {
|
|
4024
|
+
results.push({ file: rel, success: false, reason: 'unsupported extension' });
|
|
4025
|
+
skipped++;
|
|
4026
|
+
continue;
|
|
4027
|
+
}
|
|
4028
|
+
const before = readFileSync(abs, 'utf-8');
|
|
4029
|
+
const r = applyCodemod(intent, before, { language: codemodLangForExt(abs) });
|
|
4030
|
+
if (!r.success) {
|
|
4031
|
+
results.push({ file: rel, success: false, changed: false, reason: r.reason });
|
|
4032
|
+
failures++;
|
|
4033
|
+
continue;
|
|
4034
|
+
}
|
|
4035
|
+
const written = r.changed && !dryRun;
|
|
4036
|
+
if (written)
|
|
4037
|
+
writeFileSync(abs, r.output, 'utf-8');
|
|
4038
|
+
if (r.changed) {
|
|
4039
|
+
filesChanged++;
|
|
4040
|
+
totalEdits += r.edits;
|
|
4041
|
+
}
|
|
4042
|
+
results.push({ file: rel, success: true, changed: r.changed, edits: r.edits, written });
|
|
4043
|
+
}
|
|
4044
|
+
const single = targets.abs.length === 1 && !files && !glob;
|
|
4045
|
+
return {
|
|
4046
|
+
success: failures === 0,
|
|
4047
|
+
intent,
|
|
4048
|
+
mode: single ? (dryRun ? 'dry-run' : 'file') : (dryRun ? 'batch-dry-run' : 'batch'),
|
|
4049
|
+
summary: {
|
|
4050
|
+
filesScanned: targets.abs.length,
|
|
4051
|
+
filesChanged,
|
|
4052
|
+
filesUnchanged: targets.abs.length - filesChanged - failures - skipped,
|
|
4053
|
+
totalEdits,
|
|
4054
|
+
failures,
|
|
4055
|
+
skipped,
|
|
4056
|
+
truncatedAt: targets.truncated ? CODEMOD_MAX_FILES : undefined,
|
|
4057
|
+
},
|
|
4058
|
+
results: results.slice(0, 500),
|
|
4059
|
+
resultsTruncated: results.length > 500,
|
|
4060
|
+
cost: 0,
|
|
4061
|
+
tier: 1,
|
|
4062
|
+
timestamp: new Date().toISOString(),
|
|
4063
|
+
};
|
|
4064
|
+
},
|
|
4065
|
+
};
|
|
3899
4066
|
// Simple fallback complexity analyzer
|
|
3900
4067
|
function analyzeComplexityFallback(task) {
|
|
3901
4068
|
const taskLower = task.toLowerCase();
|
|
@@ -4053,6 +4220,8 @@ export const hooksTools = [
|
|
|
4053
4220
|
hooksModelRoute,
|
|
4054
4221
|
hooksModelOutcome,
|
|
4055
4222
|
hooksModelStats,
|
|
4223
|
+
// Deterministic Tier-1 codemod execution (ADR-143)
|
|
4224
|
+
hooksCodemod,
|
|
4056
4225
|
];
|
|
4057
4226
|
export default hooksTools;
|
|
4058
4227
|
//# sourceMappingURL=hooks-tools.js.map
|