@locusai/cli 0.17.10 → 0.17.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bin/locus.js +737 -157
  2. package/package.json +1 -1
package/bin/locus.js CHANGED
@@ -1,12 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
3
7
  var __export = (target, all) => {
4
8
  for (var name in all)
5
9
  __defProp(target, name, {
6
10
  get: all[name],
7
11
  enumerable: true,
8
12
  configurable: true,
9
- set: (newValue) => all[name] = () => newValue
13
+ set: __exportSetter.bind(all, name)
10
14
  });
11
15
  };
12
16
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -1321,7 +1325,7 @@ function reopenMilestone(owner, repo, milestoneNumber, options = {}) {
1321
1325
  gh(`api repos/${owner}/${repo}/milestones/${milestoneNumber} -X PATCH -f state=open`, options);
1322
1326
  }
1323
1327
  function createPR(title, body, head, base, options = {}) {
1324
- const result = gh(`pr create --title ${JSON.stringify(title)} --body ${JSON.stringify(body)} --head ${JSON.stringify(head)} --base ${JSON.stringify(base)}`, options);
1328
+ const result = ghExec(["pr", "create", "--title", title, "--body", body, "--head", head, "--base", base], options);
1325
1329
  const match = result.match(/\/pull\/(\d+)/);
1326
1330
  if (!match) {
1327
1331
  throw new Error(`Could not extract PR number from: ${result}`);
@@ -1683,6 +1687,496 @@ var init_init = __esm(() => {
1683
1687
  ];
1684
1688
  });
1685
1689
 
1690
+ // src/packages/registry.ts
1691
+ import {
1692
+ existsSync as existsSync6,
1693
+ mkdirSync as mkdirSync6,
1694
+ readFileSync as readFileSync5,
1695
+ renameSync,
1696
+ writeFileSync as writeFileSync5
1697
+ } from "node:fs";
1698
+ import { homedir as homedir2 } from "node:os";
1699
+ import { join as join6 } from "node:path";
1700
+ function getPackagesDir() {
1701
+ const dir = join6(homedir2(), ".locus", "packages");
1702
+ if (!existsSync6(dir)) {
1703
+ mkdirSync6(dir, { recursive: true });
1704
+ }
1705
+ const pkgJson = join6(dir, "package.json");
1706
+ if (!existsSync6(pkgJson)) {
1707
+ writeFileSync5(pkgJson, `${JSON.stringify({ private: true }, null, 2)}
1708
+ `, "utf-8");
1709
+ }
1710
+ return dir;
1711
+ }
1712
+ function getRegistryPath() {
1713
+ return join6(getPackagesDir(), "registry.json");
1714
+ }
1715
+ function loadRegistry() {
1716
+ const registryPath = getRegistryPath();
1717
+ if (!existsSync6(registryPath)) {
1718
+ return { packages: {} };
1719
+ }
1720
+ try {
1721
+ const raw = readFileSync5(registryPath, "utf-8");
1722
+ const parsed = JSON.parse(raw);
1723
+ if (typeof parsed === "object" && parsed !== null && "packages" in parsed && typeof parsed.packages === "object") {
1724
+ return parsed;
1725
+ }
1726
+ return { packages: {} };
1727
+ } catch {
1728
+ return { packages: {} };
1729
+ }
1730
+ }
1731
+ function saveRegistry(registry) {
1732
+ const registryPath = getRegistryPath();
1733
+ const tmp = `${registryPath}.tmp`;
1734
+ writeFileSync5(tmp, `${JSON.stringify(registry, null, 2)}
1735
+ `, "utf-8");
1736
+ renameSync(tmp, registryPath);
1737
+ }
1738
+ function resolvePackageBinary(packageName) {
1739
+ const fullName = normalizePackageName(packageName);
1740
+ const binPath = join6(getPackagesDir(), "node_modules", ".bin", fullName);
1741
+ return existsSync6(binPath) ? binPath : null;
1742
+ }
1743
+ function normalizePackageName(input) {
1744
+ if (input.startsWith("@")) {
1745
+ return input;
1746
+ }
1747
+ if (input.startsWith("locus-")) {
1748
+ return input;
1749
+ }
1750
+ return `locus-${input}`;
1751
+ }
1752
+ var init_registry = () => {};
1753
+
1754
+ // src/commands/pkg.ts
1755
+ var exports_pkg = {};
1756
+ __export(exports_pkg, {
1757
+ pkgCommand: () => pkgCommand,
1758
+ listInstalledPackages: () => listInstalledPackages
1759
+ });
1760
+ import { spawn } from "node:child_process";
1761
+ import { existsSync as existsSync7 } from "node:fs";
1762
+ function listInstalledPackages() {
1763
+ const registry = loadRegistry();
1764
+ const entries = Object.values(registry.packages);
1765
+ if (entries.length === 0) {
1766
+ process.stderr.write(`${yellow("⚠")} No packages installed.
1767
+ `);
1768
+ process.stderr.write(` Install one with: ${bold("locus install <package>")}
1769
+ `);
1770
+ return;
1771
+ }
1772
+ process.stderr.write(`
1773
+ ${bold("Installed packages:")}
1774
+
1775
+ `);
1776
+ for (const entry of entries) {
1777
+ const { name, version, manifest } = entry;
1778
+ const displayName = manifest.displayName || name;
1779
+ process.stderr.write(` ${bold(cyan(displayName))} ${dim(`v${version}`)}
1780
+ `);
1781
+ if (manifest.description) {
1782
+ process.stderr.write(` ${dim(manifest.description)}
1783
+ `);
1784
+ }
1785
+ if (manifest.commands.length > 0) {
1786
+ process.stderr.write(` Commands: ${manifest.commands.map((c) => green(c)).join(", ")}
1787
+ `);
1788
+ }
1789
+ process.stderr.write(` Run: ${bold(`locus pkg ${name.replace(/^locus-/, "")} --help`)}
1790
+ `);
1791
+ process.stderr.write(`
1792
+ `);
1793
+ }
1794
+ }
1795
+ function spawnPackageBinary(binaryPath, args) {
1796
+ return new Promise((resolve) => {
1797
+ const child = spawn(binaryPath, args, {
1798
+ stdio: "inherit",
1799
+ env: { ...process.env, LOCUS_PKG: "1" }
1800
+ });
1801
+ const onSigint = () => {
1802
+ child.kill("SIGINT");
1803
+ };
1804
+ process.on("SIGINT", onSigint);
1805
+ child.on("error", (err) => {
1806
+ process.removeListener("SIGINT", onSigint);
1807
+ process.stderr.write(`${red("✗")} Failed to spawn binary: ${err.message}
1808
+ `);
1809
+ resolve(1);
1810
+ });
1811
+ child.on("close", (code, signal) => {
1812
+ process.removeListener("SIGINT", onSigint);
1813
+ if (signal) {
1814
+ resolve(signal === "SIGINT" ? 130 : 1);
1815
+ } else {
1816
+ resolve(code ?? 0);
1817
+ }
1818
+ });
1819
+ });
1820
+ }
1821
+ async function pkgCommand(args, _flags) {
1822
+ const packageInput = args[0];
1823
+ if (!packageInput) {
1824
+ listInstalledPackages();
1825
+ return;
1826
+ }
1827
+ const packageName = normalizePackageName(packageInput);
1828
+ const registry = loadRegistry();
1829
+ const entry = registry.packages[packageName];
1830
+ if (!entry) {
1831
+ process.stderr.write(`${red("✗")} Package ${bold(`'${packageInput}'`)} is not installed.
1832
+ `);
1833
+ process.stderr.write(` Run: ${bold(`locus install ${packageInput}`)}
1834
+ `);
1835
+ process.exit(1);
1836
+ return;
1837
+ }
1838
+ const binaryPath = entry.binaryPath;
1839
+ if (!binaryPath || !existsSync7(binaryPath)) {
1840
+ process.stderr.write(`${red("✗")} Binary for ${bold(packageName)} not found on disk.
1841
+ `);
1842
+ if (binaryPath) {
1843
+ process.stderr.write(` Expected: ${dim(binaryPath)}
1844
+ `);
1845
+ }
1846
+ process.stderr.write(` Try reinstalling: ${bold(`locus install ${packageInput} --upgrade`)}
1847
+ `);
1848
+ process.exit(1);
1849
+ return;
1850
+ }
1851
+ const remainingArgs = args.slice(1);
1852
+ const exitCode = await spawnPackageBinary(binaryPath, remainingArgs);
1853
+ process.exit(exitCode);
1854
+ }
1855
+ var init_pkg = __esm(() => {
1856
+ init_terminal();
1857
+ init_registry();
1858
+ });
1859
+
1860
+ // src/commands/packages.ts
1861
+ var exports_packages = {};
1862
+ __export(exports_packages, {
1863
+ packagesCommand: () => packagesCommand
1864
+ });
1865
+ import { spawnSync } from "node:child_process";
1866
+ function checkOutdated() {
1867
+ const registry = loadRegistry();
1868
+ const entries = Object.values(registry.packages);
1869
+ if (entries.length === 0) {
1870
+ process.stderr.write(`${yellow("⚠")} No packages installed.
1871
+ `);
1872
+ process.stderr.write(` Install one with: ${bold("locus install <package>")}
1873
+ `);
1874
+ return;
1875
+ }
1876
+ process.stderr.write(`
1877
+ Checking for updates...
1878
+
1879
+ `);
1880
+ const outdated = [];
1881
+ const upToDate = [];
1882
+ for (const entry of entries) {
1883
+ const result = spawnSync("npm", ["view", entry.name, "version"], {
1884
+ encoding: "utf-8",
1885
+ shell: false
1886
+ });
1887
+ if (result.error || result.status !== 0) {
1888
+ process.stderr.write(`${yellow("⚠")} Could not check ${bold(entry.name)}: ${result.error?.message ?? "npm view failed"}
1889
+ `);
1890
+ continue;
1891
+ }
1892
+ const latest = (result.stdout ?? "").trim();
1893
+ if (!latest) {
1894
+ process.stderr.write(`${yellow("⚠")} Could not determine latest version for ${bold(entry.name)}
1895
+ `);
1896
+ continue;
1897
+ }
1898
+ if (latest !== entry.version) {
1899
+ outdated.push({ name: entry.name, installed: entry.version, latest });
1900
+ } else {
1901
+ upToDate.push(entry.name);
1902
+ }
1903
+ }
1904
+ if (outdated.length === 0) {
1905
+ process.stderr.write(`${green("✓")} All packages are up to date.
1906
+
1907
+ `);
1908
+ return;
1909
+ }
1910
+ process.stderr.write(`${bold("Outdated packages:")}
1911
+
1912
+ `);
1913
+ const nameWidth = Math.max("Package".length, ...outdated.map((e) => e.name.length));
1914
+ const installedWidth = Math.max("Installed".length, ...outdated.map((e) => e.installed.length));
1915
+ const latestWidth = Math.max("Latest".length, ...outdated.map((e) => e.latest.length));
1916
+ const pad = (s, w) => s.padEnd(w);
1917
+ process.stderr.write(` ${bold(pad("Package", nameWidth))} ${dim(pad("Installed", installedWidth))} ${green(pad("Latest", latestWidth))}
1918
+ `);
1919
+ process.stderr.write(` ${"-".repeat(nameWidth)} ${"-".repeat(installedWidth)} ${"-".repeat(latestWidth)}
1920
+ `);
1921
+ for (const entry of outdated) {
1922
+ process.stderr.write(` ${cyan(pad(entry.name, nameWidth))} ${dim(pad(entry.installed, installedWidth))} ${green(pad(entry.latest, latestWidth))}
1923
+ `);
1924
+ }
1925
+ process.stderr.write(`
1926
+ `);
1927
+ process.stderr.write(` Run ${bold("locus install <package> --upgrade")} to upgrade a package.
1928
+
1929
+ `);
1930
+ if (upToDate.length > 0) {
1931
+ process.stderr.write(` ${dim(`${upToDate.length} package(s) already up to date.`)}
1932
+
1933
+ `);
1934
+ }
1935
+ }
1936
+ async function packagesCommand(args, _flags) {
1937
+ const subcommand = args[0] ?? "list";
1938
+ switch (subcommand) {
1939
+ case "list":
1940
+ listInstalledPackages();
1941
+ break;
1942
+ case "outdated":
1943
+ checkOutdated();
1944
+ break;
1945
+ default:
1946
+ process.stderr.write(`${red("✗")} Unknown subcommand: ${bold(subcommand)}
1947
+ `);
1948
+ process.stderr.write(` Available: ${bold("list")}, ${bold("outdated")}
1949
+ `);
1950
+ process.exit(1);
1951
+ }
1952
+ }
1953
+ var init_packages = __esm(() => {
1954
+ init_terminal();
1955
+ init_registry();
1956
+ init_pkg();
1957
+ });
1958
+
1959
+ // src/commands/install.ts
1960
+ var exports_install = {};
1961
+ __export(exports_install, {
1962
+ installCommand: () => installCommand
1963
+ });
1964
+ import { spawnSync as spawnSync2 } from "node:child_process";
1965
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "node:fs";
1966
+ import { join as join7 } from "node:path";
1967
+ function parsePackageArg(raw) {
1968
+ if (raw.startsWith("@")) {
1969
+ const slashIdx = raw.indexOf("/");
1970
+ if (slashIdx !== -1) {
1971
+ const afterSlash = raw.slice(slashIdx + 1);
1972
+ const atIdx2 = afterSlash.indexOf("@");
1973
+ if (atIdx2 !== -1) {
1974
+ return {
1975
+ name: raw.slice(0, slashIdx + 1 + atIdx2),
1976
+ version: afterSlash.slice(atIdx2 + 1) || undefined
1977
+ };
1978
+ }
1979
+ }
1980
+ return { name: raw, version: undefined };
1981
+ }
1982
+ const atIdx = raw.indexOf("@");
1983
+ if (atIdx !== -1) {
1984
+ return {
1985
+ name: raw.slice(0, atIdx),
1986
+ version: raw.slice(atIdx + 1) || undefined
1987
+ };
1988
+ }
1989
+ return { name: raw, version: undefined };
1990
+ }
1991
+ async function installCommand(args, flags) {
1992
+ const rawArg = args[0];
1993
+ if (!rawArg) {
1994
+ process.stderr.write(`${red("✗")} Usage: locus install <package> [--version <ver>] [--upgrade]
1995
+ `);
1996
+ process.exit(1);
1997
+ return;
1998
+ }
1999
+ const { name: packageInput, version: inlineVersion } = parsePackageArg(rawArg);
2000
+ const pinnedVersion = flags.version ?? inlineVersion;
2001
+ const isUpgrade = flags.upgrade === "true";
2002
+ const packageName = normalizePackageName(packageInput);
2003
+ const versionSuffix = pinnedVersion ? `@${pinnedVersion}` : "@latest";
2004
+ const packageSpec = `${packageName}${versionSuffix}`;
2005
+ const registry = loadRegistry();
2006
+ const existing = registry.packages[packageName];
2007
+ if (existing && !isUpgrade) {
2008
+ process.stderr.write(`${green("✓")} ${bold(packageName)} is already installed ${dim(`(v${existing.version})`)}
2009
+ `);
2010
+ process.stderr.write(` To upgrade, run: ${bold(`locus install ${packageInput} --upgrade`)}
2011
+ `);
2012
+ return;
2013
+ }
2014
+ const packagesDir = getPackagesDir();
2015
+ const action = existing ? "Upgrading" : "Installing";
2016
+ process.stderr.write(`
2017
+ ${action} ${bold(packageSpec)}...
2018
+
2019
+ `);
2020
+ const result = spawnSync2("npm", ["install", packageSpec], {
2021
+ cwd: packagesDir,
2022
+ stdio: "inherit",
2023
+ shell: false
2024
+ });
2025
+ if (result.error) {
2026
+ process.stderr.write(`
2027
+ ${red("✗")} Could not run npm: ${result.error.message}
2028
+ `);
2029
+ process.stderr.write(` Make sure npm is installed and available in your PATH.
2030
+ `);
2031
+ process.exit(1);
2032
+ return;
2033
+ }
2034
+ if (result.status !== 0) {
2035
+ process.stderr.write(`
2036
+ ${red("✗")} Failed to install ${bold(packageSpec)}.
2037
+ `);
2038
+ process.stderr.write(` Make sure the package exists on npm and you have network access.
2039
+ `);
2040
+ process.exit(1);
2041
+ return;
2042
+ }
2043
+ const installedPkgJsonPath = join7(packagesDir, "node_modules", packageName, "package.json");
2044
+ if (!existsSync8(installedPkgJsonPath)) {
2045
+ process.stderr.write(`
2046
+ ${red("✗")} Package installed but package.json not found at:
2047
+ `);
2048
+ process.stderr.write(` ${dim(installedPkgJsonPath)}
2049
+ `);
2050
+ process.exit(1);
2051
+ return;
2052
+ }
2053
+ let installedPkgJson;
2054
+ try {
2055
+ installedPkgJson = JSON.parse(readFileSync6(installedPkgJsonPath, "utf-8"));
2056
+ } catch {
2057
+ process.stderr.write(`
2058
+ ${red("✗")} Could not parse installed package.json.
2059
+ `);
2060
+ process.exit(1);
2061
+ return;
2062
+ }
2063
+ const installedVersion = installedPkgJson.version ?? "unknown";
2064
+ const locusManifest = installedPkgJson.locus;
2065
+ if (!locusManifest) {
2066
+ process.stderr.write(`
2067
+ ${yellow("⚠")} ${bold(packageName)} does not have a "locus" field in package.json.
2068
+ `);
2069
+ process.stderr.write(` It may not integrate fully with the Locus CLI.
2070
+ `);
2071
+ }
2072
+ const binaryPath = resolvePackageBinary(packageName) ?? "";
2073
+ if (!binaryPath) {
2074
+ process.stderr.write(`
2075
+ ${yellow("⚠")} No binary found for ${bold(packageName)} in node_modules/.bin/.
2076
+ `);
2077
+ }
2078
+ const manifest = locusManifest ?? {
2079
+ displayName: packageName,
2080
+ description: "",
2081
+ commands: [],
2082
+ version: installedVersion
2083
+ };
2084
+ const entry = {
2085
+ name: packageName,
2086
+ version: installedVersion,
2087
+ installedAt: new Date().toISOString(),
2088
+ binaryPath,
2089
+ manifest
2090
+ };
2091
+ registry.packages[packageName] = entry;
2092
+ saveRegistry(registry);
2093
+ const verb = existing ? "upgraded" : "installed";
2094
+ process.stderr.write(`
2095
+ ${green("✓")} Package ${verb} successfully!
2096
+
2097
+ `);
2098
+ process.stderr.write(` Package: ${bold(cyan(packageName))}
2099
+ `);
2100
+ process.stderr.write(` Version: ${bold(installedVersion)}
2101
+ `);
2102
+ if (manifest.commands.length > 0) {
2103
+ process.stderr.write(` Commands: ${manifest.commands.map((c) => bold(c)).join(", ")}
2104
+ `);
2105
+ }
2106
+ if (binaryPath) {
2107
+ process.stderr.write(` Binary: ${dim(binaryPath)}
2108
+ `);
2109
+ }
2110
+ process.stderr.write(`
2111
+ `);
2112
+ }
2113
+ var init_install = __esm(() => {
2114
+ init_terminal();
2115
+ init_registry();
2116
+ });
2117
+
2118
+ // src/commands/uninstall.ts
2119
+ var exports_uninstall = {};
2120
+ __export(exports_uninstall, {
2121
+ uninstallCommand: () => uninstallCommand
2122
+ });
2123
+ import { spawnSync as spawnSync3 } from "node:child_process";
2124
+ async function uninstallCommand(args, _flags) {
2125
+ const rawArg = args[0];
2126
+ if (!rawArg) {
2127
+ process.stderr.write(`${red("✗")} Usage: locus uninstall <package>
2128
+ `);
2129
+ process.exit(1);
2130
+ return;
2131
+ }
2132
+ const packageName = normalizePackageName(rawArg);
2133
+ const registry = loadRegistry();
2134
+ const existing = registry.packages[packageName];
2135
+ if (!existing) {
2136
+ process.stderr.write(`${red("✗")} Package ${bold(`'${rawArg}'`)} is not installed.
2137
+ `);
2138
+ process.stderr.write(` Run ${bold("locus packages list")} to see installed packages.
2139
+ `);
2140
+ process.exit(1);
2141
+ return;
2142
+ }
2143
+ const { version } = existing;
2144
+ const packagesDir = getPackagesDir();
2145
+ process.stderr.write(`
2146
+ Uninstalling ${bold(cyan(packageName))} ${dim(`v${version}`)}...
2147
+
2148
+ `);
2149
+ const result = spawnSync3("npm", ["uninstall", packageName], {
2150
+ cwd: packagesDir,
2151
+ stdio: "inherit",
2152
+ shell: false
2153
+ });
2154
+ if (result.error) {
2155
+ process.stderr.write(`
2156
+ ${red("✗")} Could not run npm: ${result.error.message}
2157
+ `);
2158
+ process.stderr.write(` Make sure npm is installed and available in your PATH.
2159
+ `);
2160
+ process.exit(1);
2161
+ return;
2162
+ }
2163
+ if (result.status !== 0) {
2164
+ process.stderr.write(`
2165
+ ${red("✗")} npm uninstall failed for ${bold(packageName)}.
2166
+ `);
2167
+ process.exit(1);
2168
+ return;
2169
+ }
2170
+ delete registry.packages[packageName];
2171
+ saveRegistry(registry);
2172
+ process.stderr.write(`${green("✓")} Uninstalled ${bold(cyan(packageName))} ${dim(`v${version}`)}
2173
+ `);
2174
+ }
2175
+ var init_uninstall = __esm(() => {
2176
+ init_terminal();
2177
+ init_registry();
2178
+ });
2179
+
1686
2180
  // src/commands/config.ts
1687
2181
  var exports_config = {};
1688
2182
  __export(exports_config, {
@@ -1822,16 +2316,16 @@ __export(exports_logs, {
1822
2316
  logsCommand: () => logsCommand
1823
2317
  });
1824
2318
  import {
1825
- existsSync as existsSync6,
2319
+ existsSync as existsSync9,
1826
2320
  readdirSync as readdirSync2,
1827
- readFileSync as readFileSync5,
2321
+ readFileSync as readFileSync7,
1828
2322
  statSync as statSync2,
1829
2323
  unlinkSync as unlinkSync2
1830
2324
  } from "node:fs";
1831
- import { join as join6 } from "node:path";
2325
+ import { join as join8 } from "node:path";
1832
2326
  async function logsCommand(cwd, options) {
1833
- const logsDir = join6(cwd, ".locus", "logs");
1834
- if (!existsSync6(logsDir)) {
2327
+ const logsDir = join8(cwd, ".locus", "logs");
2328
+ if (!existsSync9(logsDir)) {
1835
2329
  process.stderr.write(`${dim("No logs found.")}
1836
2330
  `);
1837
2331
  return;
@@ -1851,7 +2345,7 @@ async function logsCommand(cwd, options) {
1851
2345
  return viewLog(logFiles[0], options.level, options.lines ?? 50);
1852
2346
  }
1853
2347
  function viewLog(logFile, levelFilter, maxLines) {
1854
- const content = readFileSync5(logFile, "utf-8");
2348
+ const content = readFileSync7(logFile, "utf-8");
1855
2349
  const lines = content.trim().split(`
1856
2350
  `).filter(Boolean);
1857
2351
  process.stderr.write(`
@@ -1886,9 +2380,9 @@ async function tailLog(logFile, levelFilter) {
1886
2380
  process.stderr.write(`${bold("Tailing:")} ${dim(logFile)} ${dim("(Ctrl+C to stop)")}
1887
2381
 
1888
2382
  `);
1889
- let lastSize = existsSync6(logFile) ? statSync2(logFile).size : 0;
1890
- if (existsSync6(logFile)) {
1891
- const content = readFileSync5(logFile, "utf-8");
2383
+ let lastSize = existsSync9(logFile) ? statSync2(logFile).size : 0;
2384
+ if (existsSync9(logFile)) {
2385
+ const content = readFileSync7(logFile, "utf-8");
1892
2386
  const lines = content.trim().split(`
1893
2387
  `).filter(Boolean);
1894
2388
  const recent = lines.slice(-10);
@@ -1906,12 +2400,12 @@ async function tailLog(logFile, levelFilter) {
1906
2400
  }
1907
2401
  return new Promise((resolve) => {
1908
2402
  const interval = setInterval(() => {
1909
- if (!existsSync6(logFile))
2403
+ if (!existsSync9(logFile))
1910
2404
  return;
1911
2405
  const currentSize = statSync2(logFile).size;
1912
2406
  if (currentSize <= lastSize)
1913
2407
  return;
1914
- const content = readFileSync5(logFile, "utf-8");
2408
+ const content = readFileSync7(logFile, "utf-8");
1915
2409
  const allLines = content.trim().split(`
1916
2410
  `).filter(Boolean);
1917
2411
  const oldContent = content.slice(0, lastSize);
@@ -1966,7 +2460,7 @@ function cleanLogs(logsDir) {
1966
2460
  `);
1967
2461
  }
1968
2462
  function getLogFiles(logsDir) {
1969
- return readdirSync2(logsDir).filter((f) => f.startsWith("locus-") && f.endsWith(".log")).map((f) => join6(logsDir, f)).sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs);
2463
+ return readdirSync2(logsDir).filter((f) => f.startsWith("locus-") && f.endsWith(".log")).map((f) => join8(logsDir, f)).sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs);
1970
2464
  }
1971
2465
  function formatEntry(entry) {
1972
2466
  const time = dim(new Date(entry.ts).toLocaleTimeString());
@@ -2247,9 +2741,9 @@ var init_stream_renderer = __esm(() => {
2247
2741
  });
2248
2742
 
2249
2743
  // src/repl/image-detect.ts
2250
- import { copyFileSync, existsSync as existsSync7, mkdirSync as mkdirSync6 } from "node:fs";
2251
- import { homedir as homedir2, tmpdir } from "node:os";
2252
- import { basename, extname, join as join7, resolve } from "node:path";
2744
+ import { copyFileSync, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "node:fs";
2745
+ import { homedir as homedir3, tmpdir } from "node:os";
2746
+ import { basename, extname, join as join9, resolve } from "node:path";
2253
2747
  function detectImages(input) {
2254
2748
  const detected = [];
2255
2749
  const byResolved = new Map;
@@ -2347,7 +2841,7 @@ function addIfImage(rawPath, rawMatch, detected, byResolved) {
2347
2841
  return;
2348
2842
  let resolved = stripQuotes(rawPath).replace(/\\ /g, " ");
2349
2843
  if (resolved.startsWith("~/")) {
2350
- resolved = join7(homedir2(), resolved.slice(2));
2844
+ resolved = join9(homedir3(), resolved.slice(2));
2351
2845
  }
2352
2846
  resolved = resolve(resolved);
2353
2847
  const existing = byResolved.get(resolved);
@@ -2360,7 +2854,7 @@ function addIfImage(rawPath, rawMatch, detected, byResolved) {
2360
2854
  ]);
2361
2855
  return;
2362
2856
  }
2363
- const exists = existsSync7(resolved);
2857
+ const exists = existsSync10(resolved);
2364
2858
  let stablePath = resolved;
2365
2859
  if (exists) {
2366
2860
  stablePath = copyToStable(resolved);
@@ -2414,10 +2908,10 @@ function dedupeByResolvedPath(images) {
2414
2908
  }
2415
2909
  function copyToStable(sourcePath) {
2416
2910
  try {
2417
- if (!existsSync7(STABLE_DIR)) {
2418
- mkdirSync6(STABLE_DIR, { recursive: true });
2911
+ if (!existsSync10(STABLE_DIR)) {
2912
+ mkdirSync7(STABLE_DIR, { recursive: true });
2419
2913
  }
2420
- const dest = join7(STABLE_DIR, `${Date.now()}-${basename(sourcePath)}`);
2914
+ const dest = join9(STABLE_DIR, `${Date.now()}-${basename(sourcePath)}`);
2421
2915
  copyFileSync(sourcePath, dest);
2422
2916
  return dest;
2423
2917
  } catch {
@@ -2437,7 +2931,7 @@ var init_image_detect = __esm(() => {
2437
2931
  ".tif",
2438
2932
  ".tiff"
2439
2933
  ]);
2440
- STABLE_DIR = join7(tmpdir(), "locus-images");
2934
+ STABLE_DIR = join9(tmpdir(), "locus-images");
2441
2935
  PLACEHOLDER_ID_PATTERN = /\(locus:\/\/screenshot-(\d+)\)/g;
2442
2936
  });
2443
2937
 
@@ -3112,10 +3606,13 @@ function listenForInterrupt(onInterrupt, onForceExit) {
3112
3606
  const wasRaw = stdin.isRaw;
3113
3607
  stdin.setRawMode(true);
3114
3608
  stdin.resume();
3609
+ process.stderr.write(ENABLE_BRACKETED_PASTE);
3115
3610
  let interrupted = false;
3116
3611
  let interruptTime = 0;
3117
3612
  const handler = (data) => {
3118
3613
  const seq = data.toString();
3614
+ if (seq.startsWith(PASTE_START) || seq === PASTE_END)
3615
+ return;
3119
3616
  if (seq === "\x1B" || seq === "\x03") {
3120
3617
  const now = Date.now();
3121
3618
  if (interrupted && now - interruptTime < 2000 && onForceExit) {
@@ -3135,6 +3632,7 @@ ${dim("Press")} ${yellow("ESC")} ${dim("again to force exit")}\r
3135
3632
  stdin.on("data", handler);
3136
3633
  return () => {
3137
3634
  stdin.removeListener("data", handler);
3635
+ process.stderr.write(DISABLE_BRACKETED_PASTE);
3138
3636
  if (wasRaw !== undefined && stdin.isTTY) {
3139
3637
  stdin.setRawMode(wasRaw);
3140
3638
  }
@@ -3202,7 +3700,7 @@ var init_input_handler = __esm(() => {
3202
3700
  });
3203
3701
 
3204
3702
  // src/ai/claude.ts
3205
- import { execSync as execSync4, spawn } from "node:child_process";
3703
+ import { execSync as execSync4, spawn as spawn2 } from "node:child_process";
3206
3704
 
3207
3705
  class ClaudeRunner {
3208
3706
  name = "claude";
@@ -3248,7 +3746,7 @@ class ClaudeRunner {
3248
3746
  const env = { ...process.env };
3249
3747
  delete env.CLAUDECODE;
3250
3748
  delete env.CLAUDE_CODE;
3251
- this.process = spawn("claude", args, {
3749
+ this.process = spawn2("claude", args, {
3252
3750
  cwd: options.cwd,
3253
3751
  stdio: ["pipe", "pipe", "pipe"],
3254
3752
  env
@@ -3330,7 +3828,7 @@ var init_claude = __esm(() => {
3330
3828
  });
3331
3829
 
3332
3830
  // src/ai/codex.ts
3333
- import { execSync as execSync5, spawn as spawn2 } from "node:child_process";
3831
+ import { execSync as execSync5, spawn as spawn3 } from "node:child_process";
3334
3832
  function buildCodexArgs(model) {
3335
3833
  const args = ["exec", "--full-auto", "--skip-git-repo-check"];
3336
3834
  if (model) {
@@ -3374,7 +3872,7 @@ class CodexRunner {
3374
3872
  return new Promise((resolve2) => {
3375
3873
  let output = "";
3376
3874
  let errorOutput = "";
3377
- this.process = spawn2("codex", args, {
3875
+ this.process = spawn3("codex", args, {
3378
3876
  cwd: options.cwd,
3379
3877
  stdio: ["pipe", "pipe", "pipe"],
3380
3878
  env: { ...process.env }
@@ -4964,8 +5462,8 @@ var init_sprint = __esm(() => {
4964
5462
 
4965
5463
  // src/core/prompt-builder.ts
4966
5464
  import { execSync as execSync6 } from "node:child_process";
4967
- import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "node:fs";
4968
- import { join as join8 } from "node:path";
5465
+ import { existsSync as existsSync11, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "node:fs";
5466
+ import { join as join10 } from "node:path";
4969
5467
  function buildExecutionPrompt(ctx) {
4970
5468
  const sections = [];
4971
5469
  sections.push(buildSystemContext(ctx.projectRoot));
@@ -4995,13 +5493,13 @@ function buildFeedbackPrompt(ctx) {
4995
5493
  }
4996
5494
  function buildReplPrompt(userMessage, projectRoot, _config, previousMessages) {
4997
5495
  const sections = [];
4998
- const locusmd = readFileSafe(join8(projectRoot, "LOCUS.md"));
5496
+ const locusmd = readFileSafe(join10(projectRoot, "LOCUS.md"));
4999
5497
  if (locusmd) {
5000
5498
  sections.push(`# Project Instructions
5001
5499
 
5002
5500
  ${locusmd}`);
5003
5501
  }
5004
- const learnings = readFileSafe(join8(projectRoot, ".locus", "LEARNINGS.md"));
5502
+ const learnings = readFileSafe(join10(projectRoot, ".locus", "LEARNINGS.md"));
5005
5503
  if (learnings) {
5006
5504
  sections.push(`# Past Learnings
5007
5505
 
@@ -5027,24 +5525,24 @@ ${userMessage}`);
5027
5525
  }
5028
5526
  function buildSystemContext(projectRoot) {
5029
5527
  const parts = ["# System Context"];
5030
- const locusmd = readFileSafe(join8(projectRoot, "LOCUS.md"));
5528
+ const locusmd = readFileSafe(join10(projectRoot, "LOCUS.md"));
5031
5529
  if (locusmd) {
5032
5530
  parts.push(`## Project Instructions (LOCUS.md)
5033
5531
 
5034
5532
  ${locusmd}`);
5035
5533
  }
5036
- const learnings = readFileSafe(join8(projectRoot, ".locus", "LEARNINGS.md"));
5534
+ const learnings = readFileSafe(join10(projectRoot, ".locus", "LEARNINGS.md"));
5037
5535
  if (learnings) {
5038
5536
  parts.push(`## Past Learnings
5039
5537
 
5040
5538
  ${learnings}`);
5041
5539
  }
5042
- const discussionsDir = join8(projectRoot, ".locus", "discussions");
5043
- if (existsSync8(discussionsDir)) {
5540
+ const discussionsDir = join10(projectRoot, ".locus", "discussions");
5541
+ if (existsSync11(discussionsDir)) {
5044
5542
  try {
5045
5543
  const files = readdirSync3(discussionsDir).filter((f) => f.endsWith(".md")).slice(0, 3);
5046
5544
  for (const file of files) {
5047
- const content = readFileSafe(join8(discussionsDir, file));
5545
+ const content = readFileSafe(join10(discussionsDir, file));
5048
5546
  if (content) {
5049
5547
  parts.push(`## Discussion: ${file.replace(".md", "")}
5050
5548
 
@@ -5190,9 +5688,9 @@ function buildFeedbackInstructions() {
5190
5688
  }
5191
5689
  function readFileSafe(path) {
5192
5690
  try {
5193
- if (!existsSync8(path))
5691
+ if (!existsSync11(path))
5194
5692
  return null;
5195
- return readFileSync6(path, "utf-8");
5693
+ return readFileSync8(path, "utf-8");
5196
5694
  } catch {
5197
5695
  return null;
5198
5696
  }
@@ -5622,7 +6120,7 @@ var init_commands = __esm(() => {
5622
6120
 
5623
6121
  // src/repl/completions.ts
5624
6122
  import { readdirSync as readdirSync4 } from "node:fs";
5625
- import { basename as basename2, dirname as dirname3, join as join9 } from "node:path";
6123
+ import { basename as basename2, dirname as dirname3, join as join11 } from "node:path";
5626
6124
 
5627
6125
  class SlashCommandCompletion {
5628
6126
  commands;
@@ -5677,7 +6175,7 @@ class FilePathCompletion {
5677
6175
  }
5678
6176
  findMatches(partial) {
5679
6177
  try {
5680
- const dir = partial.includes("/") ? join9(this.projectRoot, dirname3(partial)) : this.projectRoot;
6178
+ const dir = partial.includes("/") ? join11(this.projectRoot, dirname3(partial)) : this.projectRoot;
5681
6179
  const prefix = basename2(partial);
5682
6180
  const entries = readdirSync4(dir, { withFileTypes: true });
5683
6181
  return entries.filter((e) => {
@@ -5713,14 +6211,14 @@ class CombinedCompletion {
5713
6211
  var init_completions = () => {};
5714
6212
 
5715
6213
  // src/repl/input-history.ts
5716
- import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
5717
- import { dirname as dirname4, join as join10 } from "node:path";
6214
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
6215
+ import { dirname as dirname4, join as join12 } from "node:path";
5718
6216
 
5719
6217
  class InputHistory {
5720
6218
  entries = [];
5721
6219
  filePath;
5722
6220
  constructor(projectRoot) {
5723
- this.filePath = join10(projectRoot, ".locus", "sessions", ".input-history");
6221
+ this.filePath = join12(projectRoot, ".locus", "sessions", ".input-history");
5724
6222
  this.load();
5725
6223
  }
5726
6224
  add(text) {
@@ -5759,9 +6257,9 @@ class InputHistory {
5759
6257
  }
5760
6258
  load() {
5761
6259
  try {
5762
- if (!existsSync9(this.filePath))
6260
+ if (!existsSync12(this.filePath))
5763
6261
  return;
5764
- const content = readFileSync7(this.filePath, "utf-8");
6262
+ const content = readFileSync9(this.filePath, "utf-8");
5765
6263
  this.entries = content.split(`
5766
6264
  `).map((line) => this.unescape(line)).filter(Boolean);
5767
6265
  } catch {}
@@ -5769,12 +6267,12 @@ class InputHistory {
5769
6267
  save() {
5770
6268
  try {
5771
6269
  const dir = dirname4(this.filePath);
5772
- if (!existsSync9(dir)) {
5773
- mkdirSync7(dir, { recursive: true });
6270
+ if (!existsSync12(dir)) {
6271
+ mkdirSync8(dir, { recursive: true });
5774
6272
  }
5775
6273
  const content = this.entries.map((e) => this.escape(e)).join(`
5776
6274
  `);
5777
- writeFileSync5(this.filePath, content, "utf-8");
6275
+ writeFileSync6(this.filePath, content, "utf-8");
5778
6276
  } catch {}
5779
6277
  }
5780
6278
  escape(text) {
@@ -5801,21 +6299,21 @@ var init_model_config = __esm(() => {
5801
6299
  // src/repl/session-manager.ts
5802
6300
  import { randomBytes } from "node:crypto";
5803
6301
  import {
5804
- existsSync as existsSync10,
5805
- mkdirSync as mkdirSync8,
6302
+ existsSync as existsSync13,
6303
+ mkdirSync as mkdirSync9,
5806
6304
  readdirSync as readdirSync5,
5807
- readFileSync as readFileSync8,
6305
+ readFileSync as readFileSync10,
5808
6306
  unlinkSync as unlinkSync3,
5809
- writeFileSync as writeFileSync6
6307
+ writeFileSync as writeFileSync7
5810
6308
  } from "node:fs";
5811
- import { basename as basename3, join as join11 } from "node:path";
6309
+ import { basename as basename3, join as join13 } from "node:path";
5812
6310
 
5813
6311
  class SessionManager {
5814
6312
  sessionsDir;
5815
6313
  constructor(projectRoot) {
5816
- this.sessionsDir = join11(projectRoot, ".locus", "sessions");
5817
- if (!existsSync10(this.sessionsDir)) {
5818
- mkdirSync8(this.sessionsDir, { recursive: true });
6314
+ this.sessionsDir = join13(projectRoot, ".locus", "sessions");
6315
+ if (!existsSync13(this.sessionsDir)) {
6316
+ mkdirSync9(this.sessionsDir, { recursive: true });
5819
6317
  }
5820
6318
  }
5821
6319
  create(options) {
@@ -5840,14 +6338,14 @@ class SessionManager {
5840
6338
  }
5841
6339
  isPersisted(sessionOrId) {
5842
6340
  const sessionId = typeof sessionOrId === "string" ? sessionOrId : sessionOrId.id;
5843
- return existsSync10(this.getSessionPath(sessionId));
6341
+ return existsSync13(this.getSessionPath(sessionId));
5844
6342
  }
5845
6343
  load(idOrPrefix) {
5846
6344
  const files = this.listSessionFiles();
5847
6345
  const exactPath = this.getSessionPath(idOrPrefix);
5848
- if (existsSync10(exactPath)) {
6346
+ if (existsSync13(exactPath)) {
5849
6347
  try {
5850
- return JSON.parse(readFileSync8(exactPath, "utf-8"));
6348
+ return JSON.parse(readFileSync10(exactPath, "utf-8"));
5851
6349
  } catch {
5852
6350
  return null;
5853
6351
  }
@@ -5855,7 +6353,7 @@ class SessionManager {
5855
6353
  const matches = files.filter((f) => basename3(f, ".json").startsWith(idOrPrefix));
5856
6354
  if (matches.length === 1) {
5857
6355
  try {
5858
- return JSON.parse(readFileSync8(matches[0], "utf-8"));
6356
+ return JSON.parse(readFileSync10(matches[0], "utf-8"));
5859
6357
  } catch {
5860
6358
  return null;
5861
6359
  }
@@ -5868,7 +6366,7 @@ class SessionManager {
5868
6366
  save(session) {
5869
6367
  session.updated = new Date().toISOString();
5870
6368
  const path = this.getSessionPath(session.id);
5871
- writeFileSync6(path, `${JSON.stringify(session, null, 2)}
6369
+ writeFileSync7(path, `${JSON.stringify(session, null, 2)}
5872
6370
  `, "utf-8");
5873
6371
  }
5874
6372
  addMessage(session, message) {
@@ -5880,7 +6378,7 @@ class SessionManager {
5880
6378
  const sessions = [];
5881
6379
  for (const file of files) {
5882
6380
  try {
5883
- const session = JSON.parse(readFileSync8(file, "utf-8"));
6381
+ const session = JSON.parse(readFileSync10(file, "utf-8"));
5884
6382
  sessions.push({
5885
6383
  id: session.id,
5886
6384
  created: session.created,
@@ -5895,7 +6393,7 @@ class SessionManager {
5895
6393
  }
5896
6394
  delete(sessionId) {
5897
6395
  const path = this.getSessionPath(sessionId);
5898
- if (existsSync10(path)) {
6396
+ if (existsSync13(path)) {
5899
6397
  unlinkSync3(path);
5900
6398
  return true;
5901
6399
  }
@@ -5907,7 +6405,7 @@ class SessionManager {
5907
6405
  let pruned = 0;
5908
6406
  const withStats = files.map((f) => {
5909
6407
  try {
5910
- const session = JSON.parse(readFileSync8(f, "utf-8"));
6408
+ const session = JSON.parse(readFileSync10(f, "utf-8"));
5911
6409
  return { path: f, updated: new Date(session.updated).getTime() };
5912
6410
  } catch {
5913
6411
  return { path: f, updated: 0 };
@@ -5925,7 +6423,7 @@ class SessionManager {
5925
6423
  const remaining = withStats.length - pruned;
5926
6424
  if (remaining > MAX_SESSIONS) {
5927
6425
  const toRemove = remaining - MAX_SESSIONS;
5928
- const alive = withStats.filter((e) => existsSync10(e.path));
6426
+ const alive = withStats.filter((e) => existsSync13(e.path));
5929
6427
  for (let i = 0;i < toRemove && i < alive.length; i++) {
5930
6428
  try {
5931
6429
  unlinkSync3(alive[i].path);
@@ -5940,7 +6438,7 @@ class SessionManager {
5940
6438
  }
5941
6439
  listSessionFiles() {
5942
6440
  try {
5943
- return readdirSync5(this.sessionsDir).filter((f) => f.endsWith(".json") && !f.startsWith(".")).map((f) => join11(this.sessionsDir, f));
6441
+ return readdirSync5(this.sessionsDir).filter((f) => f.endsWith(".json") && !f.startsWith(".")).map((f) => join13(this.sessionsDir, f));
5944
6442
  } catch {
5945
6443
  return [];
5946
6444
  }
@@ -5949,7 +6447,7 @@ class SessionManager {
5949
6447
  return randomBytes(6).toString("hex");
5950
6448
  }
5951
6449
  getSessionPath(sessionId) {
5952
- return join11(this.sessionsDir, `${sessionId}.json`);
6450
+ return join13(this.sessionsDir, `${sessionId}.json`);
5953
6451
  }
5954
6452
  }
5955
6453
  var MAX_SESSIONS = 50, SESSION_MAX_AGE_MS;
@@ -6734,22 +7232,22 @@ var init_conflict = __esm(() => {
6734
7232
 
6735
7233
  // src/core/run-state.ts
6736
7234
  import {
6737
- existsSync as existsSync11,
6738
- mkdirSync as mkdirSync9,
6739
- readFileSync as readFileSync9,
7235
+ existsSync as existsSync14,
7236
+ mkdirSync as mkdirSync10,
7237
+ readFileSync as readFileSync11,
6740
7238
  unlinkSync as unlinkSync4,
6741
- writeFileSync as writeFileSync7
7239
+ writeFileSync as writeFileSync8
6742
7240
  } from "node:fs";
6743
- import { dirname as dirname5, join as join12 } from "node:path";
7241
+ import { dirname as dirname5, join as join14 } from "node:path";
6744
7242
  function getRunStatePath(projectRoot) {
6745
- return join12(projectRoot, ".locus", "run-state.json");
7243
+ return join14(projectRoot, ".locus", "run-state.json");
6746
7244
  }
6747
7245
  function loadRunState(projectRoot) {
6748
7246
  const path = getRunStatePath(projectRoot);
6749
- if (!existsSync11(path))
7247
+ if (!existsSync14(path))
6750
7248
  return null;
6751
7249
  try {
6752
- return JSON.parse(readFileSync9(path, "utf-8"));
7250
+ return JSON.parse(readFileSync11(path, "utf-8"));
6753
7251
  } catch {
6754
7252
  getLogger().warn("Corrupted run-state.json, ignoring");
6755
7253
  return null;
@@ -6758,15 +7256,15 @@ function loadRunState(projectRoot) {
6758
7256
  function saveRunState(projectRoot, state) {
6759
7257
  const path = getRunStatePath(projectRoot);
6760
7258
  const dir = dirname5(path);
6761
- if (!existsSync11(dir)) {
6762
- mkdirSync9(dir, { recursive: true });
7259
+ if (!existsSync14(dir)) {
7260
+ mkdirSync10(dir, { recursive: true });
6763
7261
  }
6764
- writeFileSync7(path, `${JSON.stringify(state, null, 2)}
7262
+ writeFileSync8(path, `${JSON.stringify(state, null, 2)}
6765
7263
  `, "utf-8");
6766
7264
  }
6767
7265
  function clearRunState(projectRoot) {
6768
7266
  const path = getRunStatePath(projectRoot);
6769
- if (existsSync11(path)) {
7267
+ if (existsSync14(path)) {
6770
7268
  unlinkSync4(path);
6771
7269
  }
6772
7270
  }
@@ -6907,8 +7405,8 @@ var init_shutdown = __esm(() => {
6907
7405
 
6908
7406
  // src/core/worktree.ts
6909
7407
  import { execSync as execSync11 } from "node:child_process";
6910
- import { existsSync as existsSync12, readdirSync as readdirSync6, realpathSync, statSync as statSync3 } from "node:fs";
6911
- import { join as join13 } from "node:path";
7408
+ import { existsSync as existsSync15, readdirSync as readdirSync6, realpathSync, statSync as statSync3 } from "node:fs";
7409
+ import { join as join15 } from "node:path";
6912
7410
  function git3(args, cwd) {
6913
7411
  return execSync11(`git ${args}`, {
6914
7412
  cwd,
@@ -6924,10 +7422,10 @@ function gitSafe2(args, cwd) {
6924
7422
  }
6925
7423
  }
6926
7424
  function getWorktreeDir(projectRoot) {
6927
- return join13(projectRoot, ".locus", "worktrees");
7425
+ return join15(projectRoot, ".locus", "worktrees");
6928
7426
  }
6929
7427
  function getWorktreePath(projectRoot, issueNumber) {
6930
- return join13(getWorktreeDir(projectRoot), `issue-${issueNumber}`);
7428
+ return join15(getWorktreeDir(projectRoot), `issue-${issueNumber}`);
6931
7429
  }
6932
7430
  function generateBranchName(issueNumber) {
6933
7431
  const randomSuffix = Math.random().toString(36).slice(2, 8);
@@ -6947,7 +7445,7 @@ function getWorktreeBranch(worktreePath) {
6947
7445
  function createWorktree(projectRoot, issueNumber, baseBranch) {
6948
7446
  const log = getLogger();
6949
7447
  const worktreePath = getWorktreePath(projectRoot, issueNumber);
6950
- if (existsSync12(worktreePath)) {
7448
+ if (existsSync15(worktreePath)) {
6951
7449
  log.verbose(`Worktree already exists for issue #${issueNumber}`);
6952
7450
  const existingBranch = getWorktreeBranch(worktreePath) ?? `locus/issue-${issueNumber}`;
6953
7451
  return {
@@ -6974,7 +7472,7 @@ function createWorktree(projectRoot, issueNumber, baseBranch) {
6974
7472
  function removeWorktree(projectRoot, issueNumber) {
6975
7473
  const log = getLogger();
6976
7474
  const worktreePath = getWorktreePath(projectRoot, issueNumber);
6977
- if (!existsSync12(worktreePath)) {
7475
+ if (!existsSync15(worktreePath)) {
6978
7476
  log.verbose(`Worktree for issue #${issueNumber} does not exist`);
6979
7477
  return;
6980
7478
  }
@@ -6993,7 +7491,7 @@ function removeWorktree(projectRoot, issueNumber) {
6993
7491
  function listWorktrees(projectRoot) {
6994
7492
  const log = getLogger();
6995
7493
  const worktreeDir = getWorktreeDir(projectRoot);
6996
- if (!existsSync12(worktreeDir)) {
7494
+ if (!existsSync15(worktreeDir)) {
6997
7495
  return [];
6998
7496
  }
6999
7497
  const entries = readdirSync6(worktreeDir).filter((entry) => entry.startsWith("issue-"));
@@ -7013,7 +7511,7 @@ function listWorktrees(projectRoot) {
7013
7511
  if (!match)
7014
7512
  continue;
7015
7513
  const issueNumber = Number.parseInt(match[1], 10);
7016
- const path = join13(worktreeDir, entry);
7514
+ const path = join15(worktreeDir, entry);
7017
7515
  const branch = getWorktreeBranch(path) ?? `locus/issue-${issueNumber}`;
7018
7516
  let resolvedPath;
7019
7517
  try {
@@ -7253,7 +7751,18 @@ ${bold("Summary:")}
7253
7751
  issue: t.issue,
7254
7752
  title: issues.find((i) => i.number === t.issue)?.title
7255
7753
  }));
7256
- await createSprintPR(projectRoot, config, sprintName, branchName, completedTasks);
7754
+ const prNumber = await createSprintPR(projectRoot, config, sprintName, branchName, completedTasks);
7755
+ if (prNumber !== undefined) {
7756
+ try {
7757
+ execSync12(`git checkout ${config.agent.baseBranch}`, {
7758
+ cwd: projectRoot,
7759
+ encoding: "utf-8",
7760
+ stdio: ["pipe", "pipe", "pipe"]
7761
+ });
7762
+ process.stderr.write(` ${dim(`Checked out ${config.agent.baseBranch}`)}
7763
+ `);
7764
+ } catch {}
7765
+ }
7257
7766
  }
7258
7767
  if (stats.failed === 0) {
7259
7768
  clearRunState(projectRoot);
@@ -7490,7 +7999,18 @@ ${bold("Resume complete:")} ${green(`✓ ${finalStats.done}`)} ${finalStats.fail
7490
7999
  `);
7491
8000
  if (isSprintRun && state.branch && state.sprint && finalStats.done > 0) {
7492
8001
  const completedTasks = state.tasks.filter((t) => t.status === "done").map((t) => ({ issue: t.issue }));
7493
- await createSprintPR(projectRoot, config, state.sprint, state.branch, completedTasks);
8002
+ const prNumber = await createSprintPR(projectRoot, config, state.sprint, state.branch, completedTasks);
8003
+ if (prNumber !== undefined) {
8004
+ try {
8005
+ execSync12(`git checkout ${config.agent.baseBranch}`, {
8006
+ cwd: projectRoot,
8007
+ encoding: "utf-8",
8008
+ stdio: ["pipe", "pipe", "pipe"]
8009
+ });
8010
+ process.stderr.write(` ${dim(`Checked out ${config.agent.baseBranch}`)}
8011
+ `);
8012
+ } catch {}
8013
+ }
7494
8014
  }
7495
8015
  if (finalStats.failed === 0) {
7496
8016
  clearRunState(projectRoot);
@@ -7696,13 +8216,13 @@ __export(exports_plan, {
7696
8216
  parsePlanArgs: () => parsePlanArgs
7697
8217
  });
7698
8218
  import {
7699
- existsSync as existsSync13,
7700
- mkdirSync as mkdirSync10,
8219
+ existsSync as existsSync16,
8220
+ mkdirSync as mkdirSync11,
7701
8221
  readdirSync as readdirSync7,
7702
- readFileSync as readFileSync10,
7703
- writeFileSync as writeFileSync8
8222
+ readFileSync as readFileSync12,
8223
+ writeFileSync as writeFileSync9
7704
8224
  } from "node:fs";
7705
- import { join as join14 } from "node:path";
8225
+ import { join as join16 } from "node:path";
7706
8226
  function printHelp() {
7707
8227
  process.stderr.write(`
7708
8228
  ${bold("locus plan")} — AI-powered sprint planning
@@ -7733,12 +8253,12 @@ function normalizeSprintName(name) {
7733
8253
  return name.trim().toLowerCase();
7734
8254
  }
7735
8255
  function getPlansDir(projectRoot) {
7736
- return join14(projectRoot, ".locus", "plans");
8256
+ return join16(projectRoot, ".locus", "plans");
7737
8257
  }
7738
8258
  function ensurePlansDir(projectRoot) {
7739
8259
  const dir = getPlansDir(projectRoot);
7740
- if (!existsSync13(dir)) {
7741
- mkdirSync10(dir, { recursive: true });
8260
+ if (!existsSync16(dir)) {
8261
+ mkdirSync11(dir, { recursive: true });
7742
8262
  }
7743
8263
  return dir;
7744
8264
  }
@@ -7747,14 +8267,14 @@ function generateId() {
7747
8267
  }
7748
8268
  function loadPlanFile(projectRoot, id) {
7749
8269
  const dir = getPlansDir(projectRoot);
7750
- if (!existsSync13(dir))
8270
+ if (!existsSync16(dir))
7751
8271
  return null;
7752
8272
  const files = readdirSync7(dir).filter((f) => f.endsWith(".json"));
7753
8273
  const match = files.find((f) => f.startsWith(id));
7754
8274
  if (!match)
7755
8275
  return null;
7756
8276
  try {
7757
- const content = readFileSync10(join14(dir, match), "utf-8");
8277
+ const content = readFileSync12(join16(dir, match), "utf-8");
7758
8278
  return JSON.parse(content);
7759
8279
  } catch {
7760
8280
  return null;
@@ -7800,7 +8320,7 @@ async function planCommand(projectRoot, args, flags = {}) {
7800
8320
  }
7801
8321
  function handleListPlans(projectRoot) {
7802
8322
  const dir = getPlansDir(projectRoot);
7803
- if (!existsSync13(dir)) {
8323
+ if (!existsSync16(dir)) {
7804
8324
  process.stderr.write(`${dim("No saved plans yet.")}
7805
8325
  `);
7806
8326
  return;
@@ -7818,7 +8338,7 @@ ${bold("Saved Plans:")}
7818
8338
  for (const file of files) {
7819
8339
  const id = file.replace(".json", "");
7820
8340
  try {
7821
- const content = readFileSync10(join14(dir, file), "utf-8");
8341
+ const content = readFileSync12(join16(dir, file), "utf-8");
7822
8342
  const plan = JSON.parse(content);
7823
8343
  const date = plan.createdAt ? plan.createdAt.slice(0, 10) : "";
7824
8344
  const issueCount = Array.isArray(plan.issues) ? plan.issues.length : 0;
@@ -7831,7 +8351,7 @@ ${bold("Saved Plans:")}
7831
8351
  }
7832
8352
  process.stderr.write(`
7833
8353
  `);
7834
- process.stderr.write(` Approve a plan: ${bold("locus plan approve <id>")}
8354
+ process.stderr.write(` Approve a plan: ${bold("locus plan approve <id> --sprint <name>")}
7835
8355
 
7836
8356
  `);
7837
8357
  }
@@ -7929,7 +8449,7 @@ ${bold("Approving plan:")}
7929
8449
  async function handleAIPlan(projectRoot, config, directive, sprintName, flags) {
7930
8450
  const id = generateId();
7931
8451
  const plansDir = ensurePlansDir(projectRoot);
7932
- const planPath = join14(plansDir, `${id}.json`);
8452
+ const planPath = join16(plansDir, `${id}.json`);
7933
8453
  const planPathRelative = `.locus/plans/${id}.json`;
7934
8454
  const displayDirective = directive;
7935
8455
  process.stderr.write(`
@@ -7961,7 +8481,7 @@ ${red("✗")} Planning failed: ${aiResult.error}
7961
8481
  `);
7962
8482
  return;
7963
8483
  }
7964
- if (!existsSync13(planPath)) {
8484
+ if (!existsSync16(planPath)) {
7965
8485
  process.stderr.write(`
7966
8486
  ${yellow("⚠")} Plan file was not created at ${bold(planPathRelative)}.
7967
8487
  `);
@@ -7971,7 +8491,7 @@ ${yellow("⚠")} Plan file was not created at ${bold(planPathRelative)}.
7971
8491
  }
7972
8492
  let plan;
7973
8493
  try {
7974
- const content = readFileSync10(planPath, "utf-8");
8494
+ const content = readFileSync12(planPath, "utf-8");
7975
8495
  plan = JSON.parse(content);
7976
8496
  } catch {
7977
8497
  process.stderr.write(`
@@ -7993,7 +8513,7 @@ ${yellow("⚠")} Plan file has no issues.
7993
8513
  plan.sprint = sprintName;
7994
8514
  if (!plan.createdAt)
7995
8515
  plan.createdAt = new Date().toISOString();
7996
- writeFileSync8(planPath, JSON.stringify(plan, null, 2), "utf-8");
8516
+ writeFileSync9(planPath, JSON.stringify(plan, null, 2), "utf-8");
7997
8517
  process.stderr.write(`
7998
8518
  ${bold("Plan saved:")} ${cyan(id)}
7999
8519
 
@@ -8126,16 +8646,16 @@ function buildPlanningPrompt(projectRoot, config, directive, sprintName, id, pla
8126
8646
  parts.push(`SPRINT: ${sprintName}`);
8127
8647
  }
8128
8648
  parts.push("");
8129
- const locusPath = join14(projectRoot, "LOCUS.md");
8130
- if (existsSync13(locusPath)) {
8131
- const content = readFileSync10(locusPath, "utf-8");
8649
+ const locusPath = join16(projectRoot, "LOCUS.md");
8650
+ if (existsSync16(locusPath)) {
8651
+ const content = readFileSync12(locusPath, "utf-8");
8132
8652
  parts.push("PROJECT CONTEXT (LOCUS.md):");
8133
8653
  parts.push(content.slice(0, 3000));
8134
8654
  parts.push("");
8135
8655
  }
8136
- const learningsPath = join14(projectRoot, ".locus", "LEARNINGS.md");
8137
- if (existsSync13(learningsPath)) {
8138
- const content = readFileSync10(learningsPath, "utf-8");
8656
+ const learningsPath = join16(projectRoot, ".locus", "LEARNINGS.md");
8657
+ if (existsSync16(learningsPath)) {
8658
+ const content = readFileSync12(learningsPath, "utf-8");
8139
8659
  parts.push("PAST LEARNINGS:");
8140
8660
  parts.push(content.slice(0, 2000));
8141
8661
  parts.push("");
@@ -8312,8 +8832,8 @@ __export(exports_review, {
8312
8832
  reviewCommand: () => reviewCommand
8313
8833
  });
8314
8834
  import { execSync as execSync13 } from "node:child_process";
8315
- import { existsSync as existsSync14, readFileSync as readFileSync11 } from "node:fs";
8316
- import { join as join15 } from "node:path";
8835
+ import { existsSync as existsSync17, readFileSync as readFileSync13 } from "node:fs";
8836
+ import { join as join17 } from "node:path";
8317
8837
  function printHelp2() {
8318
8838
  process.stderr.write(`
8319
8839
  ${bold("locus review")} — AI-powered code review
@@ -8470,9 +8990,9 @@ function buildReviewPrompt(projectRoot, config, pr, diff, focus) {
8470
8990
  const parts = [];
8471
8991
  parts.push(`You are an expert code reviewer for the ${config.github.owner}/${config.github.repo} repository.`);
8472
8992
  parts.push("");
8473
- const locusPath = join15(projectRoot, "LOCUS.md");
8474
- if (existsSync14(locusPath)) {
8475
- const content = readFileSync11(locusPath, "utf-8");
8993
+ const locusPath = join17(projectRoot, "LOCUS.md");
8994
+ if (existsSync17(locusPath)) {
8995
+ const content = readFileSync13(locusPath, "utf-8");
8476
8996
  parts.push("PROJECT CONTEXT:");
8477
8997
  parts.push(content.slice(0, 2000));
8478
8998
  parts.push("");
@@ -8774,14 +9294,14 @@ __export(exports_discuss, {
8774
9294
  discussCommand: () => discussCommand
8775
9295
  });
8776
9296
  import {
8777
- existsSync as existsSync15,
8778
- mkdirSync as mkdirSync11,
9297
+ existsSync as existsSync18,
9298
+ mkdirSync as mkdirSync12,
8779
9299
  readdirSync as readdirSync8,
8780
- readFileSync as readFileSync12,
9300
+ readFileSync as readFileSync14,
8781
9301
  unlinkSync as unlinkSync5,
8782
- writeFileSync as writeFileSync9
9302
+ writeFileSync as writeFileSync10
8783
9303
  } from "node:fs";
8784
- import { join as join16 } from "node:path";
9304
+ import { join as join18 } from "node:path";
8785
9305
  function printHelp4() {
8786
9306
  process.stderr.write(`
8787
9307
  ${bold("locus discuss")} — AI-powered architectural discussions
@@ -8803,12 +9323,12 @@ ${bold("Examples:")}
8803
9323
  `);
8804
9324
  }
8805
9325
  function getDiscussionsDir(projectRoot) {
8806
- return join16(projectRoot, ".locus", "discussions");
9326
+ return join18(projectRoot, ".locus", "discussions");
8807
9327
  }
8808
9328
  function ensureDiscussionsDir(projectRoot) {
8809
9329
  const dir = getDiscussionsDir(projectRoot);
8810
- if (!existsSync15(dir)) {
8811
- mkdirSync11(dir, { recursive: true });
9330
+ if (!existsSync18(dir)) {
9331
+ mkdirSync12(dir, { recursive: true });
8812
9332
  }
8813
9333
  return dir;
8814
9334
  }
@@ -8842,7 +9362,7 @@ async function discussCommand(projectRoot, args, flags = {}) {
8842
9362
  }
8843
9363
  function listDiscussions(projectRoot) {
8844
9364
  const dir = getDiscussionsDir(projectRoot);
8845
- if (!existsSync15(dir)) {
9365
+ if (!existsSync18(dir)) {
8846
9366
  process.stderr.write(`${dim("No discussions yet.")}
8847
9367
  `);
8848
9368
  return;
@@ -8859,7 +9379,7 @@ ${bold("Discussions:")}
8859
9379
  `);
8860
9380
  for (const file of files) {
8861
9381
  const id = file.replace(".md", "");
8862
- const content = readFileSync12(join16(dir, file), "utf-8");
9382
+ const content = readFileSync14(join18(dir, file), "utf-8");
8863
9383
  const titleMatch = content.match(/^#\s+(.+)/m);
8864
9384
  const title = titleMatch ? titleMatch[1] : id;
8865
9385
  const dateMatch = content.match(/\*\*Date:\*\*\s*(.+)/);
@@ -8877,7 +9397,7 @@ function showDiscussion(projectRoot, id) {
8877
9397
  return;
8878
9398
  }
8879
9399
  const dir = getDiscussionsDir(projectRoot);
8880
- if (!existsSync15(dir)) {
9400
+ if (!existsSync18(dir)) {
8881
9401
  process.stderr.write(`${red("✗")} No discussions found.
8882
9402
  `);
8883
9403
  return;
@@ -8889,7 +9409,7 @@ function showDiscussion(projectRoot, id) {
8889
9409
  `);
8890
9410
  return;
8891
9411
  }
8892
- const content = readFileSync12(join16(dir, match), "utf-8");
9412
+ const content = readFileSync14(join18(dir, match), "utf-8");
8893
9413
  process.stdout.write(`${content}
8894
9414
  `);
8895
9415
  }
@@ -8900,7 +9420,7 @@ function deleteDiscussion(projectRoot, id) {
8900
9420
  return;
8901
9421
  }
8902
9422
  const dir = getDiscussionsDir(projectRoot);
8903
- if (!existsSync15(dir)) {
9423
+ if (!existsSync18(dir)) {
8904
9424
  process.stderr.write(`${red("✗")} No discussions found.
8905
9425
  `);
8906
9426
  return;
@@ -8912,7 +9432,7 @@ function deleteDiscussion(projectRoot, id) {
8912
9432
  `);
8913
9433
  return;
8914
9434
  }
8915
- unlinkSync5(join16(dir, match));
9435
+ unlinkSync5(join18(dir, match));
8916
9436
  process.stderr.write(`${green("✓")} Deleted discussion: ${match.replace(".md", "")}
8917
9437
  `);
8918
9438
  }
@@ -8925,7 +9445,7 @@ async function convertDiscussionToPlan(projectRoot, id) {
8925
9445
  return;
8926
9446
  }
8927
9447
  const dir = getDiscussionsDir(projectRoot);
8928
- if (!existsSync15(dir)) {
9448
+ if (!existsSync18(dir)) {
8929
9449
  process.stderr.write(`${red("✗")} No discussions found.
8930
9450
  `);
8931
9451
  return;
@@ -8937,7 +9457,7 @@ async function convertDiscussionToPlan(projectRoot, id) {
8937
9457
  `);
8938
9458
  return;
8939
9459
  }
8940
- const content = readFileSync12(join16(dir, match), "utf-8");
9460
+ const content = readFileSync14(join18(dir, match), "utf-8");
8941
9461
  const titleMatch = content.match(/^#\s+(.+)/m);
8942
9462
  const discussionTitle = titleMatch ? titleMatch[1].trim() : id;
8943
9463
  await planCommand(projectRoot, [
@@ -9049,7 +9569,7 @@ ${turn.content}`;
9049
9569
  ...conversation.length > 1 ? [`---`, ``, `## Discussion Transcript`, ``, transcript, ``] : []
9050
9570
  ].join(`
9051
9571
  `);
9052
- writeFileSync9(join16(dir, `${id}.md`), markdown, "utf-8");
9572
+ writeFileSync10(join18(dir, `${id}.md`), markdown, "utf-8");
9053
9573
  process.stderr.write(`
9054
9574
  ${green("✓")} Discussion saved: ${cyan(id)} ${dim(`(${timer.formatted()})`)}
9055
9575
  `);
@@ -9063,16 +9583,16 @@ function buildDiscussionPrompt(projectRoot, config, topic, conversation, forceFi
9063
9583
  const parts = [];
9064
9584
  parts.push(`You are a senior software architect and consultant for the ${config.github.owner}/${config.github.repo} project.`);
9065
9585
  parts.push("");
9066
- const locusPath = join16(projectRoot, "LOCUS.md");
9067
- if (existsSync15(locusPath)) {
9068
- const content = readFileSync12(locusPath, "utf-8");
9586
+ const locusPath = join18(projectRoot, "LOCUS.md");
9587
+ if (existsSync18(locusPath)) {
9588
+ const content = readFileSync14(locusPath, "utf-8");
9069
9589
  parts.push("PROJECT CONTEXT:");
9070
9590
  parts.push(content.slice(0, 3000));
9071
9591
  parts.push("");
9072
9592
  }
9073
- const learningsPath = join16(projectRoot, ".locus", "LEARNINGS.md");
9074
- if (existsSync15(learningsPath)) {
9075
- const content = readFileSync12(learningsPath, "utf-8");
9593
+ const learningsPath = join18(projectRoot, ".locus", "LEARNINGS.md");
9594
+ if (existsSync18(learningsPath)) {
9595
+ const content = readFileSync14(learningsPath, "utf-8");
9076
9596
  parts.push("PAST LEARNINGS:");
9077
9597
  parts.push(content.slice(0, 2000));
9078
9598
  parts.push("");
@@ -9131,8 +9651,8 @@ __export(exports_artifacts, {
9131
9651
  formatDate: () => formatDate2,
9132
9652
  artifactsCommand: () => artifactsCommand
9133
9653
  });
9134
- import { existsSync as existsSync16, readdirSync as readdirSync9, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
9135
- import { join as join17 } from "node:path";
9654
+ import { existsSync as existsSync19, readdirSync as readdirSync9, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
9655
+ import { join as join19 } from "node:path";
9136
9656
  function printHelp5() {
9137
9657
  process.stderr.write(`
9138
9658
  ${bold("locus artifacts")} — View and manage AI-generated artifacts
@@ -9152,14 +9672,14 @@ ${dim("Artifact names support partial matching.")}
9152
9672
  `);
9153
9673
  }
9154
9674
  function getArtifactsDir(projectRoot) {
9155
- return join17(projectRoot, ".locus", "artifacts");
9675
+ return join19(projectRoot, ".locus", "artifacts");
9156
9676
  }
9157
9677
  function listArtifacts(projectRoot) {
9158
9678
  const dir = getArtifactsDir(projectRoot);
9159
- if (!existsSync16(dir))
9679
+ if (!existsSync19(dir))
9160
9680
  return [];
9161
9681
  return readdirSync9(dir).filter((f) => f.endsWith(".md")).map((fileName) => {
9162
- const filePath = join17(dir, fileName);
9682
+ const filePath = join19(dir, fileName);
9163
9683
  const stat = statSync4(filePath);
9164
9684
  return {
9165
9685
  name: fileName.replace(/\.md$/, ""),
@@ -9172,12 +9692,12 @@ function listArtifacts(projectRoot) {
9172
9692
  function readArtifact(projectRoot, name) {
9173
9693
  const dir = getArtifactsDir(projectRoot);
9174
9694
  const fileName = name.endsWith(".md") ? name : `${name}.md`;
9175
- const filePath = join17(dir, fileName);
9176
- if (!existsSync16(filePath))
9695
+ const filePath = join19(dir, fileName);
9696
+ if (!existsSync19(filePath))
9177
9697
  return null;
9178
9698
  const stat = statSync4(filePath);
9179
9699
  return {
9180
- content: readFileSync13(filePath, "utf-8"),
9700
+ content: readFileSync15(filePath, "utf-8"),
9181
9701
  info: {
9182
9702
  name: fileName.replace(/\.md$/, ""),
9183
9703
  fileName,
@@ -9341,17 +9861,17 @@ init_context();
9341
9861
  init_logger();
9342
9862
  init_rate_limiter();
9343
9863
  init_terminal();
9344
- import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
9345
- import { join as join18 } from "node:path";
9864
+ import { existsSync as existsSync20, readFileSync as readFileSync16 } from "node:fs";
9865
+ import { join as join20 } from "node:path";
9346
9866
  import { fileURLToPath } from "node:url";
9347
9867
  function getCliVersion() {
9348
9868
  const fallbackVersion = "0.0.0";
9349
- const packageJsonPath = join18(fileURLToPath(new URL(".", import.meta.url)), "..", "package.json");
9350
- if (!existsSync17(packageJsonPath)) {
9869
+ const packageJsonPath = join20(fileURLToPath(new URL(".", import.meta.url)), "..", "package.json");
9870
+ if (!existsSync20(packageJsonPath)) {
9351
9871
  return fallbackVersion;
9352
9872
  }
9353
9873
  try {
9354
- const parsed = JSON.parse(readFileSync14(packageJsonPath, "utf-8"));
9874
+ const parsed = JSON.parse(readFileSync16(packageJsonPath, "utf-8"));
9355
9875
  return parsed.version ?? fallbackVersion;
9356
9876
  } catch {
9357
9877
  return fallbackVersion;
@@ -9369,7 +9889,9 @@ function parseArgs(argv) {
9369
9889
  clean: false,
9370
9890
  resume: false,
9371
9891
  dryRun: false,
9372
- check: false
9892
+ check: false,
9893
+ upgrade: false,
9894
+ list: false
9373
9895
  };
9374
9896
  const positional = [];
9375
9897
  let i = 0;
@@ -9389,8 +9911,25 @@ function parseArgs(argv) {
9389
9911
  flags.help = true;
9390
9912
  break;
9391
9913
  case "--version":
9392
- case "-V":
9393
- flags.version = true;
9914
+ case "-V": {
9915
+ const nextToken = rawArgs[i + 1];
9916
+ if (nextToken !== undefined && /^\d/.test(nextToken)) {
9917
+ flags.installVersion = rawArgs[++i];
9918
+ } else {
9919
+ flags.version = true;
9920
+ }
9921
+ break;
9922
+ }
9923
+ case "-v":
9924
+ flags.installVersion = rawArgs[++i];
9925
+ break;
9926
+ case "--upgrade":
9927
+ case "-u":
9928
+ flags.upgrade = true;
9929
+ break;
9930
+ case "--list":
9931
+ case "-l":
9932
+ flags.list = true;
9394
9933
  break;
9395
9934
  case "--json-stream":
9396
9935
  flags.jsonStream = true;
@@ -9459,6 +9998,10 @@ ${bold("Commands:")}
9459
9998
  ${cyan("status")} Dashboard view of current state
9460
9999
  ${cyan("config")} View and manage settings
9461
10000
  ${cyan("logs")} View, tail, and manage execution logs
10001
+ ${cyan("install")} Install a community package
10002
+ ${cyan("uninstall")} Remove an installed package
10003
+ ${cyan("packages")} Manage installed packages (list, outdated)
10004
+ ${cyan("pkg")} ${dim("<name> [cmd]")} Run a command from an installed package
9462
10005
  ${cyan("upgrade")} Check for and install updates
9463
10006
 
9464
10007
  ${bold("Options:")}
@@ -9504,7 +10047,7 @@ async function main() {
9504
10047
  try {
9505
10048
  const root = getGitRoot(cwd);
9506
10049
  if (isInitialized(root)) {
9507
- logDir = join18(root, ".locus", "logs");
10050
+ logDir = join20(root, ".locus", "logs");
9508
10051
  getRateLimiter(root);
9509
10052
  }
9510
10053
  } catch {}
@@ -9534,6 +10077,43 @@ async function main() {
9534
10077
  logger.destroy();
9535
10078
  return;
9536
10079
  }
10080
+ if (command === "install") {
10081
+ if (parsed.flags.list) {
10082
+ const { packagesCommand: packagesCommand2 } = await Promise.resolve().then(() => (init_packages(), exports_packages));
10083
+ await packagesCommand2(["list"], {});
10084
+ logger.destroy();
10085
+ return;
10086
+ }
10087
+ const { installCommand: installCommand2 } = await Promise.resolve().then(() => (init_install(), exports_install));
10088
+ const installFlags = {};
10089
+ if (parsed.flags.installVersion) {
10090
+ installFlags.version = parsed.flags.installVersion;
10091
+ }
10092
+ if (parsed.flags.upgrade) {
10093
+ installFlags.upgrade = "true";
10094
+ }
10095
+ await installCommand2(parsed.args, installFlags);
10096
+ logger.destroy();
10097
+ return;
10098
+ }
10099
+ if (command === "uninstall") {
10100
+ const { uninstallCommand: uninstallCommand2 } = await Promise.resolve().then(() => (init_uninstall(), exports_uninstall));
10101
+ await uninstallCommand2(parsed.args, {});
10102
+ logger.destroy();
10103
+ return;
10104
+ }
10105
+ if (command === "packages") {
10106
+ const { packagesCommand: packagesCommand2 } = await Promise.resolve().then(() => (init_packages(), exports_packages));
10107
+ await packagesCommand2(parsed.args, {});
10108
+ logger.destroy();
10109
+ return;
10110
+ }
10111
+ if (command === "pkg") {
10112
+ const { pkgCommand: pkgCommand2 } = await Promise.resolve().then(() => (init_pkg(), exports_pkg));
10113
+ await pkgCommand2(parsed.args, {});
10114
+ logger.destroy();
10115
+ return;
10116
+ }
9537
10117
  let projectRoot;
9538
10118
  try {
9539
10119
  projectRoot = getGitRoot(cwd);