Skip to content
LogoLogo

Configuration and tooling

Sources: VIP-30, VIP-32, VIP-34, VIP-38, VIP-39. Declaration output (VIP-27) is in Functions.

Config files

{ "extends": "./tsconfig.base.json", "compilerOptions": { "strict": true }, "vibe": { "effectGlobals": false, "effectModules": false } }

-p <file> reads that file, even when another config is beside it. -p <dir> reads the first of these that exists in that directory:

  1. vibe.config.json
  2. tsconfig.json
  3. jsconfig.json

Without -p, search the current directory for the CLI, or the source file’s directory for the language server, then each parent. Stop at the nearest directory containing any of the three names; apply the order above there. Explicit file arguments bypass config discovery, as in tsc; combining them with -p is an error.

# repo/vibe.config.json and repo/packages/a/tsconfig.json exist
cd repo/packages/a
vibelang                       # packages/a/tsconfig.json
vibelang -p ../../vibe.config.json # the explicitly named file

vibe.config.json accepts every tsconfig.json field, extends included; jsconfig.json has TypeScript’s implied defaults. Ordinary relative paths, including relative extends entries, resolve from the config that declares them. A leading ${configDir} in a supported path field instead expands from the selected project config’s directory, even when that field is inherited. Fields retain their TypeScript meaning with one deliberate file-kind exception: moduleDetection applies ordinarily to .ts and .js, but every source .vibe file is an external module before binding and checking, independently of imports and prelude switches (Modules and the top level). None of its values enables shared script globals in .vibe; ordinary module emission, host rules and incompatible-option diagnostics still apply. The added top-level "vibe" section holds VibeLang's options in any of the three files. Plain tsc rejects unknown compilerOptions (TS5023) but ignores unknown top-level keys, so a repository's own TypeScript tooling keeps working.

For example, TypeScript’s ${configDir} substitution keeps the inherited output directory local to the selected project while the ordinary source path stays relative to the base:

// configs/base.json
{ "compilerOptions": { "outDir": "${configDir}/dist", "rootDir": "./src" } }
// app/tsconfig.json
{ "extends": "../configs/base.json" }
// Effective paths: outDir is app/dist; rootDir is configs/src.

.vibe and .vibex are supported source extensions: files may list them, and include, exclude and the default **/* treat .vibe as they treat .ts and .vibex as they treat .tsx (below). A compatibility sidecar is an emit output: discovery selects the source ahead of its sidecar, while an explicit files input keeps native overwrite protection (TS5055). The required sidecar write is independent of noEmit and declaration: false (VIP-27, amended).

{ "include": ["src"] }

This includes src/user.vibe without also checking its stale src/user.d.vibe.ts. With vibe.sidecarDir, a sidecar under that directory whose source exists at the mirrored path is left out the same way. So is a module body's declaration written beside a sidecar, such as src/main.vibe.parse.d.ts, while its source declares that body (VIP-40).

Declaration sidecars are emit outputs like .d.ts. Under VIP-27’s native overwrite amendment, a sidecar found through include yields to its source and emit may overwrite it. Listing both the source and its sidecar explicitly in files keeps the sidecar as an input: emit reports TS5055, exits 1, and leaves its bytes untouched. The same rule applies in the vibe.sidecarDir directory.

A non-relative import reaching a known compatibility declaration uses its available mapped source only when TypeScript's final resolved path is outside node_modules. This includes package exports, paths and package #imports. Apply realpath and preserveSymlinks first; a retained logical dependency path keeps its declaration and native module identity. The rule also applies to the effective sidecarDir map (VIP-27's dated amendment). Explicitly selected declarations remain inputs subject to the ordinary overwrite guard.

Compatibility declarations preserve the actual module selected by a source import. An explicit ./tag.vibe remains that module even beside tag.ts, including re-exports and inferred import types, in either layout (VIP-27).

Relative imports in relocated sidecars resolve from the actual output location, including references to native TypeScript modules and mirrored VibeLang declarations. Another project's checker does not inherit the producer's rootDirs. Workspace package imports use the owning config to keep a relocated sidecar and its source as one module; published packages retain declarations. Migration setup removes superseded adjacent outputs in the same layout change and preserves existing scalar path inheritance and virtual roots.

Sidecar placement follows TypeScript 5.9's declaration layout, with vibe.sidecarDir taking the place of declarationDir: an explicit rootDir supplies the source root, a composite project defaults to its config directory, and otherwise the root is the common source directory. An imported source outside the compiling config's directory is emitted by that compiling project, under its own sidecarDir if set or beside the source otherwise; its nearest config is irrelevant. An explicit rootDir that excludes the source reports TS6059. Through a project reference, only the owning project writes the source's sidecar under its own sidecarDir. Migration setup gives configs compiling outside sources their own sidecarDir unless references already keep those sources with their owner, and pairs the output directory with the source root in rootDirs. Config-relative mirrored-path examples assume rootDir: "."; root inference follows this rule.

// base.json
{ "vibe": { "effectGlobals": false, "severities": { "EK0801": "off" } } }
// child config
{ "extends": "./base.json", "vibe": { "effectModules": false } }
// effective vibe section
{ "effectGlobals": false, "effectModules": false, "severities": { "EK0801": "off" } }

extends inherits vibe key by key: child keys win and missing keys come from the base. severities merges entry by diagnostic code (VIP-30). An extends array merges bases left to right, later entries winning, before applying the child, as in TypeScript.

Write VibeLang settings in the root vibe object (VIP-30):

KeyMeaning
vibe.effectGlobalsBoolean; default true. Put Effect's functions in scope (VIP-6); false turns the function globals off. Qualified calls such as Effect.sleep remain available when vibe.effectModules is true.
vibe.effectModulesBoolean; default true. Put Effect's modules in scope (VIP-14); false requires imports.
vibe.sidecarDirString; absent by default. Move compatibility declarations from beside their sources to the mirrored directory described above (VIP-27). Relative paths use the declaring config; a leading ${configDir} uses the selected config. An empty string names the declaring directory and overrides inheritance; only absence inherits.
vibe.severitiesObject; default {}. Maps VibeLang's own warning codes and assigned imported EK38xx codes to "error", "warning", "suggestion" or "off" (VIP-30, VIP-34, VIP-38).

For example, "sidecarDir": ".vibe/declarations" with "rootDir": "." uses "rootDirs": [".", ".vibe/declarations"] for stock TypeScript resolution. These settings have no runtime translation. With both prelude switches false, write explicit imports as in TypeScript:

import { Effect } from "effect"
Effect.runSync(Effect.succeed(42)) // 42

Diagnostic settings

{ "vibe": { "severities": { "EK0801": "error", "EK4609": "off", "EK38100": "warning" } } }

Keys are whole printed codes: EK3801 and EK38100 identify different rules. Only VibeLang's own warnings and assigned imported lint codes are configurable. A VibeLang error code, an interim EK92xx code, even one printed as a warning, or an unknown code is a config error (EK3006), including when set to "error" (VIP-30, VIP-38). Thus EK0801 is configurable; EK4701 is not.

CodeDefaultTriggerRepair
EK3006ErrorA disallowed code or a severity entry whose value is not one of the four strings, whatever its JSON typeRemove the code or use a permitted value.
EK3007ErrorAn unknown vibe key or a wrong-typed value other than a severity entry, including a non-object severitiesRemove the key or supply its declared type.

Merge extends before validating each effective severity entry. An invalid entry reports EK3006 and contributes no override; valid entries and independent options still apply. An invalid child entry displaces its inherited entry without restoring it: fall back to the Effect language-service setting where applicable, then the default. Other inherited codes remain. Configuration errors keep their ordinary exit and emit consequences (VIP-30).

// Effective-entry recovery: base.json
{ "vibe": { "effectGlobals": false, "severities": { "EK0801": "error", "EK4609": "off" } } }
// child: EK3006 on "bad"; EK0801 uses its default; EK4609 and EK0508 stay off.
{ "extends": "./base.json", "vibe": { "effectModules": false, "severities": { "EK0801": "bad", "EK0508": "off" } } }
// Other invalid configurations:
{ "vibe": { "effectGlobal": false } } // EK3007: use effectGlobals
{ "vibe": { "effectModules": "no" } } // EK3007: use a boolean
{ "vibe": { "severities": { "EK0801": 1 } } } // EK3006: use a severity string
{ "vibe": { "severities": [] } } // EK3007: use an object
{ "vibe": { "severities": { "EK4701": "error" } } } // EK3006: remove the entry

For VibeLang's own diagnostics, "error" fails both vibelang and vibelang lint and participates in noEmitOnError; "warning" prints without changing exit status; "suggestion" is shown only by the language server (VIP-34). For every code, "off" reports nothing. To fail CI on an own warning, set it to "error". Imported diagnostics instead use the Effect lint host rules below.

The retired trustedPackages key is ignored in vibe and supported legacy sections, with warning EK3010: remove it. Npm functions follow the same foreign-callable contract as local TypeScript, including unknown without @throws; use a signature contract or an explicit Effect.try bridge (VIP-4, amended).

Module resolution is as in TypeScript, with .vibe and .vibex beside .ts and .tsx: NodeNext ./user.js can resolve to user.vibe. A written ./x.ts keeps native substitutions, including x.tsx and x.d.ts, but never falls back to VibeLang source; no native candidate means TS2307. Migration never writes moduleSuffixes.

Written ./x.vibe and ./x.vibex imports follow the .ts/.tsx twin's source-extension checks, including TS5097 for a value import without allowImportingTsExtensions or rewriteRelativeImportExtensions. Native option restrictions apply. rewriteRelativeImportExtensions rewrites JavaScript specifiers, not declaration specifiers (VIP-27).

For TS2307 on a stale relative .ts name with a migrated sibling, vibelang --fix repairs owned VibeLang sources to .vibe/.vibex with rewriteRelativeImportExtensions, otherwise the runtime .js (.jsx for JSX-preserving output). It refuses changes that cannot preserve types, behavior and declarations; without a migrated sibling the import stays unresolved.

Declaration import spelling

Declaration import spelling follows TypeScript's source and tsconfig rules (Declaration output). declarationImportStyle is retired: root vibelang.declarationImportStyle and compilerOptions.vibelang.declarationImportStyle are ignored with nonfatal deprecation warning EK3011 for every value, including invalid values (VIP-30, amended). No replacement option or spelling codemod is needed.

A published package carries JavaScript and ordinary .d.ts files. Compatibility *.d.vibe.ts and *.d.vibex.ts sidecars serve source tools and do not ship; published type exports point at the build’s ordinary declarations.

Legacy severity maps

Both historical paths, root vibelang.diagnostics and compilerOptions.vibelang.diagnostics, are recognized in vibe.config.json, tsconfig.json, jsconfig.json and extended configs. Each maps supported printed legacy diagnostic codes to "error", "warning" or "off"; "suggestion" is a value of the modern map, not a legacy one. Legacy replacement and inheritance behavior remains: a child without a legacy section inherits the effective base section, and adding only modern settings leaves it active. An explicit child legacy section replaces the inherited legacy section as a whole, rather than merging diagnostic entries; even an empty object or null clears the inherited legacy settings. The modern vibe.severities map still merges entry by code.

Until migrated, a supported legacy setting still controls its legacy diagnostic unless an effective modern entry for the same diagnostic or its explicitly mapped port overrides it. A reused numeric spelling alone does not establish that identity: newly assigned release errors cannot be disabled through an old map. A release-only code such as EK0801 is not valid in a legacy map.

Each legacy map reports migration warning EK3009. Setting EK3009 to "off" hides only this warning; it does not validate, migrate or disable the supported legacy settings.

// A supported legacy setting keeps applying, while its migration warning is hidden.
{ "vibelang": { "diagnostics": { /* old floating-Effect code */ "EK4801": "off" } }, "vibe": { "severities": { "EK3009": "off" } } }
// The explicit modern floatingEffect port takes precedence over the old setting:
{ "vibelang": { "diagnostics": { /* old floating-Effect code */ "EK4801": "off" } }, "vibe": { "severities": { "EK3801": "error" } } }
// Alternative historical location, in a separate config:
{ "compilerOptions": { "vibelang": { "diagnostics": { /* old floating-Effect code */ "EK4801": "warning" } } } }

vibelang --fix moves each eligible map to root vibe.severities, including in extended configs inside the repository, preserving JSONC comments, unrelated settings and existing modern entries; an extended config under node_modules is reported, not edited. It also removes the retired declarationSidecars setting from those owned configs. A rewrite that can't preserve types, behavior and declaration output refuses at that site. A map containing obsolete or nonconfigurable codes remains in place until those entries are resolved. Plain tsc ignores the custom root keys, but reports TS5023 for compilerOptions.vibelang; VibeLang's legacy compatibility does not change that.

JSX source files: .vibex

A .vibex file is a .vibe file read with TypeScript's JSX language variant (VIP-39). Every JSX production, and its scanning, checking and emit, is .tsx's; every VibeLang production is .vibe's; where they compete for < in expression position, .tsx's reading wins. The source-visible differences from .vibe are VIP-39's clash rows 1–5 and 39: write x as T for <T>x, <T,> for a generic arrow (effect <T,>, async <T,>, catch <E,>), and a space in a < /re/.

The jsx options and pragmas mean what they mean for .tsx, read through the configuration discovery above. A project without jsx gets TS17004 at an element and TS6142 at an import of a .vibex file. Under react, react-jsx and react-jsxdev, TypeScript transforms the element; under preserve and react-native the element stays in the output for the framework's compiler.

Relative specifiers resolve as follows, after TypeScript's exports, imports, paths, moduleSuffixes and resolution-mode rules select a lookup; NodeNext still requires extensions where .ts and .tsx do:

SpecifierTries, in order
./App, ./App.jsApp.ts, App.tsx, App.vibe, App.vibex, App.d.ts, then JavaScript
./App.jsxApp.tsx, App.ts, App.vibex, App.vibe, App.d.ts, App.jsx, App.js
./App.vibeApp.vibe, App.vibex, App.d.ts
./App.vibexApp.vibex, App.vibe, App.d.ts

TypeScript spellings with VibeLang forms

These built-in warnings suggest an accepted VibeLang form without changing the meaning of TypeScript-style source. They are warnings by default and use vibe.severities, independently of the imported Effect lint table. A quick fix is offered only when it preserves evaluation order, coldness, types, comments, and declaration output; otherwise the diagnostic explains why it has no automatic fix. A warning does not itself rewrite code, and a warning-only run exits zero. Untagged Data.Error has no equivalent tagged declaration: keep its library form rather than adding a tag. An unsupported legacy repair is retired, rather than treated as a language requirement.

In the EK4834, EK4835 and EK4836 rows, the call is the direct operand of bare try in effect code. Parentheses and erased TypeScript expression forms (as, satisfies, !) around it still leave it the direct operand; the call is recognized by its resolved symbol, so an import alias counts and a local binding that shadows Effect does not. An operand of fork in any form, of join, of try.all, try.race, try.any or try.allSettled or one of its elements, an argument of an Effect API, the head of a catch, and a stored or returned Effect are not direct operands, and these rows do not warn on them: fork Effect.catch(e, h) keeps recovery in the child (fork and join), and fork Effect.fail(e) fails the child, not the caller.

CodeTriggerRepair
EK4830A class written with Data.TaggedError("Name")Use error Name { … } only when its tag and class behavior are preserved.
EK4831A class written with Context.ServiceUse service Name { … }, preserving the key with static readonly key.
EK4832A const holding Schema.Struct({ … })Use schema Name { … } where the field mapping and associated type are preserved. In a declaration-emitting project, preserve an own type written as Schema.Schema.Type<typeof Name>, private or exported, because a schema declaration prints typeof Name.Type.
EK4833A written Effect.Effect<A, E, R> type, except an interface's extends element, which A!E!R cannot spellUse the equivalent A!E!R spelling, preserving never slots and required parentheses.
EK4834Effect.succeed(v) or Effect.fail(e) as the direct operand of bare try in effect codeUse the value v or throw e.
EK4835Effect.catch, Effect.catchTag, or Effect.catchTags as the direct operand of bare try in effect codeUse a catch effect handler, with match on the failure for tag cases and a typed throw for unmatched failures.
EK4836Effect.provide or Effect.provideService as the direct operand of bare try in effect codeUse a provide statement with Layers or Key = value, preserving operand evaluation order.
EK4837A class written with Schema.TaggedErrorUse schema error Name { … } when the schema and tag are preserved; an ordinary error would lose the schema.
EK4839An Effect Match chain with a supported finisher, or a direct Option.match(value, handlers) callUse match, or a function returning match for Match.type, only for patterns and handlers whose tests and results are preserved. Direct Option.match keeps subject evaluation, handler construction, property reads and the selected callback in their original order. The fix refuses a choice returning a program or comments between its arguments; Match chains also keep refusals for unsupported patterns, handlers or return types.
EK4841A schema const immediately followed by its matching typeof Name.Type aliasUse the corresponding schema Name = … declaration where the schema mapping preserves both exports.
EK4842An Effect module imported under a conflicting JavaScript global nameRename that import and its resolved uses, such as Array as Arr, only where emitted public types keep their meaning.

The EK4833 advisory treats a custom qualified alias named Effect as an equivalent spelling only when its resolved type is Effect's own Effect interface and its written arguments match that type's success, error and requirements slots. An omitted slot must resolve to never. An exact forwarding alias may qualify; aliases whose parameter order, defaults, wrapped slots, added requirements or union branches would change the repaired type retain their spelling without this advisory.

For example, the EK4830 repair may replace this class with error Missing { id: string } while preserving Fail(Missing { id: "x" }):

VibeLang
// compiler-gap: pending VIP-1, VIP-13
class Missing extends Data.TaggedError("Missing")<{ readonly id: string }> {}
effect function program() { throw new Missing({ id: "x" }) }
Effect
// TypeScript-form advisory reference
import { Data, Effect, Exit } from "effect"
class Missing extends Data.TaggedError("Missing")<{ readonly id: string }> {}
const result = Effect.runSyncExit(Effect.fail(new Missing({ id: "x" })))
if (Exit.isSuccess(result)) throw new Error("expected a failure")
const reason = result.cause.reasons[0]
if (reason._tag !== "Fail") throw new Error("expected a typed failure")
console.log(JSON.stringify([reason._tag, reason.error._tag, reason.error.id]))

Only the first line of demo warns. The other three run their Effect in a child fiber or as an element, where the suggested form would change meaning: a throw in place of fork Effect.fail("boom") would fail demo itself, while the child's failure lets demo go on and inspect it.

VibeLang
// compiler-gap: pending VIP-34
effect function demo(e: number!string, h: (x: string) => number!!) {
  const one = try Effect.succeed(1)              // EK4834
  const child = fork Effect.catch(e, h)          // no EK4835: recovery stays in the child
  const failedChild = fork Effect.fail("boom")   // no EK4834: it fails the child, not demo
  const both = try.all [Effect.succeed(one), e]  // no EK4834 on an element
  return [child, failedChild, both]
}
Effect
// TypeScript-form advisory reference
import { Effect } from "effect"
function demo(e: Effect.Effect<number, string>, h: (x: string) => Effect.Effect<number>) {
  return Effect.gen(function* () {
    const one = yield* Effect.succeed(1)
    const child = yield* Effect.forkChild(Effect.catch(e, h))
    const failedChild = yield* Effect.forkChild(Effect.fail("boom"))
    const both = yield* Effect.all([Effect.succeed(one), e], { concurrency: "unbounded" })
    return [child, failedChild, both]
  })
}

The Effect version

During alpha, VibeLang pins exactly one Effect version, and the pin moves to each new Effect release. The pinned dependency artifact is exactly effect@4.1.1; the 4.1 minimum means the pin never moves below 4.1, not that other 4.1.x versions are supported (VIP-32, amended). Every Effect TypeScript mapping in this specification, and the Exit guarantee of Runtime guarantees, is relative to the pin. The pin controls variables during alpha; it is not the long-term plan, and supported version ranges get their own VIP after alpha.

A migration upgrades the target repository to the pinned Effect first, on its own: it fixes what breaks and lands that commit with green tests. Only then does it convert files to .vibe, so each breakage shows its real cause. A repository that already uses VibeLang upgrades Effect together with the VibeLang release pinned to the new Effect, because the published vibelang-runtime enforces its exact pin.

VibeLang declares an exact effect peer dependency. The compiler reads the resolved effect/package.json; any version other than 4.1.1, including 4.1.0, is EK3201 in the CLI and editor (VIP-32). For example, resolving effect@4.0.3 reports: the resolved effect is 4.0.3; VibeLang Y pins 4.1.1: upgrade both together, where Y is the VibeLang release.

Compiler and tooling requirements

Every VibeLang diagnostic says what is wrong and how to fix it, with a mechanical quick fix where possible. Appendix B assigns each VibeLang rule its EK code. Native lexical, syntax and static errors keep their TS codes, as in TypeScript, including independent errors in embedded expressions and types. A VibeLang production, placement rule, proposal early error or adopted reconciliation uses its owning VIP's code even when a TypeScript parser helper implements it; reuse an assigned code for the same rejection (VIP-34).

When emitted JavaScript needs vibelang-runtime and the package cannot resolve, report one TS2307 naming that package, never unresolved names for its generated helpers.

Fix ownership and preservation

vibelang --fix -p tsconfig.json

Every breaking change ships a codemod. One vibelang --fix run applies fixes until none applies; running it again changes nothing. A run that has not converged within its pass limit reports the remaining fixes as an error. Quick fixes and codemods use the formatter to preserve the parse under Appendix A.

A run repairs only the selected project's own files (VIP-34):

FilePermitted repair writes
Owned .vibe/.vibex program sourcesIncludes an owned source an import reaches even when exclude omits it from root discovery.
Owned configsLegacy severity maps, retired declarationSidecars, the EK3012 plugin-entry migration and the --fix-vip-only EK4802 severity override (below). Traverse extends only inside the repository and outside node_modules; report external configs without editing them.
Fresh sibling .tsOnly the platform repair, after proving preservation of run phase, captures, receiver and public types; it never overwrites an existing file.
Dependencies, typeRoots, bundled libraries, foreign .ts, .tsx, .js, .jsx, .mjs, .cjs, declarations (.d.ts, .d.vibe.ts, .d.vibex.ts), emitted output, tsBuildInfoFile, lock files and other referenced projectsNo repair writes. With -b, each project fixes its own files in build order.

A forbidden write is refused with a not-fixed explanation and no change. A rewrite that can't preserve types, behavior and declaration output refuses at that site. Scope flags select passes; --only further limits ordinary repairs.

Trailing-block migration

CodeTriggerRepair
EK1101A rejected trailing blockPass the block as the last argument: Layer.effect(Users) { … } becomes Layer.effect(Users, effect { … }); it.effect("loads") { … } becomes it.effect("loads", effect () => { … }); a plain callback becomes () => { … }.
TS2345, TS2554, TS2769, or transitional EK1023A legacy Effect-dual trailing block; EK1023 applies when appending the block type-checks with changed meaningThe versioned dual migration runs first, preserves the original data-first or curried argument position and emits no duplicate EK1101.

Use the pinned legacy source, compiler and options to determine the block's kind and argument position, never the callee's parameter type alone. Preserve runtime behavior and comments; printed comment placement and equivalent arrow layout may differ. A comment between the call and its rejected trailing block refuses the fix (VIP-11).

Parsing uses TypeScript's statement boundaries and recovery. Compatibility decoding supplies diagnosis and migration only, never an executable callback. EK1101 suppresses only the redundant missing-separator TS1005 for the same rejection; independent native errors remain. A newline before the block, an optional chain, a heritage clause and provide providers are outside this form.

Formatting and existing tools

VibeLang's formatter is the only formatter. Prettier, dprint and Oxfmt reach it through plugins, so a repository's existing formatter command covers .vibe files. It takes the repository's quote style and print width from that formatter's configuration, so .vibe and .ts files share them. It preserves the provide line restrictions (VIP-12): never break after provide, before or after a provider-separating comma, or before the block’s {. A long provider list stays on one line. The formatter neither introduces nor removes Layer.mergeAll(…); an author who writes it may break lines between its arguments (example). It preserves every line-sensitive and parenthesis-sensitive rule in Appendix A: never insert or remove a line break or parentheses that change the parse. Its tests cover each such rule, including contextual keywords, Effect type slots, bodyless errors, field and variant separators, statement boundaries, catch arrows and for concurrent heads and bodies. Under the same compiler/options, formatting preserves parsed program meaning: bindings and scopes, operator and control-flow targets, evaluation order, receivers and call syntax, ordinary runtime values and effects, coldness, failures, interruption and finalizers. It preserves inferred and declared public types, declarations and throws contracts, and diagnostic codes, categories and occurrences; diagnostic locations and quoted source follow the formatted file. Comments and their code associations survive, including JSDoc, compiler/linter/tool directives and directive-prologue meaning. An unparsable file is reported and left unchanged; formatting is idempotent. Semantic migrations and diagnostic repairs belong to vibelang --fix or editor actions.

Ordinary layout and lexical-spelling normalization may change emitted JavaScript bytes. Function.prototype.toString, generated source text, source offsets, line/column and stack presentation may reflect the formatted text. This source-text boundary never permits changes to ordinary strings/data, tagged-template raw or cooked values, regex meaning, function names or lengths, property names or directive semantics. For example, changing an escaped string into an actual use strict directive changes meaning. Regenerate compiler-generated source-map and declaration-map provenance, including sourcesContent, for the formatted file; never retain stale maps. Preservation tests compare parsed source/emit structure, runtime behavior, types/declarations, diagnostics, comments/directives and maps; arbitrary regex stripping is unsound, and a finite structural oracle is not a proof of all program equivalence. Exact-byte tests remain useful where equality is expected, without restricting advertised native formatter style settings.

Type checkers use .d.vibe.ts; linters, Knip, bundlers and test runners read .vibe. A repository's Oxlint JS-plugin rules run on .vibe files wherever applicable to the source or emitted forms. Every tool is tested against the dogfood repositories. The language server prefers an in-program ./x.vibe source to its stale adjacent or relocated x.d.vibe.ts.

Output names follow the native twin: x.vibe emits x.js and x.d.ts in outDir; x.vibex emits x.jsx under "jsx": "preserve", otherwise x.js. JavaScript source-extension rewriting requires rewriteRelativeImportExtensions. Bundler plugins use the same names (VIP-34). Ordinary declarations use the native twin's relative specifiers; compatibility sidecars preserve the selected VibeLang source identity, relocated when needed. Neither extension flag rewrites declaration specifiers; see Declaration output (VIP-27).

Transitional diagnostics

These Appendix B compatibility codes select preserving repairs; they do not run Effects or change omitted-slot or service semantics. vibelang --fix retains its safety checks and refusal rules.

CodeDefaultTriggerRepair
EK4853WarningAn omitted requirement slot formerly inferred requirementsWrite those requirements, or omit the whole return annotation to infer the contract (VIP-3).
EK4854WarningThe retired abstract-class service spellingWrite service, retaining its contract and key (VIP-9).
EK4855WarningA service's cached static readonly defaultUse an equivalent native Context.Service class, retaining its key, eager initialization, cached identity, field and type reads, mutable shape members and missing-provider failure; otherwise refuse (VIP-9).
EK4856WarningA type resolves to the retired global Effect<A, E, R> aliasWrite A!E!R where it needs no new parentheses, otherwise Effect.Effect<A, E, R>, including heritage elements (VIP-3).
EK4857ErrorAn immediate written infer channel belongs to the legacy return-slot setWrite checker-proved channel types; only on an explicitly effect-marked callable with every channel infer, omit the annotation if preservation is proved (VIP-3).

EK4856 selects the resolved legacy symbol: local and imported types named Effect retain their TypeScript meaning. Keep existing parentheses, written default slots and the Effect namespace binding; otherwise refuse. Emitted declarations already use Effect.Effect<…> and stay unchanged. Remove the alias only with a coordinated compiler, codemod and docs release. Afterwards, a module-only Effect<number> is native TS2709; an unbound name follows TypeScript resolution (VIP-3).

The remaining migrations below rewrite retired forms to their VIP form or equivalent Effect TypeScript under the same preservation rule. The legacy fork(program, options) call becomes try Effect.forkChild(program, options). The user guide's Migrating retired forms page is explanatory, not normative.

Ordinary mutable-marker migration

EK3410 rejects a retired mutable marker on an ordinary interface or object-type property or index signature, or a plain error payload. vibelang --fix, including --fix-migration-only, removes redundant member markers; a marked plain-error payload becomes an explicit compatibility class using the existing runtime error factory, preserving tagged matching. Schema fields, schema errors, schema tuples and data bodies have separate rules. A method or identifier named mutable keeps its TypeScript meaning.

Legacy data receiver methods

EK1402 rejects a legacy Effect data receiver method such as Option.some(1).map(f). vibelang --fix, including --fix-migration-only, writes the resolved module's data-first call, Option.map(Option.some(1), f). Native/own methods, namespace calls, computed access and optional chains are outside this form. No interim diagnostic is specified.

Legacy prelude shorthand

The transitional compiler accepts these shorthands without a migration diagnostic. All three --fix scopes select their resolved legacy bindings:

Written shorthandRepairAfter retirement
Bare prelude Scope typeScope.ScopeNative TS2709 for using a module namespace as a type
Same-named module export, such as Option<A> or FileSystem.of(impl)Option.Option<A> or FileSystem.FileSystem.of(impl)Native TS2709 for a namespace type or TS2339 for an absent namespace member

Written imports, local or ambient types, type parameters, already qualified exports and a namespace's own members keep their TypeScript meaning. Prelude import cleanup retains the explicit export names. These are TypeScript's errors on equivalent explicit namespace imports, not new EK codes. Remove compatibility only in a coordinated compiler, codemod and docs release; the transitional inventory records it.

Legacy fork, try-statement and wire migration

CodeRejected formvibelang --fix repair
EK1707Bare fork { … }fork effect { … }; explicit Effect operands stay unchanged (VIP-2, VIP-17).
EK0503A try statement in effect codeExplicit Effects, catch effect and native cleanup combinators (VIP-5).
EK2401The resolved retired wire intrinsicExplicit Layer.provideMerge composition replaces the rejected form; an unrelated local wire keeps its TypeScript meaning (VIP-24).

For these retirements and the mutable-marker, receiver-method and prelude-shorthand retirements above, enforcement waits until preserving codemods ship and every migration repository has zero remaining uses. This states the target, not completion of implementation or consumer migration. Every repair obeys the preservation and refusal rules.

Empty legacy trust wrappers

EK0407 retires the resolved trusted(f, []) wrapper. For each private direct synchronous call in an explicit Effect body, vibelang --fix writes an identity-mapped Effect.try and Effect.orDie bridge over f, captured at the original declaration, or refuses if preservation cannot be proved. A reviewed .ts helper with @throws {never} is another replacement. Listed-failure wrappers are outside this migration.

Legacy entry-dispatch migration

EK2601 rejects the retired side-effect markers vibelang/platform/node, vibelang/platform/bun and vibelang/platform/browser, even without main: legacy platform entry marker: run vibelang --fix and write an explicit runner.

Only a marked module is migration input. An eligible legacy startup is an exported, non-default, zero-parameter function declaration main with a body and no checker-resolved top-level runner call under the pinned legacy compiler and options. vibelang --fix removes the marker and expands eligible startup as follows:

MarkerExplicit runnerLegacy service layer
nodeNodeRuntime.runMain from @effect/platform-node/NodeRuntimeNodeServices.layer
bunThe same Node runnerBunServices.layer from @effect/platform-bun/BunServices
browserBrowserRuntime.runMain from @effect/platform-browser/BrowserRuntimeBrowserServices.layer

Use a listed service layer only when the pinned legacy compiler selected it; preserve selected experimental layers and nesting order. A marker does not by itself add a layer. Pass main() for an Effect function, otherwise Effect.sync(main), retaining the declaration's callable kind and public types. Without eligible startup, remove only the inert marker and keep any explicit runner. CommonJS does not justify retaining the retired dispatcher; ESM cannot introduce import.meta.main. Preserve entry-only execution and import-only behavior. Refuse an unavailable runner or layer, an entry test not expressible in admitted source, or any other loss of types, behavior or declarations: keep the marker and report its file, line and reason as not fixed, with a nonzero exit. No retired dispatcher or fallback runner is a migration target. A remaining marker keeps EK2601; main alone is ordinary source, gets no entry diagnostic and causes no implicit startup (VIP-26, VIP-34).

Selecting ordinary fix files

--only <files> limits ordinary --fix to the listed modules (VIP-34, amended). <files> is a comma-separated list of paths relative to the current directory, or @list.txt, a file of such paths, one per line; the flag can repeat. An empty list, or a path that is not a .vibe or .vibex module of the project, stops the invocation before it writes anything. The repairs write no other file, a config included, even when they are interrupted; the compilation after them still writes what any compilation writes, such as declaration sidecars. A fix that would edit another file is not applied. Only an error in a listed module refuses a fix: a fix that breaks an unlisted module is kept, and the compilation after --fix reports the error. The result is a partial repair and its report says so: a clean run does not verify the project. --only cannot be combined with --fix-migration-only or --fix-vip-only, because a project-wide migration changes what other modules mean. Alone, it is an unknown compiler option (TS5023).

vibelang --fix --only src/a.vibe,src/b.vibe -p tsconfig.json
vibelang --fix --only @batch.txt -p tsconfig.json
vibelang --only src/a.vibe # TS5023: add --fix

Effect's type-aware lint

vibelang lint -p tsconfig.json
{ "compilerOptions": { "plugins": [{ "name": "@effect/language-service" }] }, "vibe": { "severities": { "EK3801": "warning" } } }

The existing Effect language-service configuration in compilerOptions.plugins selects the imported effecttsgo/* rules. They run on selected .ts and .vibe inputs, keeping the pinned upstream rule/file eligibility and declaration/library exclusions. VibeLang-specific language checks remain confined to .vibe (VIP-38).

Compiler builds, the language server and vibelang lint use the same activation and selection: a resolved plugin entry named @effect/language-service enables the rules; diagnostics: false or diagnosticSeverity: null disables its diagnostics. Inheritance and native options follow the pinned upstream. A vibe.severities entry never re-enables disabled diagnostics. Until the severity-key activation migration retires the legacy shortcut, an assigned EK38xx key in a config without a resolved plugin entry still activates the whole default plugin, as the legacy release did, and reports EK3012; after that retirement, such a key alone activates nothing. Once enabled, severity resolves from vibe.severities, then the repository's Effect language-service configuration, then the rule's default (VIP-38). An override can enable a default-off rule; every assigned imported code accepts all four severity values, even "off" for a default-error rule.

Reporting and exit status

HostImported diagnostic behavior
CompilerWarnings affect exit status and noEmitOnError unless ignoreEffectWarningsInTscExitCode is true; errors use ignoreEffectErrorsInTscExitCode. Suggestions print when includeSuggestionsInTsc is true and affect exit/emission only when ignoreEffectSuggestionsInTscExitCode is false. Ignore options do not hide printed diagnostics.
Language serverUses the same rule selection and categories, with the upstream options for that host.
vibelang lintEmits no files, fails on errors, and exits 0 for warnings or suggestions alone. Compiler *InTscExitCode options do not apply. It accepts compiler options and files only, never an Oxlint config or lint-specific flags.

Pinned defaults are diagnostics: true, includeSuggestionsInTsc: true, ignoreEffectErrorsInTscExitCode: false, ignoreEffectWarningsInTscExitCode: false and ignoreEffectSuggestionsInTscExitCode: true. Severity overrides change categories, not these options. The separate Oxlint integration keeps its own configuration (VIP-38).

Severity-key activation migration

EK3012 is a migration warning when a resolved config has an assigned EK38xx severity key, "off" included, without a resolved Effect plugin entry: an imported lint severity needs an explicit Effect plugin entry; run vibelang --fix. An existing resolved plugin entry named @effect/language-service, including a native disable setting, prevents this migration. An unrelated plugin does not prevent it.

Ordinary --fix, --fix-migration-only and --fix-vip-only append { "name": "@effect/language-service" } to the selected project's own compilerOptions.plugins. Copy inherited plugin entries before appending; never edit an external base. Retain explicit disables, severities, JSONC comments and unrelated settings under the ownership and preservation rules. Selected-file --fix --only writes no configs and cannot apply this repair.

The legacy shortcut activated the whole default plugin even for an "off" key. Add no per-rule suppression or ignore flags: the plain entry must preserve diagnostic inclusion, categories, exit status and emission, except for EK3012 itself. Refuse an unprovable replacement. With the entry present, migration is idempotent and reports no EK3012. Retire implicit severity-key activation only when this migration is available and verified against the legacy release (VIP-38).

VibeLang warnings

The following own warnings default to "warning"; they are separate from imported rules and the TypeScript-form advisories. They preserve the underlying behavior. An effect { … } expression statement builds and discards an unrun Effect; imported floatingEffect reports it as EK3801, as in TypeScript.

WarningCodeSuggested fix
A plain catch handler returns an unrun Effect (VIP-5)EK0508catch effect (e) => try …
A plain call in effect code infers an Effect type argument outside a declared Effect position (Inferred Effect arguments; VIP-1, amended)EK4802The editor offers try for a direct Effect-valued argument when its written failure and requirement contract accepts it, or explicit type arguments to keep the program. Batch --fix chooses only try and refuses an edit that adds an error.
Imported lint severities have no resolved Effect plugin entryEK3012Run vibelang --fix to preserve legacy plugin activation explicitly.
A legacy severity map remains in the configEK3009Run vibelang --fix after resolving obsolete entries.
The retired trustedPackages key is ignored (VIP-4, amended 2026-11-02)EK3010Remove the key; npm functions follow the ordinary foreign-callable contract.
A retired declarationImportStyle setting is ignored, for every value (VIP-30)EK3011Remove the setting; declaration spelling follows the native TypeScript twin.
using in effect codeEK0801Use acquireDisposable inside scoped.
An extractor's argument count cannot match its extraction listEK0703Use a bare class test or the matching number of subpatterns.
await on a Fiber (VIP-5)EK4609await runPromise(Fiber.join(fiber)) in async code; join fiber in effect code
await on a Deferred (VIP-5)EK4610await runPromise(Deferred.await(deferred)) in async code; try Deferred.await(deferred) in effect code

Imported code inventory

The imported rules are pinned by UPSTREAM to @effect/tsgo@0.57.0, commit 999942fa41462eb46dfcb2d718e7671db8e14156 (VIP-38). The generated table below and lint-codes.json assign one stable code per rule, even for a rule with several upstream messages. CLI and editor print the code and rule name, for example EK3801 floatingEffect: ….

Keep EK3801 for floatingEffect; the initial allocation sorts all remaining rule names from ordinal 2. Future additions append in sorted order after the highest reserved ordinal; removed rules keep their slots and a rule's code never changes once assigned (VIP-38). Codes have the prefix EK38 and a decimal ordinal of at least two digits: EK3899 is followed by EK38100. This pin has 130 rules. Assignments live in docs/tools/lint-code-assignments.json.

A VIP-32 upstream bump runs sh tsc/internal/effecttsgo/sync.sh <tag> from typescript/, then node docs/tools/lint-codes.mjs --write regenerates the inventory and tables. Defaults are in the generated table; severity resolution is defined above.

Imported rules from @effect/tsgo@0.57.0 (commit 999942fa41462eb46dfcb2d718e7671db8e14156).

CodeRuleVIPChapterDefault severity
EK3802effecttsgo/abortControllerInEffect38Toolingsuggestion
EK3803effecttsgo/acquireReleaseDisposable38Toolingsuggestion
EK3804effecttsgo/allOfMapToForEach38Toolingsuggestion
EK3805effecttsgo/anyUnknownInErrorContext38Toolingoff
EK3806effecttsgo/apiStabilityLeak38Toolingoff
EK3807effecttsgo/asyncFunction38Toolingoff
EK3808effecttsgo/catchAllTagDispatchToCatchTag38Toolingsuggestion
EK3809effecttsgo/catchAllToMapError38Toolingsuggestion
EK3810effecttsgo/catchChainToFirstSuccessOf38Toolingsuggestion
EK3811effecttsgo/catchConditionalRefailToCatchIf38Toolingsuggestion
EK3812effecttsgo/catchDieToOrDie38Toolingsuggestion
EK3813effecttsgo/catchIfTagToCatchTag38Toolingsuggestion
EK3814effecttsgo/catchRefailToTapError38Toolingsuggestion
EK3815effecttsgo/catchTagToCatchReason38Toolingsuggestion
EK3816effecttsgo/catchToIgnore38Toolingsuggestion
EK3817effecttsgo/catchToOrElseSucceed38Toolingsuggestion
EK3818effecttsgo/catchUnfailableEffect38Toolingsuggestion
EK3819effecttsgo/classSelfMismatch38Toolingerror
EK3820effecttsgo/cryptoRandomUUID38Toolingoff
EK3821effecttsgo/cryptoRandomUUIDInEffect38Toolingoff
EK3822effecttsgo/deterministicKeys38Toolingoff
EK3823effecttsgo/duplicatePackage38Toolingwarning
EK3824effecttsgo/effectDoNotation38Toolingoff
EK3825effecttsgo/effectFnIife38Toolingwarning
EK3826effecttsgo/effectFnImplicitAny38Toolingerror
EK3827effecttsgo/effectFnOpportunity38Toolingsuggestion
EK3828effecttsgo/effectGenUsesAdapter38Toolingwarning
EK3829effecttsgo/effectInFailure38Toolingwarning
EK3830effecttsgo/effectInVoidSuccess38Toolingwarning
EK3831effecttsgo/effectMapFlatten38Toolingsuggestion
EK3832effecttsgo/effectMapVoid38Toolingsuggestion
EK3833effecttsgo/effectSleepZeroToYieldNow38Toolingsuggestion
EK3834effecttsgo/effectSucceedWithVoid38Toolingsuggestion
EK3835effecttsgo/errorPayloadShadowsName38Toolingsuggestion
EK3836effecttsgo/experimentalApiUsage38Toolingwarning
EK3837effecttsgo/extendsNativeError38Toolingoff
EK3838effecttsgo/flatMapConditionalToFilterOrFail38Toolingsuggestion
EK3839effecttsgo/flatMapIgnoredParamToAndThen38Toolingsuggestion
EK3840effecttsgo/flatMapToMap38Toolingsuggestion
EK3841effecttsgo/flatMapToTap38Toolingsuggestion
EK3801effecttsgo/floatingEffect38Toolingerror
EK3842effecttsgo/floatingEffectInVitest38Toolingerror
EK3843effecttsgo/genericEffectServices38Toolingwarning
EK3844effecttsgo/globalConsole38Toolingoff
EK3845effecttsgo/globalConsoleInEffect38Toolingoff
EK3846effecttsgo/globalDate38Toolingoff
EK3847effecttsgo/globalDateInEffect38Toolingoff
EK3848effecttsgo/globalErrorInEffectCatch38Toolingwarning
EK3849effecttsgo/globalErrorInEffectFailure38Toolingwarning
EK3850effecttsgo/globalFetch38Toolingoff
EK3851effecttsgo/globalFetchInEffect38Toolingoff
EK3852effecttsgo/globalRandom38Toolingoff
EK3853effecttsgo/globalRandomInEffect38Toolingoff
EK3854effecttsgo/globalTimers38Toolingoff
EK3855effecttsgo/globalTimersInEffect38Toolingoff
EK3856effecttsgo/instanceOfSchema38Toolingoff
EK3857effecttsgo/layerMergeAllWithDependencies38Toolingwarning
EK3858effecttsgo/lazyEffect38Toolingsuggestion
EK3859effecttsgo/lazyPromiseInEffectSync38Toolingwarning
EK3860effecttsgo/leakingRequirements38Toolingsuggestion
EK3861effecttsgo/mapSomeToAsSome38Toolingsuggestion
EK3862effecttsgo/matchEffectToMapBoth38Toolingsuggestion
EK3863effecttsgo/matchEffectToMatch38Toolingsuggestion
EK3864effecttsgo/missedPipeableOpportunity38Toolingoff
EK3865effecttsgo/missingEffectContext38Toolingerror
EK3866effecttsgo/missingEffectError38Toolingerror
EK3867effecttsgo/missingEffectServiceDependency38Toolingoff
EK3868effecttsgo/missingLayerContext38Toolingerror
EK3869effecttsgo/missingPipeableSignature38Toolingoff
EK3870effecttsgo/missingReturnYieldStar38Toolingerror
EK3871effecttsgo/missingStarInYieldEffectGen38Toolingerror
EK3872effecttsgo/multipleCatchTag38Toolingsuggestion
EK3873effecttsgo/multipleEffectProvide38Toolingwarning
EK3874effecttsgo/nestedEffectGenYield38Toolingoff
EK3875effecttsgo/newPromise38Toolingoff
EK3876effecttsgo/newSchemaClass38Toolingoff
EK3877effecttsgo/nodeBuiltinImport38Toolingoff
EK3878effecttsgo/nonObjectEffectServiceType38Toolingerror
EK3879effecttsgo/obsoleteMatchImport38Toolingwarning
EK3880effecttsgo/obsoleteSchemaImport38Toolingwarning
EK3881effecttsgo/optionMatchToFromOption38Toolingsuggestion
EK3882effecttsgo/outdatedApi38Toolingwarning
EK3883effecttsgo/overriddenSchemaConstructor38Toolingerror
EK3884effecttsgo/preferBarrelImport38Toolingoff
EK3885effecttsgo/preferEffectArray38Toolingoff
EK3886effecttsgo/preferEffectRecord38Toolingoff
EK3887effecttsgo/preferEffectString38Toolingoff
EK3888effecttsgo/preferNamespaceImport38Toolingoff
EK3889effecttsgo/preferSchemaOverJson38Toolingoff
EK3890effecttsgo/preferSchemaTaggedError38Toolingoff
EK3891effecttsgo/preferSchemaTypeProperty38Toolingoff
EK3892effecttsgo/preferSucceedSomeOrNone38Toolingsuggestion
EK3893effecttsgo/preferTypedSchemaDecoder38Toolingsuggestion
EK3894effecttsgo/preferUnsafeConstructor38Toolingsuggestion
EK3895effecttsgo/processEnv38Toolingoff
EK3896effecttsgo/processEnvInEffect38Toolingoff
EK3897effecttsgo/promiseInEffectSuccess38Toolingwarning
EK3898effecttsgo/provideLayerSucceedToProvideService38Toolingsuggestion
EK3899effecttsgo/raceFirstWithSleepToTimeout38Toolingsuggestion
EK38100effecttsgo/redundantMapError38Toolingsuggestion
EK38101effecttsgo/redundantOrDie38Toolingsuggestion
EK38102effecttsgo/redundantSchemaTagIdentifier38Toolingsuggestion
EK38103effecttsgo/returnEffectInGen38Toolingsuggestion
EK38104effecttsgo/runEffectInsideEffect38Toolingsuggestion
EK38105effecttsgo/runOfExitToRunExit38Toolingsuggestion
EK38106effecttsgo/schemaClassMissingBrand38Toolingsuggestion
EK38107effecttsgo/schemaClassObjectLiteral38Toolingwarning
EK38108effecttsgo/schemaLiteralNonFinite38Toolingerror
EK38109effecttsgo/schemaNumber38Toolingsuggestion
EK38110effecttsgo/schemaOpaqueInstanceMember38Toolingerror
EK38111effecttsgo/schemaStructWithTag38Toolingsuggestion
EK38112effecttsgo/schemaSync38Toolingoff
EK38113effecttsgo/schemaSyncInEffect38Toolingsuggestion
EK38114effecttsgo/schemaUnionOfLiterals38Toolingoff
EK38115effecttsgo/scopeInLayerEffect38Toolingwarning
EK38116effecttsgo/serviceNotAsClass38Toolingoff
EK38117effecttsgo/strictBooleanExpressions38Toolingoff
EK38118effecttsgo/strictEffectProvide38Toolingoff
EK38119effecttsgo/syncToSucceed38Toolingsuggestion
EK38120effecttsgo/timeoutCatchTagToTimeoutOrElse38Toolingsuggestion
EK38121effecttsgo/tryCatchInEffectGen38Toolingsuggestion
EK38122effecttsgo/unknownInEffectCatch38Toolingwarning
EK38123effecttsgo/unnecessaryArrowBlock38Toolingoff
EK38124effecttsgo/unnecessaryEffectGen38Toolingsuggestion
EK38125effecttsgo/unnecessaryFailYieldableError38Toolingsuggestion
EK38126effecttsgo/unnecessaryPipe38Toolingsuggestion
EK38127effecttsgo/unnecessaryPipeChain38Toolingsuggestion
EK38128effecttsgo/unnecessaryTypeofType38Toolingsuggestion
EK38129effecttsgo/unsafeEffectTypeAssertion38Toolingoff
EK38130effecttsgo/unstableApiUsage38Toolingwarning

Tools that only analyze code, such as cycle checkers (madge) and dependency graphs, may run on VibeLang's compiled output. Formatters cannot; they read .vibe itself.

Fix scopes

vibelang lint accepts the compiler’s options, including -p/--project and file arguments, with no lint-specific options; for example, vibelang lint --project tsconfig.json selects that project (VIP-38). vibelang --fix remains the codemod entry point. Two flags scope that invocation (VIP-34, amended):

FlagScope when used with --fix
--fix-migration-onlyMigration fixes, excluding TypeScript-form teaching rewrites and prelude/import cleanup.
--fix-vip-onlyThe existing VIP migration passes, including the VIP-1/VIP-2 function-kind and try migration, excluding prelude/import cleanup.

In every scope, --fix removes an import only when the resulting declaration preserves API identity: the same exports, types and documentation. The prelude supplying a name does not itself prove that its import is unneeded. Changes only to formatting, quote style, import extension spelling, or import and member order where tsc does not care do not prevent removal; an inferred import type must still denote the same public type. Source checking remains a separate requirement: under noUnusedLocals, an import the fixes leave unused in the source is removed, since it would report TS6133. Every .vibe module has a declaration, its sidecar, so noEmit and declaration: false change nothing here.

Either scope flag requires --fix somewhere in the argument list, before or after it; alone, each is an unknown compiler option (TS5023). The ordinary --fix invocation and its project options keep their existing meaning.

vibelang --fix --fix-migration-only -p tsconfig.json
vibelang --fix-vip-only --fix -p tsconfig.json
vibelang --fix-vip-only # TS5023: add --fix

Only --fix-vip-only runs the VIP-1/VIP-2 function-kind and try passes, reading input as source for the release before VIP-1. It writes try before each call that ran where it stood before VIP-1, and removes the try that ran a known legacy wrapper, deciding from the original resolved callable and migration provenance, not the final value's shape or annotation alone. A rewrite that can't preserve types, behavior and declaration output refuses at that site (VIP-34, amended). Ordinary --fix and --fix-migration-only do not run those passes: ordinary --fix never removes a valid try from current source merely because its callee is unannotated. In current source, try make() intentionally runs the result whenever it passes the operand test.

A value that fails that operand test, such as Option.some(1) or Result.succeed(2), keeps EK0502: the migration inserts no conversion and unwraps nothing, whatever methods the value has (VIP-5, amended).

When --fix-vip-only refused any call, it sets "vibe": { "severities": { "EK4802": "error" } } in the project's config with a comment that says why, so all EK4802 inferred Effect argument sites in the project fail to compile; the owner removes the setting once a run refuses none.

These commands have no Effect TypeScript runtime translation.