moflo 4.9.7 → 4.9.9
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/bin/hooks.mjs +14 -1
- package/bin/index-all.mjs +124 -104
- package/bin/lib/index-fingerprint.mjs +0 -0
- package/bin/prompt-hook.mjs +16 -2
- package/bin/session-start-launcher.mjs +41 -4
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
package/bin/hooks.mjs
CHANGED
|
@@ -289,7 +289,20 @@ async function main() {
|
|
|
289
289
|
// chain and producing the sqlite_autoindex corruption that #743 had
|
|
290
290
|
// to repair on subsequent sessions. The launcher's foreground §3e
|
|
291
291
|
// migration is the canonical migration site. See #744.
|
|
292
|
-
|
|
292
|
+
// Prefer the npm-bin copy of index-all.mjs over __dirname resolution
|
|
293
|
+
// (#866). When this hooks.mjs is loaded from `.claude/scripts/` (e.g.
|
|
294
|
+
// an older launcher spawned us before the launcher's bin-anchor fix
|
|
295
|
+
// landed), `resolve(__dirname, 'index-all.mjs')` would point at the
|
|
296
|
+
// synced mirror — which the launcher's section 3 may still be in the
|
|
297
|
+
// middle of overwriting during an upgrade. resolveBinOrLocal's
|
|
298
|
+
// bin-first ordering guarantees the spawned chain matches the
|
|
299
|
+
// installed package even when the mirror is mid-sync.
|
|
300
|
+
const indexAllScript = resolveBinOrLocal('flo-index-all', 'index-all.mjs');
|
|
301
|
+
if (indexAllScript) {
|
|
302
|
+
spawnWindowless('node', [indexAllScript], 'sequential indexing chain');
|
|
303
|
+
} else {
|
|
304
|
+
log('warn', 'index-all.mjs not found (checked npm bin + .claude/scripts/)');
|
|
305
|
+
}
|
|
293
306
|
// Neural patterns now loaded by moflo core routing — no external patching.
|
|
294
307
|
break;
|
|
295
308
|
}
|
package/bin/index-all.mjs
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Sequential indexer chain for session-start.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Each step is gated independently — see `lib/index-fingerprint.mjs`. The
|
|
6
|
+
* orchestrator just walks the plan, asks the gate per step, runs the
|
|
7
|
+
* survivors, and saves the post-run fingerprint when each succeeds.
|
|
8
|
+
*
|
|
9
|
+
* Steps run sequentially (DB-writing) to avoid sql.js last-write-wins
|
|
10
|
+
* concurrency issues (#78). HNSW rebuild is last, after every other step
|
|
11
|
+
* has committed (#81).
|
|
8
12
|
*
|
|
9
13
|
* Spawned as a single detached background process by hooks.mjs session-start.
|
|
10
14
|
*/
|
|
@@ -15,7 +19,12 @@ import { fileURLToPath } from 'url';
|
|
|
15
19
|
import { spawn, spawnSync } from 'child_process';
|
|
16
20
|
import { platform } from 'os';
|
|
17
21
|
import { hnswIndexPath } from './lib/moflo-paths.mjs';
|
|
18
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
decideStepGate,
|
|
24
|
+
computeStepFingerprint,
|
|
25
|
+
saveStepFingerprint,
|
|
26
|
+
cleanupLegacyFingerprint,
|
|
27
|
+
} from './lib/index-fingerprint.mjs';
|
|
19
28
|
|
|
20
29
|
// Cap fastembed/ONNX thread count when spawning the heavy steps. Without
|
|
21
30
|
// this, ONNX defaults to one thread per CPU core (22+ on a modern dev box),
|
|
@@ -66,7 +75,6 @@ function resolveBin(binName, localScript) {
|
|
|
66
75
|
|
|
67
76
|
function getLocalCliPath() {
|
|
68
77
|
const paths = [
|
|
69
|
-
resolve(projectRoot, 'node_modules/moflo/bin/cli.js'),
|
|
70
78
|
resolve(projectRoot, 'node_modules/moflo/bin/cli.js'),
|
|
71
79
|
resolve(projectRoot, 'node_modules/.bin/flo'),
|
|
72
80
|
// Development: local CLI
|
|
@@ -159,128 +167,140 @@ function runStep(label, cmd, args, timeoutMs = 120_000, extraEnv = null) {
|
|
|
159
167
|
});
|
|
160
168
|
}
|
|
161
169
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
// module exists to fix. Override with FLO_FORCE_INDEX=1.
|
|
171
|
-
const gate = decideIndexGate(projectRoot);
|
|
172
|
-
if (gate.skip) {
|
|
173
|
-
log(`SKIP full chain — ${gate.reason} (no inputs changed since last run)`);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
log(`Sequential indexing chain started (gate: ${gate.reason})`);
|
|
177
|
-
|
|
178
|
-
// 1. Guidance indexer
|
|
179
|
-
if (isIndexEnabled('guidance')) {
|
|
180
|
-
const guidanceScript = resolveBin('flo-index', 'index-guidance.mjs');
|
|
181
|
-
if (guidanceScript) {
|
|
182
|
-
await runStep('guidance-index', 'node', [guidanceScript, '--no-embeddings']);
|
|
183
|
-
} else {
|
|
184
|
-
log('SKIP guidance-index (script not found)');
|
|
185
|
-
}
|
|
186
|
-
} else {
|
|
187
|
-
log('SKIP guidance-index (disabled in moflo.yaml)');
|
|
188
|
-
}
|
|
170
|
+
/**
|
|
171
|
+
* Build the ordered step plan. Each entry is `{ name, cmd, args, timeoutMs, env? }`.
|
|
172
|
+
* Steps disabled in moflo.yaml or whose script can't be located are filtered
|
|
173
|
+
* out here so the run loop only sees runnable steps.
|
|
174
|
+
*/
|
|
175
|
+
function buildStepPlan() {
|
|
176
|
+
const plan = [];
|
|
177
|
+
const localCli = getLocalCliPath();
|
|
189
178
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
await runStep('code-map', 'node', [codeMapScript, '--no-embeddings'], 180_000);
|
|
195
|
-
} else {
|
|
196
|
-
log('SKIP code-map (script not found)');
|
|
179
|
+
const consider = (name, cfgKey, scriptName, binName, args, timeoutMs = 120_000, env = null) => {
|
|
180
|
+
if (cfgKey && !isIndexEnabled(cfgKey)) {
|
|
181
|
+
log(`SKIP ${name} (disabled in moflo.yaml)`);
|
|
182
|
+
return;
|
|
197
183
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// 3. Test indexer
|
|
203
|
-
if (isIndexEnabled('tests')) {
|
|
204
|
-
const testScript = resolveBin('flo-testmap', 'index-tests.mjs');
|
|
205
|
-
if (testScript) {
|
|
206
|
-
await runStep('test-index', 'node', [testScript, '--no-embeddings']);
|
|
207
|
-
} else {
|
|
208
|
-
log('SKIP test-index (script not found)');
|
|
184
|
+
const script = scriptName ? resolveBin(binName, scriptName) : null;
|
|
185
|
+
if (scriptName && !script) {
|
|
186
|
+
log(`SKIP ${name} (script not found)`);
|
|
187
|
+
return;
|
|
209
188
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
189
|
+
plan.push({
|
|
190
|
+
name,
|
|
191
|
+
cmd: 'node',
|
|
192
|
+
args: scriptName ? [script, ...args] : args,
|
|
193
|
+
timeoutMs,
|
|
194
|
+
env,
|
|
195
|
+
});
|
|
196
|
+
};
|
|
213
197
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
await runStep('patterns-index', 'node', [patternsScript]);
|
|
219
|
-
} else {
|
|
220
|
-
log('SKIP patterns-index (script not found)');
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
log('SKIP patterns-index (disabled in moflo.yaml)');
|
|
224
|
-
}
|
|
198
|
+
consider('guidance-index', 'guidance', 'index-guidance.mjs', 'flo-index', ['--no-embeddings']);
|
|
199
|
+
consider('code-map', 'code_map', 'generate-code-map.mjs', 'flo-codemap', ['--no-embeddings'], 180_000);
|
|
200
|
+
consider('test-index', 'tests', 'index-tests.mjs', 'flo-testmap', ['--no-embeddings']);
|
|
201
|
+
consider('patterns-index', 'patterns', 'index-patterns.mjs', 'flo-patterns', []);
|
|
225
202
|
|
|
226
|
-
//
|
|
227
|
-
|
|
203
|
+
// Pretrain extracts patterns from the repo via the CLI subcommand. No
|
|
204
|
+
// direct script — invoke through the local flo binary.
|
|
228
205
|
if (localCli) {
|
|
229
|
-
|
|
206
|
+
plan.push({
|
|
207
|
+
name: 'pretrain',
|
|
208
|
+
cmd: 'node',
|
|
209
|
+
args: [localCli, 'hooks', 'pretrain'],
|
|
210
|
+
timeoutMs: 120_000,
|
|
211
|
+
});
|
|
230
212
|
} else {
|
|
231
213
|
log('SKIP pretrain (CLI not found)');
|
|
232
214
|
}
|
|
233
215
|
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
216
|
+
// build-embeddings runs fastembed → thread-capped to keep CPU usable.
|
|
217
|
+
consider('build-embeddings', null, 'build-embeddings.mjs', 'flo-embeddings', [], 300_000, ONNX_THREAD_CAP);
|
|
218
|
+
|
|
219
|
+
// HNSW MUST run last (after all DB writes are committed, #81). Same thread
|
|
220
|
+
// cap — rebuild-index loads fastembed for stats lookups.
|
|
221
|
+
//
|
|
222
|
+
// No `--force`: the embeddings-migration service (run by the launcher
|
|
223
|
+
// before this chain) handles model bumps, and `build-embeddings` above
|
|
224
|
+
// fills any rows that lack embeddings. So `rebuild-index` finds nothing
|
|
225
|
+
// to embed in steady state and takes the no-work path, which still
|
|
226
|
+
// refreshes the HNSW sidecar via `writeSidecarOrFail` and is followed by
|
|
227
|
+
// the existsSync post-check below. `--force` only added a 4000-row
|
|
228
|
+
// re-embed that the fingerprint gate (#858) is specifically trying to
|
|
229
|
+
// avoid (#859).
|
|
230
|
+
if (localCli) {
|
|
231
|
+
plan.push({
|
|
232
|
+
name: 'hnsw-rebuild',
|
|
233
|
+
cmd: 'node',
|
|
234
|
+
args: [localCli, 'memory', 'rebuild-index'],
|
|
235
|
+
timeoutMs: 300_000,
|
|
236
|
+
env: ONNX_THREAD_CAP,
|
|
237
|
+
});
|
|
241
238
|
} else {
|
|
242
|
-
log('SKIP
|
|
239
|
+
log('SKIP hnsw-rebuild (CLI not found)');
|
|
243
240
|
}
|
|
244
241
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
242
|
+
return plan;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function main() {
|
|
246
|
+
const startTime = Date.now();
|
|
247
|
+
const plan = buildStepPlan();
|
|
248
|
+
|
|
249
|
+
let ranAny = false;
|
|
250
|
+
let hnswAttempted = false;
|
|
250
251
|
let hnswOk = true;
|
|
251
|
-
|
|
252
|
-
|
|
252
|
+
|
|
253
|
+
for (const step of plan) {
|
|
254
|
+
const gate = decideStepGate(step.name, projectRoot);
|
|
255
|
+
if (gate.skip) {
|
|
256
|
+
log(`SKIP ${step.name} (${gate.reason})`);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
log(`RUN ${step.name} (${gate.reason})`);
|
|
260
|
+
if (step.name === 'hnsw-rebuild') hnswAttempted = true;
|
|
261
|
+
const ok = await runStep(step.name, step.cmd, step.args, step.timeoutMs, step.env || null);
|
|
253
262
|
if (ok) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
263
|
+
// POST-run fingerprint: re-compute to capture any state mutated by
|
|
264
|
+
// this step (e.g. build-embeddings bumping memory.db mtime). Saving
|
|
265
|
+
// the POST value lets next session correctly compare against the
|
|
266
|
+
// stable post-step state.
|
|
267
|
+
try {
|
|
268
|
+
const post = computeStepFingerprint(step.name, projectRoot);
|
|
269
|
+
if (!saveStepFingerprint(step.name, projectRoot, post)) {
|
|
270
|
+
log(`WARN ${step.name} fingerprint save failed (next session will re-run)`);
|
|
271
|
+
}
|
|
272
|
+
} catch (err) {
|
|
273
|
+
const msg = (err && err.message ? err.message.split('\n')[0] : 'unknown');
|
|
274
|
+
log(`WARN ${step.name} fingerprint compute failed: ${msg}`);
|
|
261
275
|
}
|
|
262
|
-
|
|
276
|
+
ranAny = true;
|
|
277
|
+
} else if (step.name === 'hnsw-rebuild') {
|
|
263
278
|
hnswOk = false;
|
|
264
279
|
}
|
|
265
|
-
} else {
|
|
266
|
-
log('SKIP hnsw-rebuild (CLI not found)');
|
|
267
280
|
}
|
|
268
281
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
} else {
|
|
280
|
-
log('WARN fingerprint save failed (next session will re-run chain)');
|
|
282
|
+
// hnsw-rebuild post-check: sidecar must physically exist after the step
|
|
283
|
+
// ran successfully. Missing sidecar means cold-start memory search will
|
|
284
|
+
// silently rebuild from SQL on every consumer process — the regression
|
|
285
|
+
// this guard exists to surface (#854). Only meaningful when we actually
|
|
286
|
+
// tried to rebuild.
|
|
287
|
+
if (hnswAttempted && hnswOk) {
|
|
288
|
+
const sidecar = hnswIndexPath(projectRoot);
|
|
289
|
+
if (!existsSync(sidecar)) {
|
|
290
|
+
log(`FAIL hnsw-rebuild post-check: sidecar missing at ${sidecar}`);
|
|
291
|
+
hnswOk = false;
|
|
281
292
|
}
|
|
282
293
|
}
|
|
283
294
|
|
|
295
|
+
// Always tidy up the v1 fingerprint file from 4.9.7 — even on all-skip
|
|
296
|
+
// sessions, otherwise the orphan survives indefinitely.
|
|
297
|
+
cleanupLegacyFingerprint(projectRoot);
|
|
298
|
+
|
|
299
|
+
const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
300
|
+
log(ranAny
|
|
301
|
+
? `Sequential indexing chain complete (${totalElapsed}s)`
|
|
302
|
+
: `Sequential indexing chain skipped — all steps gated unchanged (${totalElapsed}s)`);
|
|
303
|
+
|
|
284
304
|
if (!hnswOk) process.exit(1);
|
|
285
305
|
}
|
|
286
306
|
|
|
Binary file
|
package/bin/prompt-hook.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execFileSync } from 'child_process';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
3
4
|
import { resolve } from 'path';
|
|
4
5
|
|
|
5
6
|
// Read stdin JSON from Claude Code
|
|
@@ -70,6 +71,19 @@ if (TEST_HINTS.test(lower)) {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
// #867 — surface post-install restart notice. File is written by
|
|
75
|
+
// scripts/post-install-notice.mjs and cleared by the SessionStart launcher
|
|
76
|
+
// once the running moflo matches the file's version, so the file's
|
|
77
|
+
// existence at prompt-time is itself the "still needs restart" signal.
|
|
78
|
+
var restartNotice = '';
|
|
79
|
+
try {
|
|
80
|
+
var pendingPath = resolve(projectDir, '.moflo', 'restart-pending.json');
|
|
81
|
+
var pending = JSON.parse(readFileSync(pendingPath, 'utf-8'));
|
|
82
|
+
if (pending && typeof pending.message === 'string' && pending.message.length > 0) {
|
|
83
|
+
restartNotice = pending.message;
|
|
84
|
+
}
|
|
85
|
+
} catch (e) { /* ENOENT or malformed — silent fast-path */ }
|
|
86
|
+
|
|
87
|
+
var parts = [restartNotice, output.trim(), nsHint].filter(Boolean);
|
|
88
|
+
if (parts.length) process.stdout.write(parts.join('\n\n') + '\n');
|
|
75
89
|
process.exit(0);
|
|
@@ -14,6 +14,17 @@ import { fileURLToPath } from 'url';
|
|
|
14
14
|
import { mofloDir } from './lib/moflo-paths.mjs';
|
|
15
15
|
import { repairMemoryDbIfCorrupt } from './lib/db-repair.mjs';
|
|
16
16
|
|
|
17
|
+
// Headless skip (#860). The daemon's headless workers spawn `claude --print`
|
|
18
|
+
// with CLAUDE_CODE_HEADLESS=true (see src/cli/services/headless-worker-
|
|
19
|
+
// executor.ts). Each spawned Claude inherits SessionStart hooks, which
|
|
20
|
+
// would re-enter this launcher and fork the indexer chain — bumping
|
|
21
|
+
// memory.db mtime, invalidating the 4.9.7 fingerprint gate, and pegging
|
|
22
|
+
// CPU on the daemon's 15-min worker cycle.
|
|
23
|
+
if (process.env.CLAUDE_CODE_HEADLESS === 'true' || process.env.CLAUDE_CODE_HEADLESS === '1') {
|
|
24
|
+
emitWarning('session-start-launcher skipped (CLAUDE_CODE_HEADLESS=true)');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
29
|
|
|
19
30
|
// Single source of truth for the launcher's guidance-mirror header. Section 3
|
|
@@ -206,6 +217,23 @@ try {
|
|
|
206
217
|
// own errors if the DB is still broken.
|
|
207
218
|
}
|
|
208
219
|
|
|
220
|
+
// ── 0d. Clear post-install restart notice when version is current (#867) ───
|
|
221
|
+
// scripts/post-install-notice.mjs drops `.moflo/restart-pending.json` on every
|
|
222
|
+
// `npm install moflo`. The UserPromptSubmit hook surfaces it on every prompt
|
|
223
|
+
// until cleared, so this session only sees the message between install and
|
|
224
|
+
// the FIRST restart that actually picks up the new bits.
|
|
225
|
+
try {
|
|
226
|
+
const pendingPath = join(mofloDir(projectRoot), 'restart-pending.json');
|
|
227
|
+
const pkgPath = resolve(projectRoot, 'node_modules/moflo/package.json');
|
|
228
|
+
const pending = JSON.parse(readFileSync(pendingPath, 'utf-8'));
|
|
229
|
+
const installedVersion = JSON.parse(readFileSync(pkgPath, 'utf-8')).version;
|
|
230
|
+
if (pending && typeof pending.version === 'string' && pending.version === installedVersion) {
|
|
231
|
+
unlinkSync(pendingPath);
|
|
232
|
+
try { unlinkSync(join(mofloDir(projectRoot), 'last-install-banner.json')); } catch { /* tracker may not exist */ }
|
|
233
|
+
emitMutation('cleared post-install restart notice', `${installedVersion} now running`);
|
|
234
|
+
}
|
|
235
|
+
} catch { /* file missing or malformed — silent fast-path */ }
|
|
236
|
+
|
|
209
237
|
// ── 1. Helper: fire-and-forget a background process ─────────────────────────
|
|
210
238
|
function fireAndForget(cmd, args, label) {
|
|
211
239
|
try {
|
|
@@ -1095,11 +1123,20 @@ if (mutationCount > 0) {
|
|
|
1095
1123
|
}
|
|
1096
1124
|
|
|
1097
1125
|
// ── 4. Spawn background tasks ───────────────────────────────────────────────
|
|
1098
|
-
const localCli = resolve(projectRoot, 'node_modules/moflo/bin/cli.js');
|
|
1099
|
-
const hasLocalCli = existsSync(localCli);
|
|
1100
1126
|
|
|
1101
|
-
// hooks.mjs session-start (daemon, indexer, pretrain, HNSW, neural patterns)
|
|
1102
|
-
|
|
1127
|
+
// hooks.mjs session-start (daemon, indexer, pretrain, HNSW, neural patterns).
|
|
1128
|
+
// Prefer the npm-bin copy over the `.claude/scripts/` mirror (#866). The mirror
|
|
1129
|
+
// is a derived sync that races the launcher's section-3 file copies during the
|
|
1130
|
+
// very upgrade session — spawning the still-stale `.claude/scripts/hooks.mjs`
|
|
1131
|
+
// then chaining `__dirname/index-all.mjs` produces an orphan running pre-
|
|
1132
|
+
// upgrade argv (e.g. `rebuild-index --force` after #859 had already dropped
|
|
1133
|
+
// it). The bin/ copy is updated atomically by `npm install moflo` (single-
|
|
1134
|
+
// step), so spawning from there guarantees the running hook code matches the
|
|
1135
|
+
// installed package. Falls back to the mirror only when the package copy is
|
|
1136
|
+
// unresolvable (development / symlinked installs).
|
|
1137
|
+
const hooksPkg = resolve(projectRoot, 'node_modules/moflo/bin/hooks.mjs');
|
|
1138
|
+
const hooksMirror = resolve(projectRoot, '.claude/scripts/hooks.mjs');
|
|
1139
|
+
const hooksScript = existsSync(hooksPkg) ? hooksPkg : hooksMirror;
|
|
1103
1140
|
if (existsSync(hooksScript)) {
|
|
1104
1141
|
fireAndForget('node', [hooksScript, 'session-start'], 'hooks session-start');
|
|
1105
1142
|
}
|
package/dist/src/cli/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.9",
|
|
4
4
|
"description": "MoFlo — AI agent orchestration for Claude Code. A standalone, opinionated toolkit with semantic memory, learned routing, gates, spells, and the /flo issue-execution skill.",
|
|
5
5
|
"main": "dist/src/cli/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
82
82
|
"@typescript-eslint/parser": "^7.18.0",
|
|
83
83
|
"eslint": "^8.0.0",
|
|
84
|
-
"moflo": "^4.9.
|
|
84
|
+
"moflo": "^4.9.8",
|
|
85
85
|
"tsx": "^4.21.0",
|
|
86
86
|
"typescript": "^5.9.3",
|
|
87
87
|
"vitest": "^4.0.0"
|