@stitchdb/cli 0.1.0 → 0.3.0
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 +373 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,32 +2,63 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* `stitch` — CLI for the Stitch memory + agent control plane.
|
|
4
4
|
*
|
|
5
|
-
* stitch login
|
|
6
|
-
* stitch logout
|
|
7
|
-
* stitch whoami
|
|
5
|
+
* stitch login Save your API key locally.
|
|
6
|
+
* stitch logout Remove the saved key.
|
|
7
|
+
* stitch whoami Show the configured key (masked).
|
|
8
|
+
*
|
|
9
|
+
* stitch install One-shot: install Stitch as an MCP
|
|
10
|
+
* server in Claude Code, wire auto-log
|
|
11
|
+
* hooks, write a CLAUDE.md instruction
|
|
12
|
+
* in the current project so Claude
|
|
13
|
+
* auto-recalls past context at session start.
|
|
8
14
|
*
|
|
9
15
|
* stitch remember <text> [--kind X] [--tag a,b]
|
|
10
16
|
* stitch recall <query> [-k 5]
|
|
11
17
|
*
|
|
12
|
-
* stitch
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* stitch
|
|
16
|
-
* stitch
|
|
18
|
+
* stitch thread append --role <r> [--thread <t>] [--content <c>]
|
|
19
|
+
* Append a turn (or read content from stdin).
|
|
20
|
+
* Used by Claude Code hooks for auto-logging.
|
|
21
|
+
* stitch thread recall [--thread <t>] [--last 10] [--semantic <q>]
|
|
22
|
+
* stitch thread current Print the thread name for the current repo / cwd.
|
|
23
|
+
*
|
|
24
|
+
* stitch agent register <name> Create a new agent identity.
|
|
25
|
+
* stitch agent list List your agents (online status).
|
|
26
|
+
* stitch agent run --id <agent_id> Hold open a control channel.
|
|
27
|
+
* stitch agent dispatch <id> <prompt> Send a command to a connected agent.
|
|
28
|
+
* stitch agent revoke <id> Revoke an agent.
|
|
17
29
|
*
|
|
18
30
|
* stitch workspace create <name>
|
|
19
31
|
* stitch workspace list
|
|
20
|
-
* stitch workspace use <id>
|
|
32
|
+
* stitch workspace use <id>
|
|
21
33
|
*
|
|
22
|
-
* stitch mcp-install
|
|
34
|
+
* stitch mcp-install Print the `claude mcp add stitch …` command.
|
|
23
35
|
*/
|
|
24
36
|
import * as fs from 'node:fs';
|
|
25
37
|
import * as path from 'node:path';
|
|
26
38
|
import * as os from 'node:os';
|
|
27
|
-
import { spawn } from 'node:child_process';
|
|
39
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
40
|
+
import { fileURLToPath } from 'node:url';
|
|
28
41
|
import { Stitch } from '@stitchdb/agent';
|
|
29
42
|
const CONFIG_DIR = path.join(os.homedir(), '.stitch');
|
|
30
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
|
+
}
|
|
31
62
|
function loadConfig() {
|
|
32
63
|
try {
|
|
33
64
|
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
@@ -306,6 +337,311 @@ async function cmdAgent(args) {
|
|
|
306
337
|
console.error('Usage: stitch agent [register|list|run|dispatch|revoke] …');
|
|
307
338
|
process.exit(2);
|
|
308
339
|
}
|
|
340
|
+
// ── Threads — append / recall / current ───────────────────────────────────
|
|
341
|
+
function inferThread() {
|
|
342
|
+
// Reasonable default: <git-repo-name>/<branch> if we're in a git repo,
|
|
343
|
+
// else the cwd's basename. Hooks call this to get a stable thread per project.
|
|
344
|
+
try {
|
|
345
|
+
const dir = process.cwd();
|
|
346
|
+
let cur = dir;
|
|
347
|
+
for (let i = 0; i < 8; i++) {
|
|
348
|
+
if (fs.existsSync(path.join(cur, '.git'))) {
|
|
349
|
+
const parent = path.dirname(cur);
|
|
350
|
+
const repoName = path.basename(cur);
|
|
351
|
+
let branch = 'main';
|
|
352
|
+
try {
|
|
353
|
+
const head = fs.readFileSync(path.join(cur, '.git', 'HEAD'), 'utf8').trim();
|
|
354
|
+
const m = head.match(/^ref:\s+refs\/heads\/(.+)$/);
|
|
355
|
+
if (m)
|
|
356
|
+
branch = m[1];
|
|
357
|
+
}
|
|
358
|
+
catch { /* detached */ }
|
|
359
|
+
return `${repoName}/${branch}`;
|
|
360
|
+
void parent;
|
|
361
|
+
}
|
|
362
|
+
const next = path.dirname(cur);
|
|
363
|
+
if (next === cur)
|
|
364
|
+
break;
|
|
365
|
+
cur = next;
|
|
366
|
+
}
|
|
367
|
+
return path.basename(dir);
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
return 'default';
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
async function cmdThread(args) {
|
|
374
|
+
const cfg = loadConfig();
|
|
375
|
+
const stitch = client(cfg);
|
|
376
|
+
const sub = args[0];
|
|
377
|
+
const rest = args.slice(1);
|
|
378
|
+
if (sub === 'append') {
|
|
379
|
+
const role = parseFlag(rest, ['--role']);
|
|
380
|
+
let thread = parseFlag(rest, ['--thread', '-t']) || inferThread();
|
|
381
|
+
let content = parseFlag(rest, ['--content', '-c']);
|
|
382
|
+
if (!content) {
|
|
383
|
+
// If no --content given, read from stdin (so a hook can pipe in the message).
|
|
384
|
+
content = await readStdinAll();
|
|
385
|
+
}
|
|
386
|
+
if (!role) {
|
|
387
|
+
console.error('Usage: stitch thread append --role user|assistant --content <text> [--thread name]');
|
|
388
|
+
process.exit(2);
|
|
389
|
+
}
|
|
390
|
+
if (!content) {
|
|
391
|
+
console.error('No content provided (use --content or pipe stdin).');
|
|
392
|
+
process.exit(2);
|
|
393
|
+
}
|
|
394
|
+
const out = await stitch.thread(thread).append({ role, content });
|
|
395
|
+
console.log(`Appended ${out.turn_id} to ${thread} (${out.thread_id})`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (sub === 'recall') {
|
|
399
|
+
const positionals = positional(rest);
|
|
400
|
+
const thread = parseFlag(rest, ['--thread', '-t']) || positionals[0] || inferThread();
|
|
401
|
+
const last = Number(parseFlag(rest, ['--last']) || '10');
|
|
402
|
+
const semantic = parseFlag(rest, ['--semantic']);
|
|
403
|
+
const out = await stitch.thread(thread).recall({ last, semantic, semantic_k: 5 });
|
|
404
|
+
if (!out.thread_id) {
|
|
405
|
+
console.log('Thread not found.');
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (out.recent.length === 0 && out.semantic.length === 0) {
|
|
409
|
+
console.log('Empty thread.');
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (out.recent.length) {
|
|
413
|
+
console.log(`# Recent (${out.recent.length})`);
|
|
414
|
+
for (const t of out.recent)
|
|
415
|
+
console.log(`- ${t.role}: ${t.content}`);
|
|
416
|
+
}
|
|
417
|
+
if (out.semantic.length) {
|
|
418
|
+
console.log(`\n# Semantic matches`);
|
|
419
|
+
for (const t of out.semantic)
|
|
420
|
+
console.log(`- ${t.role} (score ${(t.score ?? 0).toFixed(3)}): ${t.content}`);
|
|
421
|
+
}
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (sub === 'current') {
|
|
425
|
+
console.log(inferThread());
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
console.error('Usage: stitch thread [append|recall|current] …');
|
|
429
|
+
process.exit(2);
|
|
430
|
+
}
|
|
431
|
+
function readStdinAll() {
|
|
432
|
+
return new Promise((resolve) => {
|
|
433
|
+
let data = '';
|
|
434
|
+
if (process.stdin.isTTY) {
|
|
435
|
+
resolve('');
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
process.stdin.setEncoding('utf8');
|
|
439
|
+
process.stdin.on('data', (c) => (data += c));
|
|
440
|
+
process.stdin.on('end', () => resolve(data.trim()));
|
|
441
|
+
});
|
|
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
|
+
}
|
|
522
|
+
// ── One-shot install: MCP server + auto-log hooks + project CLAUDE.md ─────
|
|
523
|
+
async function cmdInstall(args) {
|
|
524
|
+
const cfg = loadConfig();
|
|
525
|
+
if (!cfg.apiKey) {
|
|
526
|
+
console.error('Run `stitch login` first.');
|
|
527
|
+
process.exit(2);
|
|
528
|
+
}
|
|
529
|
+
const stitch = client(cfg);
|
|
530
|
+
const ws = await stitch.resolveWorkspace();
|
|
531
|
+
const baseUrl = cfg.baseUrl || 'https://db.stitchdb.com';
|
|
532
|
+
const mcpUrl = `${baseUrl}/mcp/v1/${ws}`;
|
|
533
|
+
const noMcp = hasFlag(args, ['--no-mcp']);
|
|
534
|
+
const noHooks = hasFlag(args, ['--no-hooks']);
|
|
535
|
+
const noClaudeMd = hasFlag(args, ['--no-claude-md']);
|
|
536
|
+
// 1. claude mcp add
|
|
537
|
+
if (!noMcp) {
|
|
538
|
+
const claudePath = process.env.STITCH_CLAUDE_BIN || 'claude';
|
|
539
|
+
process.stdout.write('• Adding Stitch as an MCP server in Claude Code… ');
|
|
540
|
+
const { exit_code, stderr } = await runSilent(claudePath, [
|
|
541
|
+
'mcp', 'add', '--transport', 'http', '--scope', 'user', 'stitch', mcpUrl,
|
|
542
|
+
'-H', `Authorization: Bearer ${cfg.apiKey}`,
|
|
543
|
+
]);
|
|
544
|
+
if (exit_code === 0)
|
|
545
|
+
console.log('ok');
|
|
546
|
+
else if (stderr.includes('already exists'))
|
|
547
|
+
console.log('already configured');
|
|
548
|
+
else
|
|
549
|
+
console.log(`failed (${stderr.trim().slice(0, 120)})`);
|
|
550
|
+
}
|
|
551
|
+
// 2. Hooks for auto-logging conversations
|
|
552
|
+
if (!noHooks) {
|
|
553
|
+
process.stdout.write('• Wiring auto-log hooks (UserPromptSubmit + Stop)… ');
|
|
554
|
+
try {
|
|
555
|
+
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
|
556
|
+
const existing = fs.existsSync(settingsPath)
|
|
557
|
+
? JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
|
|
558
|
+
: {};
|
|
559
|
+
existing.hooks = existing.hooks || {};
|
|
560
|
+
existing.hooks.UserPromptSubmit = mergeHook(existing.hooks.UserPromptSubmit, STITCH_USER_HOOK);
|
|
561
|
+
existing.hooks.Stop = mergeHook(existing.hooks.Stop, STITCH_STOP_HOOK);
|
|
562
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
563
|
+
fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
|
|
564
|
+
console.log('ok');
|
|
565
|
+
}
|
|
566
|
+
catch (e) {
|
|
567
|
+
console.log(`skipped (${e.message})`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
// 3. CLAUDE.md instruction in cwd so Claude auto-recalls at session start
|
|
571
|
+
if (!noClaudeMd) {
|
|
572
|
+
const claudeMd = path.join(process.cwd(), 'CLAUDE.md');
|
|
573
|
+
const block = STITCH_CLAUDE_MD_BLOCK;
|
|
574
|
+
let body = '';
|
|
575
|
+
let action = 'created';
|
|
576
|
+
if (fs.existsSync(claudeMd)) {
|
|
577
|
+
body = fs.readFileSync(claudeMd, 'utf8');
|
|
578
|
+
if (body.includes('<!-- stitch:auto -->')) {
|
|
579
|
+
action = 'already present';
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
body = body.trimEnd() + '\n\n' + block + '\n';
|
|
583
|
+
action = 'appended';
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
body = block + '\n';
|
|
588
|
+
}
|
|
589
|
+
if (action !== 'already present')
|
|
590
|
+
fs.writeFileSync(claudeMd, body);
|
|
591
|
+
console.log(`• ${action === 'created' ? 'Wrote' : action === 'appended' ? 'Appended to' : 'Found'} ./CLAUDE.md (${action})`);
|
|
592
|
+
}
|
|
593
|
+
console.log();
|
|
594
|
+
console.log('Done. Open a fresh `claude` session — Stitch is wired.');
|
|
595
|
+
console.log(' • remember/recall tools are available');
|
|
596
|
+
console.log(' • every turn auto-logs to the thread for this repo');
|
|
597
|
+
console.log(' • Claude auto-pulls past context at session start (per CLAUDE.md)');
|
|
598
|
+
}
|
|
599
|
+
const STITCH_USER_HOOK = {
|
|
600
|
+
matcher: '*',
|
|
601
|
+
hooks: [
|
|
602
|
+
{ type: 'command', command: `bash -c 'jq -r .prompt | stitch thread append --role user --thread "$(stitch thread current)" 2>/dev/null || true'` },
|
|
603
|
+
],
|
|
604
|
+
};
|
|
605
|
+
const STITCH_STOP_HOOK = {
|
|
606
|
+
matcher: '*',
|
|
607
|
+
hooks: [
|
|
608
|
+
{ type: 'command', command: `bash -c 'jq -r .response.text 2>/dev/null | stitch thread append --role assistant --thread "$(stitch thread current)" 2>/dev/null || true'` },
|
|
609
|
+
],
|
|
610
|
+
};
|
|
611
|
+
function mergeHook(existing, entry) {
|
|
612
|
+
const arr = Array.isArray(existing) ? existing.slice() : [];
|
|
613
|
+
// Replace any earlier Stitch entry; identify by marker substring.
|
|
614
|
+
const isStitch = (h) => JSON.stringify(h).includes('stitch thread append');
|
|
615
|
+
const filtered = arr.filter((h) => !isStitch(h));
|
|
616
|
+
filtered.push(entry);
|
|
617
|
+
return filtered;
|
|
618
|
+
}
|
|
619
|
+
const STITCH_CLAUDE_MD_BLOCK = `<!-- stitch:auto -->
|
|
620
|
+
## Stitch memory
|
|
621
|
+
|
|
622
|
+
This project is wired to Stitch (memory layer for AI agents).
|
|
623
|
+
|
|
624
|
+
When a session starts here, **call \`recall\` from the \`stitch\` MCP server first**
|
|
625
|
+
to pull any prior facts, decisions, or discussion threads about this repo before
|
|
626
|
+
answering. The thread name for this repo is reported by \`stitch thread current\`.
|
|
627
|
+
|
|
628
|
+
When you learn something durable about this codebase or its conventions, call
|
|
629
|
+
\`remember\` to save it — \`fact\`, \`decision\`, \`snippet\`, or \`preference\`.
|
|
630
|
+
|
|
631
|
+
Every user message and assistant reply is also auto-logged to a per-repo thread
|
|
632
|
+
via Claude Code hooks, so future sessions can \`thread_recall\` to resume context.
|
|
633
|
+
<!-- /stitch:auto -->`;
|
|
634
|
+
async function runSilent(cmd, args) {
|
|
635
|
+
return new Promise((resolve) => {
|
|
636
|
+
const child = spawn(cmd, args);
|
|
637
|
+
let stdout = '';
|
|
638
|
+
let stderr = '';
|
|
639
|
+
child.stdout.on('data', (d) => (stdout += d.toString()));
|
|
640
|
+
child.stderr.on('data', (d) => (stderr += d.toString()));
|
|
641
|
+
child.on('error', (e) => resolve({ stdout, stderr: stderr + '\n' + e.message, exit_code: 127 }));
|
|
642
|
+
child.on('close', (code) => resolve({ stdout, stderr, exit_code: code ?? 0 }));
|
|
643
|
+
});
|
|
644
|
+
}
|
|
309
645
|
async function cmdMcpInstall(args) {
|
|
310
646
|
const cfg = loadConfig();
|
|
311
647
|
if (!cfg.apiKey) {
|
|
@@ -445,9 +781,24 @@ function help() {
|
|
|
445
781
|
stitch whoami Show the configured key.
|
|
446
782
|
stitch logout
|
|
447
783
|
|
|
784
|
+
stitch update Update to the latest @stitchdb/cli.
|
|
785
|
+
stitch version Print the installed version.
|
|
786
|
+
|
|
787
|
+
stitch install [--no-mcp] [--no-hooks] [--no-claude-md]
|
|
788
|
+
One-shot: register Stitch as an MCP
|
|
789
|
+
server in Claude Code, wire up auto-log
|
|
790
|
+
hooks, drop a CLAUDE.md instruction in
|
|
791
|
+
the current project so Claude auto-recalls
|
|
792
|
+
at session start.
|
|
793
|
+
|
|
448
794
|
stitch remember <text> [--kind X] [--tag a,b]
|
|
449
795
|
stitch recall <query> [-k 5]
|
|
450
796
|
|
|
797
|
+
stitch thread append --role user|assistant [--thread <name>] [--content <text>]
|
|
798
|
+
stitch thread recall [--thread <name>] [--last 10] [--semantic <q>]
|
|
799
|
+
stitch thread current Print the auto-derived thread name
|
|
800
|
+
for the current repo / cwd.
|
|
801
|
+
|
|
451
802
|
stitch workspace [list | create <name> | use <id>]
|
|
452
803
|
|
|
453
804
|
stitch agent register <name> Create an agent identity (id only).
|
|
@@ -469,6 +820,16 @@ async function main(argv) {
|
|
|
469
820
|
case 'whoami': return cmdWhoami();
|
|
470
821
|
case 'remember': return cmdRemember(rest);
|
|
471
822
|
case 'recall': return cmdRecall(rest);
|
|
823
|
+
case 'thread': return cmdThread(rest);
|
|
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
|
+
}
|
|
472
833
|
case 'workspace':
|
|
473
834
|
case 'ws': return cmdWorkspace(rest);
|
|
474
835
|
case 'agent': return cmdAgent(rest);
|
|
@@ -489,4 +850,4 @@ async function main(argv) {
|
|
|
489
850
|
process.exit(1);
|
|
490
851
|
}
|
|
491
852
|
}
|
|
492
|
-
main(process.argv.slice(2));
|
|
853
|
+
main(process.argv.slice(2)).then(() => maybeNotifyUpdate());
|