@athrio/loom 0.0.13 → 0.0.15

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/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as transduce, $i as Number$2, $n as result, $t as optionalKey, A as FileSystem, Ai as make$7, An as forEach, Ar as done, At as Defect, B as fromEffect, Bt as Struct, Cn as effectify, Cr as isDone, D as log, Di as Service, Dn as filterOrFail, Dr as succeed$3, Dt as ArraySchema, E as error, Ei as Reference, En as filter$2, Er as fail$2, Et as nominal, F as concat, Fi as flatMap$2, Fn as ignore, Fr as toMillis, G as mapEffect$1, Gi as liftThrowable, Gn as option, Gr as findFirst, H as isStream, Hi as getOrUndefined, Hn as mapError, Ht as TrueLiterals, I as decodeText, In as interrupt, J as orDie$1, Ji as none, Jn as promise, K as merge, Ki as map$4, Kn as orDie, Kr as findFirstIndex, Kt as decodeUnknownEffect, L as empty$2, Li as fromNullishOr, Ln as isEffect, Lr as dedupe, M as WatchBackend, Mi as contains, Mn as forkScoped, Mr as pretty, Mt as Int$4, N as make$10, Ni as exists, Nr as TaggedError$1, Nt as Literal, O as Path$3, Oi as empty$1, On as flatMap$1, Or as Done, P as callback$1, Pn as gen, Pr as taggedEnum, Q as splitLines, Qi as Array_, Qn as raceFirst, Qt as optional$3, R as filter$1, Ri as fromUndefinedOr, Si as fail$1, Sr as doneUnsafe, T as make$9, Ti as succeed$2, Tn as fail, Tr as makeUnsafe$1, Ui as isNone, Un as match$2, V as groupedWithin, Vn as map$2, Vr as empty, Vt as TaggedError, Wi as isSome, Wn as matchEffect, Wr as filter, Wt as Union, X as runCollect, Xi as some, Xn as provideContext, Y as run$3, Yi as orElse, Yn as provide$1, Yt as is, Z as runForEach, Zn as provideService, _ as orElse$1, _a as pipe, _n as catchCause, _r as provideMerge, _t as poll, a as make$15, aa as mapInput, ai as isReadonlyArrayNonEmpty, ar as sync, at as fromChannel, b as when, bn as catchTags, br as MinimumLogLevel, bt as makeUnsafe, c as errorExitCode, ca as BaseProto, cn as toNumber, cr as tryPromise, ct as badArgument, da as isNotNull, di as match, dn as all, dr as void_, dt as mapDone, ea as String$6, er as runFork, fa as isNotUndefined, fn as andThen, fr as catchDone, ft as endUnsafe, ga as identity, gn as callback, gr as provide, gt as offerUnsafe, ha as dual, hi as reduce, hn as cached, hr as mergeAll, ht as offer, i as isQuitError, ia as isGreaterThan, in as value, ir as suspend, it as drain, j as FileTypeId, jn as forever, jr as fail$3, jt as FalseLiterals, k as make$11, ki as getUnsafe, kn as fnUntraced, kr as UnknownError, kt as BooleanLiterals, l as errorReported, la as format$1, li as map$3, ln as acquireRelease, lr as try_, lt as systemError, ma as isUndefined, mr as effect, mt as make$13, n as QuitError, nr as serviceOption, nt as get, o as Stdio, oa as Prototype, or as tap, ot as isSink, pa as isNullish, pi as of, pn as as, pr as round, pt as failCauseUnsafe, q as mkString, qi as match$1, qn as orElseSucceed, qr as findLast, r as Terminal, rn as make$6, rr as succeed$1, rt as make$14, s as make$12, sa as assignProperty, sn as bytes, sr as tapError, st as BadArgument, tn as toCodecStringTree, tr as scoped, tt as unwrap, u as makeRunMain, ua as hasProperty, un as addFinalizer$1, ur as uninterruptible, ut as fromTransform, va as pipeArguments, vi as sort, vn as catchFilter, vr as succeed$4, vt as take, w as Crypto, wi as match$3, wn as ensuring, wr as isDoneUnsafe, xn as catch_, xr as _await, y as value$1, yn as catchTag, yr as addFinalizer, yt as make$8, z as fromChannel$1, zi as getOrElse, zt as String$5 } from "./dist-LBqwqmSY.js";
3
- import { a as holds, c as storeDirs, l as PackageConfig, n as DocumentSource, o as loomsIn, r as LoomCompiler, s as projectIn, t as LoomFormatter, u as LoomConfig } from "./LoomFormatter-D6wh-SLN.js";
4
- import { a as installedVersion, d as standalone, f as storeDir, h as workspaceRoot, i as installTypeScript, l as servicePackage, n as embeddedFile, o as removeService, p as storeTypeScript, r as installService, t as ServiceInstall, u as serviceVersion } from "./LoomStore-axDgE8RF.js";
2
+ import { $ as transduce, $i as Array_, $n as result, $t as optionalKey, A as FileSystem, Ai as getUnsafe, An as forEach, Ar as done, At as Defect, B as fromEffect, Bi as getOrElse, Bt as Struct, Ci as fail$1, Cn as effectify, Cr as isDone, D as log, Di as Reference, Dn as filterOrFail, Dr as succeed$3, Dt as ArraySchema, E as error, Ei as succeed$2, En as filter$2, Er as fail$2, Et as nominal, F as concat, Fi as filter$3, Fn as ignore, Fr as toMillis, G as mapEffect$1, Gi as isSome, Gn as option, Gr as filter, H as isStream, Hn as mapError, Hr as empty, Ht as TrueLiterals, I as decodeText, Ii as flatMap$2, In as interrupt, J as orDie$1, Ji as match$1, Jn as promise, Jr as findLast, K as merge, Ki as liftThrowable, Kn as orDie, Kr as findFirst, Kt as decodeUnknownEffect, L as empty$2, Ln as isEffect, Lr as dedupe, M as WatchBackend, Mn as forkScoped, Mr as pretty, Mt as Int$4, N as make$10, Ni as contains, Nr as TaggedError$1, Nt as Literal, O as Path$3, Oi as Service, On as flatMap$1, Or as Done, P as callback$1, Pi as exists, Pn as gen, Pr as taggedEnum, Q as splitLines, Qn as raceFirst, Qt as optional$3, R as filter$1, Ri as fromNullishOr, Rr as dedupeWith, Sr as doneUnsafe, T as make$9, Ti as match$3, Tn as fail, Tr as makeUnsafe$1, Ui as getOrUndefined, Un as match$2, V as groupedWithin, Vn as map$2, Vt as TaggedError, Wi as isNone, Wn as matchEffect, Wt as Union, X as runCollect, Xi as orElse, Xn as provideContext, Xr as flatMap$3, Y as run$3, Yi as none, Yn as provide$1, Yt as is, Z as runForEach, Zi as some, Zn as provideService, _ as orElse$1, _a as dual, _n as catchCause, _r as provideMerge, _t as poll, a as make$15, aa as isGreaterThan, ar as sync, at as fromChannel, b as when, ba as pipeArguments, bn as catchTags, br as MinimumLogLevel, bt as makeUnsafe, c as errorExitCode, ca as assignProperty, cn as toNumber, cr as tryPromise, ct as badArgument, da as equals, dn as all, dr as void_, dt as mapDone, ea as Number$2, er as runFork, fa as hasProperty, fi as match, fn as andThen, fr as catchDone, ft as endUnsafe, ga as isUndefined, gi as reduce, gn as callback, gr as provide, gt as offerUnsafe, ha as isNullish, hn as cached, hr as mergeAll, ht as offer, i as isQuitError, in as value, ir as suspend, it as drain, j as FileTypeId, ji as make$7, jn as forever, jr as fail$3, jt as FalseLiterals, k as make$11, ki as empty$1, kn as fnUntraced, kr as UnknownError, kt as BooleanLiterals, l as errorReported, la as BaseProto, ln as acquireRelease, lr as try_, lt as systemError, ma as isNotUndefined, mi as of, mr as effect, mt as make$13, n as QuitError, nr as serviceOption, nt as get, o as Stdio, oa as mapInput, oi as isReadonlyArrayNonEmpty, or as tap, ot as isSink, pa as isNotNull, pn as as, pr as round, pt as failCauseUnsafe, q as mkString, qi as map$4, qn as orElseSucceed, qr as findFirstIndex, r as Terminal, rn as make$6, rr as succeed$1, rt as make$14, s as make$12, sa as Prototype, sn as bytes, sr as tapError, st as BadArgument, ta as String$6, tn as toCodecStringTree, tr as scoped, tt as unwrap, u as makeRunMain, ua as format$1, ui as map$3, un as addFinalizer$1, ur as uninterruptible, ut as fromTransform, va as identity, vn as catchFilter, vr as succeed$4, vt as take, w as Crypto, wn as ensuring, wr as isDoneUnsafe, xn as catch_, xr as _await, y as value$1, ya as pipe, yi as sort, yn as catchTag, yr as addFinalizer, yt as make$8, z as fromChannel$1, zi as fromUndefinedOr, zt as String$5 } from "./dist-NXZtp5_0.js";
3
+ import { a as holds, c as storeDirs, l as PackageConfig, n as DocumentSource, o as loomsIn, r as LoomCompiler, s as projectIn, t as LoomFormatter, u as LoomConfig } from "./LoomFormatter-BHgesYCg.js";
4
+ import { a as installedVersion, d as serviceVersion, f as standalone, g as workspaceRoot, h as workspaceOf, i as installTypeScript, m as storeTypeScript, n as embeddedFile, p as storeDir, r as installService, s as removeService, t as ServiceInstall, u as servicePackage } from "./LoomStore-C5R57cqa.js";
5
5
  import * as OS from "node:os";
6
6
  import { homedir, tmpdir } from "node:os";
7
7
  import * as NodeChildProcess from "node:child_process";
@@ -9,9 +9,9 @@ import { PassThrough } from "node:stream";
9
9
  import * as NodeCrypto from "node:crypto";
10
10
  import { createHash } from "node:crypto";
11
11
  import * as NFS from "node:fs";
12
- import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
12
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
13
13
  import * as Path from "node:path";
14
- import { dirname, relative, resolve } from "node:path";
14
+ import { delimiter, dirname, relative, resolve } from "node:path";
15
15
  import { gunzipSync } from "node:zlib";
16
16
  import * as NodeUrl from "node:url";
17
17
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -8702,8 +8702,8 @@ var formatLanguages = (target) => gen(function* () {
8702
8702
  yield* sync(() => {
8703
8703
  globalThis.require ??= createRequire(import.meta.url);
8704
8704
  });
8705
- const { installHostRuntime } = yield* promise(() => import("./Runtime-Cj1Rsxs1.js"));
8706
- const { loadActive } = yield* promise(() => import("./LanguageLoader-DJGDopcP.js"));
8705
+ const { installHostRuntime } = yield* promise(() => import("./Runtime-B-1DeuX_.js"));
8706
+ const { loadActive } = yield* promise(() => import("./LanguageLoader-CBfzN4NH.js"));
8707
8707
  yield* sync(() => installHostRuntime(ts));
8708
8708
  return yield* loadActive(ids, root);
8709
8709
  })
@@ -8796,7 +8796,7 @@ var installNote = (language, change) => ServiceInstall.$match(change, {
8796
8796
  var failedInstall = (error) => refuse(`loom: could not install ${error.packageName} — ${String(error.cause)}`);
8797
8797
  var alreadyActive = (language, dir) => log(`\n ${teal("✓")} ${language} is already activated\n` + match$1(installedVersion(language, dir), {
8798
8798
  onNone: () => ` ${dim(`its service is not in the store; \`loom update ${language}\` installs it`)}\n`,
8799
- onSome: (version) => version === "0.0.13" ? "" : ` ${dim(`the store holds its service at ${version}; \`loom update ${language}\` brings it to ${serviceVersion}`)}\n`
8799
+ onSome: (version) => version === "0.0.15" ? "" : ` ${dim(`the store holds its service at ${version}; \`loom update ${language}\` brings it to ${serviceVersion}`)}\n`
8800
8800
  }));
8801
8801
  var add = (language) => gen(function* () {
8802
8802
  const config = yield* LoomConfig;
@@ -8828,8 +8828,67 @@ var update = (languages, all) => gen(function* () {
8828
8828
  yield* forEach(ids, (id) => installService(id, dir).pipe(flatMap$1((change) => log(` ${teal("✓")} ${dim(installNote(id, change))}`))));
8829
8829
  yield* log(` ${dim(`store: ${storeDir(dir)}`)}\n`);
8830
8830
  }).pipe(catchTag("StoreError", failedInstall));
8831
- var packageManagerUpgrade = (bundle) => value$1(bundle).pipe(when((path) => /[\\/]\.bun[\\/]/.test(path), () => "bun add -g @athrio/loom@latest"), when((path) => /[\\/]pnpm[\\/]/.test(path), () => "pnpm add -g @athrio/loom@latest"), orElse$1(() => "npm install -g @athrio/loom@latest"));
8832
- var loomVersion = "0.0.13";
8831
+ var Installation = taggedEnum();
8832
+ var lockfiles = [
8833
+ ["bun.lock", "bun"],
8834
+ ["bun.lockb", "bun"],
8835
+ ["pnpm-lock.yaml", "pnpm"],
8836
+ ["yarn.lock", "yarn"]
8837
+ ];
8838
+ var projectManager = (root) => pipe(findFirst(lockfiles, ([lockfile]) => existsSync(resolve(root, lockfile))), match$1({
8839
+ onNone: () => "npm",
8840
+ onSome: ([, manager]) => manager
8841
+ }));
8842
+ var projectAt = (path) => pipe(fromNullishOr(/^(.*?)[\\/]node_modules[\\/]/.exec(path)), flatMap$2((found) => fromNullishOr(found[1])), filter$3((root) => existsSync(resolve(root, "package.json"))));
8843
+ var marked = (mark) => (path) => mark.test(path);
8844
+ var installationAt = (path, otherwise) => value$1(path).pipe(when(marked(/[\\/]Cellar[\\/]/), () => Installation.Homebrew()), when(marked(/[\\/]\.bun[\\/]/), () => Installation.Bun()), when(marked(/[\\/]pnpm[\\/]/), () => Installation.Pnpm()), when(marked(/[\\/]node_modules[\\/]/), (p) => match$1(projectAt(p), {
8845
+ onNone: () => Installation.Npm(),
8846
+ onSome: (root) => Installation.Project({
8847
+ root,
8848
+ manager: projectManager(root)
8849
+ })
8850
+ })), when(marked(/[\\/]npm[\\/][^\\/]+$/), () => Installation.Npm()), orElse$1(() => otherwise));
8851
+ var realOf = (path) => getOrElse(liftThrowable(() => realpathSync(path))(), () => path);
8852
+ var thisFile = standalone ? process.execPath : fileURLToPath(import.meta.url);
8853
+ var thisInstallation = installationAt(realOf(thisFile), standalone ? Installation.Executable() : Installation.Npm());
8854
+ var describedInstallation = Installation.$match({
8855
+ Executable: () => "a standalone executable",
8856
+ Unowned: () => "a file no installer owns",
8857
+ Homebrew: () => "installed by Homebrew",
8858
+ Npm: () => "installed by npm",
8859
+ Bun: () => "installed by Bun",
8860
+ Pnpm: () => "installed by pnpm",
8861
+ Project: ({ root, manager }) => `installed by ${manager} in the project at ${root}`
8862
+ });
8863
+ var quoted = (path) => `"${path}"`;
8864
+ var projectCommands = {
8865
+ npm: {
8866
+ add: "npm install",
8867
+ remove: "npm uninstall"
8868
+ },
8869
+ bun: {
8870
+ add: "bun add",
8871
+ remove: "bun remove"
8872
+ },
8873
+ pnpm: {
8874
+ add: "pnpm add",
8875
+ remove: "pnpm remove"
8876
+ },
8877
+ yarn: {
8878
+ add: "yarn add",
8879
+ remove: "yarn remove"
8880
+ }
8881
+ };
8882
+ var installerUpgrade = Installation.$match({
8883
+ Executable: () => none(),
8884
+ Unowned: () => none(),
8885
+ Homebrew: () => some("brew upgrade loom"),
8886
+ Npm: () => some("npm install -g @athrio/loom@latest"),
8887
+ Bun: () => some("bun add -g @athrio/loom@latest"),
8888
+ Pnpm: () => some("pnpm add -g @athrio/loom@latest"),
8889
+ Project: ({ root, manager }) => some(`cd ${quoted(root)} && ${projectCommands[manager].add} @athrio/loom@latest`)
8890
+ });
8891
+ var loomVersion = "0.0.15";
8833
8892
  var releases = process.env.LOOM_RELEASES ?? "https://api.github.com/repos/athrio-com/loom/releases";
8834
8893
  var ReleaseSchema = Struct({
8835
8894
  tag_name: String$5,
@@ -8921,8 +8980,9 @@ var upgradeStandalone = gen(function* () {
8921
8980
  yield* log(`\n ${teal("✓")} upgraded Loom from ${loomVersion} to ${latest}\n ${dim("run `loom update --all` in each workspace to bring its services along")}\n`);
8922
8981
  });
8923
8982
  var upgrade = gen(function* () {
8924
- if (standalone) return yield* upgradeStandalone;
8925
- yield* log(`\n Loom was installed by a package manager, which upgrades it:\n\n ${packageManagerUpgrade(fileURLToPath(import.meta.url))}\n`);
8983
+ const command = installerUpgrade(thisInstallation);
8984
+ if (isNone(command)) return yield* upgradeStandalone;
8985
+ yield* log(`\n Loom was ${describedInstallation(thisInstallation)}. Upgrade it with:\n\n ${command.value}\n\n ${dim("then run `loom update --all` in each workspace to bring its services along")}\n`);
8926
8986
  }).pipe(catchTags({
8927
8987
  ReleaseUnreachable: (error) => refuse(`loom: could not reach the latest release at ${error.url} — ${String(error.cause)}`),
8928
8988
  ReleaseLacksAsset: (error) => refuse(`loom: release ${error.version} carries no ${error.asset}`),
@@ -8938,11 +8998,91 @@ var status = gen(function* () {
8938
8998
  const languages = Object.keys(manifest?.languages ?? {});
8939
8999
  const mark = (id) => match$1(installedVersion(id, dir), {
8940
9000
  onNone: () => ` ${dim("○")} ${id}${dim(` — run \`loom update ${id}\``)}`,
8941
- onSome: (version) => version === "0.0.13" ? ` ${teal("●")} ${id}` : ` ${dim("◐")} ${id}${dim(` — ${version} is installed; run \`loom update ${id}\` for ${serviceVersion}`)}`
9001
+ onSome: (version) => version === "0.0.15" ? ` ${teal("●")} ${id}` : ` ${dim("◐")} ${id}${dim(` — ${version} is installed; run \`loom update ${id}\` for ${serviceVersion}`)}`
8942
9002
  });
8943
9003
  yield* log(`\n ${dim("store")} ${storeDir(dir)}\n ${dim("primary")} ${primary ?? dim("(none)")}\n ${dim("activated")} ${languages.length === 0 ? dim("(none)") : languages.join(", ")}\n`);
8944
9004
  if (languages.length > 0) yield* log(`${languages.map(mark).join("\n")}\n`);
8945
9005
  });
9006
+ var Finding = taggedEnum();
9007
+ var commandNames = (command) => process.platform === "win32" ? [`${command}.exe`, `${command}.cmd`] : [command];
9008
+ var isProgram = (file) => pipe(liftThrowable(() => statSync(file))(), exists((stats) => stats.isFile() && (process.platform === "win32" || (stats.mode & 73) !== 0)));
9009
+ var programsOnPath = (command) => pipe((process.env.PATH ?? "").split(delimiter), filter((folder) => folder !== ""), flatMap$3((folder) => map$3(commandNames(command), (name) => resolve(folder, name))), filter(isProgram), dedupeWith((one, other) => realOf(one) === realOf(other)));
9010
+ var loomsOnPath = () => programsOnPath("loom");
9011
+ var installationOf = (file) => installationAt(realOf(file), Installation.Unowned());
9012
+ var isThisLoom = (file) => realOf(file) === realOf(thisFile) || equals(installationOf(file), thisInstallation);
9013
+ var installLine = "curl -fsSL https://github.com/athrio-com/loom/releases/latest/download/install.sh | sh";
9014
+ var installerRemoval = (installation, file) => Installation.$match(installation, {
9015
+ Executable: () => `rm ${quoted(file)}`,
9016
+ Unowned: () => `rm ${quoted(file)}`,
9017
+ Homebrew: () => "brew uninstall loom",
9018
+ Npm: () => "npm uninstall -g @athrio/loom",
9019
+ Bun: () => "bun remove -g @athrio/loom",
9020
+ Pnpm: () => "pnpm remove -g @athrio/loom",
9021
+ Project: ({ root, manager }) => `cd ${quoted(root)} && ${projectCommands[manager].remove} @athrio/loom`
9022
+ });
9023
+ var otherLoom = (file, note) => Finding.Problem({
9024
+ note: `${note}, ${describedInstallation(installationOf(file))}`,
9025
+ fix: installerRemoval(installationOf(file), file)
9026
+ });
9027
+ var thisLoomFinding = Finding.Fine({ note: `loom v${loomVersion} runs from ${thisFile}, ${describedInstallation(thisInstallation)}` });
9028
+ var pathFindings = (looms) => match(looms, {
9029
+ onEmpty: () => [Finding.Problem({
9030
+ note: "no loom is on PATH, so a terminal finds none to run",
9031
+ fix: installLine
9032
+ })],
9033
+ onNonEmpty: ([first, ...rest]) => [isThisLoom(first) ? Finding.Fine({ note: `a terminal runs this loom, at ${first}` }) : otherLoom(first, `a terminal runs ${first} instead`), ...pipe(rest, filter((file) => !isThisLoom(file)), map$3((file) => otherLoom(file, `${file} is on PATH too`)))]
9034
+ });
9035
+ var serviceFinding = (id, root) => match$1(installedVersion(id, root), {
9036
+ onNone: () => Finding.Problem({
9037
+ note: `the store holds no service for ${id}`,
9038
+ fix: `loom update ${id}`
9039
+ }),
9040
+ onSome: (version) => version === "0.0.15" ? Finding.Fine({ note: `the store holds ${id}'s service at this Loom's ${serviceVersion}` }) : Finding.Problem({
9041
+ note: `the store holds ${id}'s service at ${version}, and this Loom needs ${serviceVersion}`,
9042
+ fix: `loom update ${id}`
9043
+ })
9044
+ });
9045
+ var serviceFindings = (languages, root) => match(languages, {
9046
+ onEmpty: () => [Finding.Fine({ note: "no language is activated, so the store needs no service" })],
9047
+ onNonEmpty: (ids) => map$3(ids, (id) => serviceFinding(id, root))
9048
+ });
9049
+ var missingTypeScript = (root) => value$1({
9050
+ standalone,
9051
+ npm: programsOnPath("npm").length > 0
9052
+ }).pipe(when({ standalone: true }, () => Finding.Fine({ note: `the language server unpacks the TypeScript Loom carries into ${storeDir(root)} when it first starts` })), when({ npm: true }, () => Finding.Fine({ note: `the language server installs TypeScript ^6 into ${storeDir(root)} with npm when it first starts` })), orElse$1(() => Finding.Problem({
9053
+ note: "the language server finds no TypeScript, and has no npm to install one with",
9054
+ fix: installLine
9055
+ })));
9056
+ var typeScriptFinding = (root) => match$1(foundTypeScript(root), {
9057
+ onNone: () => missingTypeScript(root),
9058
+ onSome: (file) => Finding.Fine({ note: `the language server finds TypeScript in ${dirname(dirname(file))}` })
9059
+ });
9060
+ var workspaceFindings = (root) => gen(function* () {
9061
+ const manifest = yield* (yield* LoomConfig).manifest(root);
9062
+ return [...serviceFindings(Object.keys(manifest?.languages ?? {}), root), typeScriptFinding(root)];
9063
+ });
9064
+ var amber = (s) => `\x1b[38;5;214m${s}${reset}`;
9065
+ var findingLines = (findings) => map$3(findings, Finding.$match({
9066
+ Fine: ({ note }) => ` ${teal("✓")} ${note}`,
9067
+ Problem: ({ note, fix }) => ` ${amber("✗")} ${note}\n ${fix}`
9068
+ })).join("\n");
9069
+ var doctor = gen(function* () {
9070
+ const dir = process.cwd();
9071
+ const machine = [thisLoomFinding, ...pathFindings(loomsOnPath())];
9072
+ const workspace = workspaceOf(dir);
9073
+ const inWorkspace = yield* match$1(workspace, {
9074
+ onNone: () => succeed$1([]),
9075
+ onSome: workspaceFindings
9076
+ });
9077
+ const problems = filter([...machine, ...inWorkspace], Finding.$is("Problem")).length;
9078
+ yield* log(`\n ${dim("this machine")}\n${findingLines(machine)}\n\n` + match$1(workspace, {
9079
+ onNone: () => ` ${dim(`no workspace holds ${dir}, so no services to check`)}\n`,
9080
+ onSome: (root) => ` ${dim(`the workspace at ${root}`)}\n${findingLines(inWorkspace)}\n`
9081
+ }));
9082
+ if (problems === 0) return yield* log(` ${teal("✓")} no problems found\n`);
9083
+ yield* log(` ${problems === 1 ? "1 problem" : `${problems} problems`} found\n`);
9084
+ yield* sync(() => void (process.exitCode = 1));
9085
+ });
8946
9086
  var set = (name, value) => gen(function* () {
8947
9087
  const config = yield* LoomConfig;
8948
9088
  const tangler = yield* LoomTangler;
@@ -9043,7 +9183,7 @@ var helixBlock = {
9043
9183
  "name = \"loom\"",
9044
9184
  "scope = \"source.loom\"",
9045
9185
  "file-types = [\"loom\"]",
9046
- "roots = [\".loom\"]",
9186
+ "roots = [\".loom/config.yaml\"]",
9047
9187
  "injection-regex = \"^loom$\"",
9048
9188
  "language-servers = [\"loom\"]",
9049
9189
  "soft-wrap = { enable = true, wrap-at-text-width = true }",
@@ -9091,7 +9231,13 @@ var nvimPlugin = [
9091
9231
  "vim.lsp.config('loom', {",
9092
9232
  " cmd = { 'loom', 'lsp', '--stdio' },",
9093
9233
  " filetypes = { 'loom' },",
9094
- " root_markers = { '.loom', '.git' },",
9234
+ " root_dir = function(bufnr, on_dir)",
9235
+ " local workspace = function(name, path)",
9236
+ " local config = vim.fs.joinpath(path, name, 'config.yaml')",
9237
+ " return name == '.loom' and vim.uv.fs_stat(config) ~= nil",
9238
+ " end",
9239
+ " on_dir(vim.fs.root(bufnr, workspace) or vim.fs.root(bufnr, '.git'))",
9240
+ " end,",
9095
9241
  "})",
9096
9242
  "vim.lsp.enable('loom')",
9097
9243
  "",
@@ -9150,6 +9296,7 @@ var updateCommand = make$5("update", {
9150
9296
  }, ({ languages, all }) => update(languages, all));
9151
9297
  var upgradeCommand = make$5("upgrade", {}, () => upgrade);
9152
9298
  var statusCommand = make$5("status", {}, () => status);
9299
+ var doctorCommand = make$5("doctor", {}, () => doctor);
9153
9300
  var setCommand = make$5("set", {
9154
9301
  name: String$1("name"),
9155
9302
  value: String$1("value")
@@ -9173,6 +9320,7 @@ var program = run(make$5("loom").pipe(withSubcommands([
9173
9320
  updateCommand,
9174
9321
  upgradeCommand,
9175
9322
  statusCommand,
9323
+ doctorCommand,
9176
9324
  setCommand,
9177
9325
  designCommand,
9178
9326
  helixCommand,
@@ -9180,7 +9328,7 @@ var program = run(make$5("loom").pipe(withSubcommands([
9180
9328
  ])), { version: loomVersion }).pipe(provide$1(LoomTangler.layer), provide$1(LoomWeaver.layer), provide$1(LoomContents.layer), provide$1(LoomFormatter.layer), provide$1(DocumentSource.layer), provide$1(PackageConfig.layer), provide$1(LoomConfig.layer), provide$1(layer));
9181
9329
  if (process.argv[2] === "lsp") {
9182
9330
  globalThis.require = createRequire(import.meta.url);
9183
- const { startLanguageServer } = await import("./LoomServer-C54yIIo1.js");
9331
+ const { startLanguageServer } = await import("./LoomServer-D14D2si9.js");
9184
9332
  startLanguageServer();
9185
9333
  } else runMain(program);
9186
9334
  //#endregion