@captainsafia/stitch 1.0.0-preview.c77f34b → 1.0.0-preview.f2a6c41-preview.e348edd

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 (3) hide show
  1. package/dist/cli.js +206 -159
  2. package/dist/mcp.js +2 -1
  3. package/package.json +2 -1
package/dist/cli.js CHANGED
@@ -1895,7 +1895,7 @@ var require_commander = __commonJS((exports) => {
1895
1895
  var require_package = __commonJS((exports, module) => {
1896
1896
  module.exports = {
1897
1897
  name: "@captainsafia/stitch",
1898
- version: "1.0.0-preview.c77f34b",
1898
+ version: "1.0.0-preview.f2a6c41-preview.e348edd",
1899
1899
  type: "module",
1900
1900
  description: "A local-first CLI for recording intent and binding it to git commits/diffs",
1901
1901
  main: "dist/api.js",
@@ -1943,6 +1943,7 @@ var require_package = __commonJS((exports, module) => {
1943
1943
  "@modelcontextprotocol/sdk": "^1.25.1",
1944
1944
  "async-mutex": "^0.5.0",
1945
1945
  commander: "^12.0.0",
1946
+ "gh-release-update-notifier": "^1.0.0",
1946
1947
  "smol-toml": "^1.3.0",
1947
1948
  zod: "^4.2.1"
1948
1949
  },
@@ -1964,6 +1965,9 @@ var require_package = __commonJS((exports, module) => {
1964
1965
  };
1965
1966
  });
1966
1967
 
1968
+ // src/cli.ts
1969
+ import { join as join4 } from "path";
1970
+
1967
1971
  // node_modules/commander/esm.mjs
1968
1972
  var import__ = __toESM(require_commander(), 1);
1969
1973
  var {
@@ -1980,13 +1984,181 @@ var {
1980
1984
  Help
1981
1985
  } = import__.default;
1982
1986
 
1987
+ // node_modules/gh-release-update-notifier/dist/index.mjs
1988
+ import { existsSync, readFileSync, writeFileSync } from "fs";
1989
+ var ReleaseNotifier = class {
1990
+ repo;
1991
+ checkInterval;
1992
+ cacheFilePath;
1993
+ token;
1994
+ cachedReleases = null;
1995
+ lastFetchTime = 0;
1996
+ constructor(config) {
1997
+ this.repo = config.repo;
1998
+ this.checkInterval = config.checkInterval ?? 3600000;
1999
+ this.cacheFilePath = config.cacheFilePath ?? null;
2000
+ this.token = config.token ?? null;
2001
+ this.loadCacheFromDisk();
2002
+ }
2003
+ async getLatestRelease(includePrerelease = false) {
2004
+ try {
2005
+ const validReleases = (await this.fetchAllReleases()).filter((release) => {
2006
+ if (release.draft)
2007
+ return false;
2008
+ if (!includePrerelease && release.prerelease)
2009
+ return false;
2010
+ return true;
2011
+ });
2012
+ if (validReleases.length === 0)
2013
+ return null;
2014
+ const latest = validReleases[0];
2015
+ return {
2016
+ tagName: latest.tag_name,
2017
+ name: latest.name,
2018
+ prerelease: latest.prerelease,
2019
+ draft: latest.draft,
2020
+ htmlUrl: latest.html_url,
2021
+ publishedAt: latest.published_at
2022
+ };
2023
+ } catch (error) {
2024
+ throw new Error(`Failed to fetch releases: ${error instanceof Error ? error.message : String(error)}`);
2025
+ }
2026
+ }
2027
+ async getLatestPrerelease() {
2028
+ try {
2029
+ const prerelease = (await this.fetchAllReleases()).find((release) => release.prerelease && !release.draft);
2030
+ if (!prerelease)
2031
+ return null;
2032
+ return {
2033
+ tagName: prerelease.tag_name,
2034
+ name: prerelease.name,
2035
+ prerelease: prerelease.prerelease,
2036
+ draft: prerelease.draft,
2037
+ htmlUrl: prerelease.html_url,
2038
+ publishedAt: prerelease.published_at
2039
+ };
2040
+ } catch (error) {
2041
+ throw new Error(`Failed to fetch prereleases: ${error instanceof Error ? error.message : String(error)}`);
2042
+ }
2043
+ }
2044
+ async checkVersion(currentVersion, isPrerelease = false) {
2045
+ try {
2046
+ const releases = await this.fetchAllReleases();
2047
+ if (releases.length === 0)
2048
+ return {
2049
+ updateAvailable: false,
2050
+ currentVersion,
2051
+ latestVersion: null,
2052
+ latestRelease: null
2053
+ };
2054
+ const normalizedCurrent = this.normalizeVersion(currentVersion);
2055
+ const currentRelease = releases.find((r) => this.normalizeVersion(r.tag_name) === normalizedCurrent || r.tag_name === currentVersion);
2056
+ const latestRelease = isPrerelease ? releases.find((r) => r.prerelease && !r.draft) : releases.find((r) => !r.prerelease && !r.draft);
2057
+ if (!latestRelease)
2058
+ return {
2059
+ updateAvailable: false,
2060
+ currentVersion,
2061
+ latestVersion: null,
2062
+ latestRelease: null
2063
+ };
2064
+ const latest = {
2065
+ tagName: latestRelease.tag_name,
2066
+ name: latestRelease.name,
2067
+ prerelease: latestRelease.prerelease,
2068
+ draft: latestRelease.draft,
2069
+ htmlUrl: latestRelease.html_url,
2070
+ publishedAt: latestRelease.published_at
2071
+ };
2072
+ if (!currentRelease)
2073
+ return {
2074
+ updateAvailable: true,
2075
+ currentVersion,
2076
+ latestVersion: latestRelease.tag_name,
2077
+ latestRelease: latest
2078
+ };
2079
+ return {
2080
+ updateAvailable: this.isVersionOlder(currentRelease.published_at, latestRelease.published_at),
2081
+ currentVersion,
2082
+ latestVersion: latestRelease.tag_name,
2083
+ latestRelease: latest
2084
+ };
2085
+ } catch (error) {
2086
+ throw new Error(`Failed to check version: ${error instanceof Error ? error.message : String(error)}`);
2087
+ }
2088
+ }
2089
+ async fetchAllReleases() {
2090
+ const now = Date.now();
2091
+ if (this.cachedReleases !== null && this.checkInterval > 0 && now - this.lastFetchTime < this.checkInterval)
2092
+ return this.cachedReleases;
2093
+ const headers = {
2094
+ Accept: "application/vnd.github+json",
2095
+ "X-GitHub-Api-Version": "2022-11-28"
2096
+ };
2097
+ if (this.token)
2098
+ headers["Authorization"] = `Bearer ${this.token}`;
2099
+ const response = await fetch(`https://api.github.com/repos/${this.repo}/releases`, { headers });
2100
+ if (!response.ok)
2101
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
2102
+ const releases = await response.json();
2103
+ releases.sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
2104
+ this.cachedReleases = releases;
2105
+ this.lastFetchTime = now;
2106
+ this.saveCacheToDisk();
2107
+ return releases;
2108
+ }
2109
+ clearCache() {
2110
+ this.cachedReleases = null;
2111
+ this.lastFetchTime = 0;
2112
+ this.saveCacheToDisk();
2113
+ }
2114
+ loadCacheFromDisk() {
2115
+ if (!this.cacheFilePath)
2116
+ return;
2117
+ try {
2118
+ if (existsSync(this.cacheFilePath)) {
2119
+ const data = readFileSync(this.cacheFilePath, "utf-8");
2120
+ const cache = JSON.parse(data);
2121
+ this.cachedReleases = cache.releases;
2122
+ this.lastFetchTime = cache.lastFetchTime;
2123
+ }
2124
+ } catch {
2125
+ this.cachedReleases = null;
2126
+ this.lastFetchTime = 0;
2127
+ }
2128
+ }
2129
+ saveCacheToDisk() {
2130
+ if (!this.cacheFilePath)
2131
+ return;
2132
+ try {
2133
+ if (this.cachedReleases === null)
2134
+ writeFileSync(this.cacheFilePath, JSON.stringify({
2135
+ lastFetchTime: 0,
2136
+ releases: []
2137
+ }), "utf-8");
2138
+ else {
2139
+ const cache = {
2140
+ lastFetchTime: this.lastFetchTime,
2141
+ releases: this.cachedReleases
2142
+ };
2143
+ writeFileSync(this.cacheFilePath, JSON.stringify(cache), "utf-8");
2144
+ }
2145
+ } catch {}
2146
+ }
2147
+ normalizeVersion(version) {
2148
+ return version.trim().replace(/^v/i, "");
2149
+ }
2150
+ isVersionOlder(date1, date2) {
2151
+ return new Date(date1).getTime() < new Date(date2).getTime();
2152
+ }
2153
+ };
2154
+
1983
2155
  // src/api.ts
1984
2156
  import { spawn } from "child_process";
1985
2157
 
1986
2158
  // src/core/store.ts
1987
2159
  import { readdir, readFile, writeFile, mkdir } from "fs/promises";
1988
2160
  import { join } from "path";
1989
- import { existsSync } from "fs";
2161
+ import { existsSync as existsSync2 } from "fs";
1990
2162
 
1991
2163
  // src/core/errors.ts
1992
2164
  class StitchError extends Error {
@@ -3346,13 +3518,13 @@ function getCurrentFilePath(repoRoot) {
3346
3518
  return join(repoRoot, STITCH_DIR, CURRENT_FILE);
3347
3519
  }
3348
3520
  function isInitialized(repoRoot) {
3349
- return existsSync(getStitchDir(repoRoot));
3521
+ return existsSync2(getStitchDir(repoRoot));
3350
3522
  }
3351
3523
  async function initializeStitch(repoRoot) {
3352
3524
  const stitchesDir = getStitchesDir(repoRoot);
3353
3525
  const currentPath = getCurrentFilePath(repoRoot);
3354
3526
  await mkdir(stitchesDir, { recursive: true });
3355
- if (!existsSync(currentPath)) {
3527
+ if (!existsSync2(currentPath)) {
3356
3528
  await writeFile(currentPath, "", "utf-8");
3357
3529
  }
3358
3530
  }
@@ -3407,7 +3579,7 @@ async function loadStitch(repoRoot, id) {
3407
3579
  throw new NotInitializedError;
3408
3580
  }
3409
3581
  const filePath = getStitchFilePath(repoRoot, id);
3410
- if (!existsSync(filePath)) {
3582
+ if (!existsSync2(filePath)) {
3411
3583
  throw new StitchNotFoundError(id);
3412
3584
  }
3413
3585
  const content = await readFile(filePath, "utf-8");
@@ -3431,7 +3603,7 @@ async function listStitches(repoRoot, filter) {
3431
3603
  throw new NotInitializedError;
3432
3604
  }
3433
3605
  const stitchesDir = getStitchesDir(repoRoot);
3434
- if (!existsSync(stitchesDir)) {
3606
+ if (!existsSync2(stitchesDir)) {
3435
3607
  return [];
3436
3608
  }
3437
3609
  const files = await readdir(stitchesDir);
@@ -3966,145 +4138,6 @@ function truncate(str, maxLength) {
3966
4138
  function renderSuccess(message) {
3967
4139
  return `\u2713 ${message}`;
3968
4140
  }
3969
- // src/platform/update/version.ts
3970
- function parseVersion(version) {
3971
- const previewMatch = version.match(/^(\d+)\.(\d+)\.(\d+)(-preview\.([a-f0-9]+))?$/);
3972
- if (!previewMatch) {
3973
- throw new Error(`Invalid version format: ${version}`);
3974
- }
3975
- return {
3976
- major: parseInt(previewMatch[1], 10),
3977
- minor: parseInt(previewMatch[2], 10),
3978
- patch: parseInt(previewMatch[3], 10),
3979
- prerelease: previewMatch[5],
3980
- isPreview: !!previewMatch[4]
3981
- };
3982
- }
3983
- function compareVersions(a, b) {
3984
- const parsedA = parseVersion(a);
3985
- const parsedB = parseVersion(b);
3986
- if (parsedA.major !== parsedB.major) {
3987
- return parsedA.major < parsedB.major ? -1 : 1;
3988
- }
3989
- if (parsedA.minor !== parsedB.minor) {
3990
- return parsedA.minor < parsedB.minor ? -1 : 1;
3991
- }
3992
- if (parsedA.patch !== parsedB.patch) {
3993
- return parsedA.patch < parsedB.patch ? -1 : 1;
3994
- }
3995
- if (!parsedA.isPreview && parsedB.isPreview)
3996
- return 1;
3997
- if (parsedA.isPreview && !parsedB.isPreview)
3998
- return -1;
3999
- if (parsedA.prerelease && parsedB.prerelease) {
4000
- return parsedA.prerelease.localeCompare(parsedB.prerelease);
4001
- }
4002
- return 0;
4003
- }
4004
- function isUpdateAvailable(current, latest) {
4005
- return compareVersions(current, latest) < 0;
4006
- }
4007
- // src/platform/update/check.ts
4008
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
4009
- import { existsSync as existsSync2 } from "fs";
4010
- import { join as join3 } from "path";
4011
- var CACHE_FILE = "update-check.json";
4012
- var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
4013
- var GITHUB_API_URL = "https://api.github.com/repos/captainsafia/stitch/releases";
4014
- function getCacheFilePath() {
4015
- return join3(getConfigDir(), CACHE_FILE);
4016
- }
4017
- async function readCache() {
4018
- const cachePath = getCacheFilePath();
4019
- if (!existsSync2(cachePath)) {
4020
- return null;
4021
- }
4022
- try {
4023
- const content = await readFile2(cachePath, "utf-8");
4024
- return JSON.parse(content);
4025
- } catch {
4026
- return null;
4027
- }
4028
- }
4029
- async function writeCache(cache) {
4030
- const configDir = getConfigDir();
4031
- if (!existsSync2(configDir)) {
4032
- await mkdir2(configDir, { recursive: true });
4033
- }
4034
- const cachePath = getCacheFilePath();
4035
- await writeFile2(cachePath, JSON.stringify(cache, null, 2), "utf-8");
4036
- }
4037
- function isCacheValid(cache) {
4038
- const now = Date.now();
4039
- return now - cache.lastChecked < CHECK_INTERVAL_MS;
4040
- }
4041
- async function fetchLatestRelease() {
4042
- try {
4043
- const response = await fetch(`${GITHUB_API_URL}/latest`, {
4044
- headers: {
4045
- Accept: "application/vnd.github.v3+json",
4046
- "User-Agent": "stitch-cli"
4047
- }
4048
- });
4049
- if (!response.ok) {
4050
- return null;
4051
- }
4052
- const data = await response.json();
4053
- return data.tag_name.replace(/^v/, "");
4054
- } catch {
4055
- return null;
4056
- }
4057
- }
4058
- async function fetchLatestPreviewRelease() {
4059
- try {
4060
- const response = await fetch(`${GITHUB_API_URL}?per_page=20`, {
4061
- headers: {
4062
- Accept: "application/vnd.github.v3+json",
4063
- "User-Agent": "stitch-cli"
4064
- }
4065
- });
4066
- if (!response.ok) {
4067
- return null;
4068
- }
4069
- const releases = await response.json();
4070
- for (const release of releases) {
4071
- const version = release.tag_name.replace(/^v/, "");
4072
- if (version.includes("-preview.")) {
4073
- return version;
4074
- }
4075
- }
4076
- return null;
4077
- } catch {
4078
- return null;
4079
- }
4080
- }
4081
- async function getLatestVersion(forceRefresh = false) {
4082
- if (!forceRefresh) {
4083
- const cache = await readCache();
4084
- if (cache && isCacheValid(cache)) {
4085
- return {
4086
- stable: cache.latestVersion,
4087
- preview: cache.latestPreviewVersion ?? null
4088
- };
4089
- }
4090
- }
4091
- const [stableVersion, previewVersion] = await Promise.all([
4092
- fetchLatestRelease(),
4093
- fetchLatestPreviewRelease()
4094
- ]);
4095
- if (stableVersion) {
4096
- const cache = {
4097
- lastChecked: Date.now(),
4098
- latestVersion: stableVersion,
4099
- latestPreviewVersion: previewVersion ?? undefined
4100
- };
4101
- writeCache(cache).catch(() => {});
4102
- }
4103
- return {
4104
- stable: stableVersion,
4105
- preview: previewVersion
4106
- };
4107
- }
4108
4141
  // src/platform/update/download.ts
4109
4142
  var DOWNLOAD_BASE_URL = "https://github.com/captainsafia/stitch/releases/download";
4110
4143
  function detectPlatform() {
@@ -4188,7 +4221,7 @@ async function downloadBinary(url, destPath, onProgress) {
4188
4221
  // src/platform/update/install.ts
4189
4222
  import { rename, unlink, chmod } from "fs/promises";
4190
4223
  import { existsSync as existsSync3 } from "fs";
4191
- import { dirname, join as join4 } from "path";
4224
+ import { dirname, join as join3 } from "path";
4192
4225
  var {$: $2 } = globalThis.Bun;
4193
4226
  function getCurrentExecutablePath() {
4194
4227
  return process.execPath;
@@ -4196,8 +4229,8 @@ function getCurrentExecutablePath() {
4196
4229
  async function cleanupOldBinaries(execPath) {
4197
4230
  const dir = dirname(execPath);
4198
4231
  const baseName = execPath.endsWith(".exe") ? "stitch" : "stitch";
4199
- const oldPath = join4(dir, `${baseName}.old`);
4200
- const oldPathExe = join4(dir, `${baseName}.old.exe`);
4232
+ const oldPath = join3(dir, `${baseName}.old`);
4233
+ const oldPathExe = join3(dir, `${baseName}.old.exe`);
4201
4234
  for (const path of [oldPath, oldPathExe]) {
4202
4235
  if (existsSync3(path)) {
4203
4236
  try {
@@ -4211,8 +4244,8 @@ async function installBinaryUpdate(targetVersion, currentVersion, onProgress) {
4211
4244
  const execPath = getCurrentExecutablePath();
4212
4245
  const dir = dirname(execPath);
4213
4246
  const isWindows = platform === "windows-x64";
4214
- const newBinaryPath = join4(dir, isWindows ? "stitch.new.exe" : "stitch.new");
4215
- const oldBinaryPath = join4(dir, isWindows ? "stitch.old.exe" : "stitch.old");
4247
+ const newBinaryPath = join3(dir, isWindows ? "stitch.new.exe" : "stitch.new");
4248
+ const oldBinaryPath = join3(dir, isWindows ? "stitch.old.exe" : "stitch.old");
4216
4249
  const downloadUrl = getDownloadUrl(targetVersion, platform);
4217
4250
  try {
4218
4251
  await cleanupOldBinaries(execPath);
@@ -4348,13 +4381,23 @@ function getInstallMethodDescription(method) {
4348
4381
  var packageJson = await Promise.resolve().then(() => __toESM(require_package(), 1));
4349
4382
  var program2 = new Command;
4350
4383
  program2.name("stitch").description(packageJson.description).version(packageJson.version);
4384
+ function createNotifier() {
4385
+ const cacheFilePath = join4(getConfigDir(), "update-check.json");
4386
+ return new ReleaseNotifier({
4387
+ repo: "captainsafia/stitch",
4388
+ checkInterval: 86400000,
4389
+ cacheFilePath,
4390
+ token: process.env["GITHUB_TOKEN"]
4391
+ });
4392
+ }
4351
4393
  async function checkForUpdates() {
4352
4394
  try {
4353
- const versions = await getLatestVersion();
4354
4395
  const current = packageJson.version;
4355
- if (versions.stable && isUpdateAvailable(current, versions.stable)) {
4396
+ const notifier = createNotifier();
4397
+ const result = await notifier.checkVersion(current, true);
4398
+ if (result.updateAvailable && result.latestVersion) {
4356
4399
  console.error("");
4357
- console.error(`A new version of stitch is available: ${versions.stable} (current: ${current})`);
4400
+ console.error(`A new version of stitch is available: ${result.latestVersion} (current: ${current})`);
4358
4401
  console.error("Run 'stitch update' to update.");
4359
4402
  console.error("");
4360
4403
  }
@@ -4585,20 +4628,24 @@ program2.command("update").description("Update stitch to the latest version").op
4585
4628
  targetVersion = options.target;
4586
4629
  } else if (options.preview) {
4587
4630
  console.log("Checking for latest preview version...");
4588
- const versions = await getLatestVersion(true);
4589
- if (!versions.preview) {
4631
+ const notifier = createNotifier();
4632
+ notifier.clearCache();
4633
+ const previewRelease = await notifier.getLatestPrerelease();
4634
+ if (!previewRelease) {
4590
4635
  console.error("Error: No preview version available.");
4591
4636
  process.exit(1);
4592
4637
  }
4593
- targetVersion = versions.preview;
4638
+ targetVersion = previewRelease.tagName.replace(/^v/, "");
4594
4639
  } else {
4595
4640
  console.log("Checking for latest version...");
4596
- const versions = await getLatestVersion(true);
4597
- if (!versions.stable) {
4641
+ const notifier = createNotifier();
4642
+ notifier.clearCache();
4643
+ const stableRelease = await notifier.getLatestRelease();
4644
+ if (!stableRelease) {
4598
4645
  console.error("Error: Could not fetch latest version. Check your network connection.");
4599
4646
  process.exit(1);
4600
4647
  }
4601
- targetVersion = versions.stable;
4648
+ targetVersion = stableRelease.tagName.replace(/^v/, "");
4602
4649
  }
4603
4650
  if (current === targetVersion) {
4604
4651
  console.log(`Already running stitch v${current}`);
package/dist/mcp.js CHANGED
@@ -6494,7 +6494,7 @@ var require_dist = __commonJS((exports, module) => {
6494
6494
  var require_package = __commonJS((exports, module) => {
6495
6495
  module.exports = {
6496
6496
  name: "@captainsafia/stitch",
6497
- version: "1.0.0-preview.c77f34b",
6497
+ version: "1.0.0-preview.f2a6c41-preview.e348edd",
6498
6498
  type: "module",
6499
6499
  description: "A local-first CLI for recording intent and binding it to git commits/diffs",
6500
6500
  main: "dist/api.js",
@@ -6542,6 +6542,7 @@ var require_package = __commonJS((exports, module) => {
6542
6542
  "@modelcontextprotocol/sdk": "^1.25.1",
6543
6543
  "async-mutex": "^0.5.0",
6544
6544
  commander: "^12.0.0",
6545
+ "gh-release-update-notifier": "^1.0.0",
6545
6546
  "smol-toml": "^1.3.0",
6546
6547
  zod: "^4.2.1"
6547
6548
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@captainsafia/stitch",
3
- "version": "1.0.0-preview.c77f34b",
3
+ "version": "1.0.0-preview.f2a6c41-preview.e348edd",
4
4
  "type": "module",
5
5
  "description": "A local-first CLI for recording intent and binding it to git commits/diffs",
6
6
  "main": "dist/api.js",
@@ -48,6 +48,7 @@
48
48
  "@modelcontextprotocol/sdk": "^1.25.1",
49
49
  "async-mutex": "^0.5.0",
50
50
  "commander": "^12.0.0",
51
+ "gh-release-update-notifier": "^1.0.0",
51
52
  "smol-toml": "^1.3.0",
52
53
  "zod": "^4.2.1"
53
54
  },