@stitchdb/cli 0.2.0 → 0.3.1
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/cli.js +111 -2
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -36,10 +36,29 @@
|
|
|
36
36
|
import * as fs from 'node:fs';
|
|
37
37
|
import * as path from 'node:path';
|
|
38
38
|
import * as os from 'node:os';
|
|
39
|
-
import { spawn } from 'node:child_process';
|
|
39
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
40
|
+
import { fileURLToPath } from 'node:url';
|
|
40
41
|
import { Stitch } from '@stitchdb/agent';
|
|
41
42
|
const CONFIG_DIR = path.join(os.homedir(), '.stitch');
|
|
42
43
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
44
|
+
const UPDATE_CACHE = path.join(CONFIG_DIR, 'update-check.json');
|
|
45
|
+
// Embedded at build time from package.json, but keeps a fallback if the file's
|
|
46
|
+
// not next to the bundle (e.g. published tarball layout).
|
|
47
|
+
const CLI_VERSION = readSelfVersion() ?? '0.0.0';
|
|
48
|
+
function readSelfVersion() {
|
|
49
|
+
try {
|
|
50
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
51
|
+
for (const candidate of [path.join(here, '..', 'package.json'), path.join(here, 'package.json')]) {
|
|
52
|
+
if (fs.existsSync(candidate)) {
|
|
53
|
+
const pkg = JSON.parse(fs.readFileSync(candidate, 'utf8'));
|
|
54
|
+
if (pkg.name === '@stitchdb/cli' && typeof pkg.version === 'string')
|
|
55
|
+
return pkg.version;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch { /* ignore */ }
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
43
62
|
function loadConfig() {
|
|
44
63
|
try {
|
|
45
64
|
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
@@ -421,6 +440,85 @@ function readStdinAll() {
|
|
|
421
440
|
process.stdin.on('end', () => resolve(data.trim()));
|
|
422
441
|
});
|
|
423
442
|
}
|
|
443
|
+
function loadUpdateCache() {
|
|
444
|
+
try {
|
|
445
|
+
return JSON.parse(fs.readFileSync(UPDATE_CACHE, 'utf8'));
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function saveUpdateCache(c) {
|
|
452
|
+
try {
|
|
453
|
+
if (!fs.existsSync(CONFIG_DIR))
|
|
454
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
455
|
+
fs.writeFileSync(UPDATE_CACHE, JSON.stringify(c));
|
|
456
|
+
}
|
|
457
|
+
catch { /* ignore */ }
|
|
458
|
+
}
|
|
459
|
+
function semverGt(a, b) {
|
|
460
|
+
const pa = a.split('.').map((x) => parseInt(x, 10) || 0);
|
|
461
|
+
const pb = b.split('.').map((x) => parseInt(x, 10) || 0);
|
|
462
|
+
for (let i = 0; i < 3; i++) {
|
|
463
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0))
|
|
464
|
+
return true;
|
|
465
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0))
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Best-effort fetch of the latest published version. Cached for 6h so we don't
|
|
472
|
+
* hammer npm. Runs after the user's command is done and prints a one-line
|
|
473
|
+
* notice if an update is available. Never blocks, never errors loudly.
|
|
474
|
+
*/
|
|
475
|
+
async function maybeNotifyUpdate() {
|
|
476
|
+
if (process.env.STITCH_NO_UPDATE_CHECK === '1')
|
|
477
|
+
return;
|
|
478
|
+
try {
|
|
479
|
+
const cache = loadUpdateCache();
|
|
480
|
+
const fresh = cache && Date.now() - cache.checkedAt < 6 * 3600_000;
|
|
481
|
+
let latest = fresh ? cache.latest : '';
|
|
482
|
+
if (!fresh) {
|
|
483
|
+
const ctrl = new AbortController();
|
|
484
|
+
const t = setTimeout(() => ctrl.abort(), 1500);
|
|
485
|
+
try {
|
|
486
|
+
const res = await fetch('https://registry.npmjs.org/@stitchdb/cli/latest', { signal: ctrl.signal });
|
|
487
|
+
if (res.ok) {
|
|
488
|
+
const j = await res.json();
|
|
489
|
+
if (j.version)
|
|
490
|
+
latest = j.version;
|
|
491
|
+
saveUpdateCache({ checkedAt: Date.now(), latest });
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
finally {
|
|
495
|
+
clearTimeout(t);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (latest && semverGt(latest, CLI_VERSION)) {
|
|
499
|
+
const msg = `\n Stitch CLI ${latest} is available (you have ${CLI_VERSION}). Run: stitch update\n`;
|
|
500
|
+
process.stderr.write(msg);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch { /* never let the check break a real command */ }
|
|
504
|
+
}
|
|
505
|
+
async function cmdUpdate(args) {
|
|
506
|
+
const channel = parseFlag(args, ['--tag']) || 'latest';
|
|
507
|
+
console.log(`Updating @stitchdb/cli (current: ${CLI_VERSION}) → ${channel}…`);
|
|
508
|
+
// Use the user's npm. Try `npm i -g @stitchdb/cli@latest` first; if that
|
|
509
|
+
// fails with EACCES, retry under sudo.
|
|
510
|
+
const cmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
511
|
+
const result = spawnSync(cmd, ['install', '-g', `@stitchdb/cli@${channel}`], { stdio: 'inherit' });
|
|
512
|
+
if (result.status !== 0) {
|
|
513
|
+
console.error('\nUpdate failed. Try one of:');
|
|
514
|
+
console.error(` npm install -g @stitchdb/cli@${channel}`);
|
|
515
|
+
console.error(` sudo npm install -g @stitchdb/cli@${channel}`);
|
|
516
|
+
process.exit(result.status ?? 1);
|
|
517
|
+
}
|
|
518
|
+
// Reset the cache so we don't nag with the version we just installed.
|
|
519
|
+
saveUpdateCache({ checkedAt: Date.now(), latest: '0.0.0' });
|
|
520
|
+
console.log('\nUpdated. Open a fresh shell or run `which stitch` to verify.');
|
|
521
|
+
}
|
|
424
522
|
// ── One-shot install: MCP server + auto-log hooks + project CLAUDE.md ─────
|
|
425
523
|
async function cmdInstall(args) {
|
|
426
524
|
const cfg = loadConfig();
|
|
@@ -683,6 +781,9 @@ function help() {
|
|
|
683
781
|
stitch whoami Show the configured key.
|
|
684
782
|
stitch logout
|
|
685
783
|
|
|
784
|
+
stitch update Update to the latest @stitchdb/cli.
|
|
785
|
+
stitch version Print the installed version.
|
|
786
|
+
|
|
686
787
|
stitch install [--no-mcp] [--no-hooks] [--no-claude-md]
|
|
687
788
|
One-shot: register Stitch as an MCP
|
|
688
789
|
server in Claude Code, wire up auto-log
|
|
@@ -721,6 +822,14 @@ async function main(argv) {
|
|
|
721
822
|
case 'recall': return cmdRecall(rest);
|
|
722
823
|
case 'thread': return cmdThread(rest);
|
|
723
824
|
case 'install': return cmdInstall(rest);
|
|
825
|
+
case 'update':
|
|
826
|
+
case 'upgrade': return cmdUpdate(rest);
|
|
827
|
+
case 'version':
|
|
828
|
+
case '--version':
|
|
829
|
+
case '-v': {
|
|
830
|
+
console.log(CLI_VERSION);
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
724
833
|
case 'workspace':
|
|
725
834
|
case 'ws': return cmdWorkspace(rest);
|
|
726
835
|
case 'agent': return cmdAgent(rest);
|
|
@@ -741,4 +850,4 @@ async function main(argv) {
|
|
|
741
850
|
process.exit(1);
|
|
742
851
|
}
|
|
743
852
|
}
|
|
744
|
-
main(process.argv.slice(2));
|
|
853
|
+
main(process.argv.slice(2)).then(() => maybeNotifyUpdate());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stitchdb/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Stitch CLI — manage memory + run agents from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"license": "MIT",
|
|
17
17
|
"engines": { "node": ">=20" },
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@stitchdb/agent": "^0.1.
|
|
19
|
+
"@stitchdb/agent": "^0.1.1"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"typescript": "^5.4.0",
|