@keepgoingdev/cli 1.4.0 → 1.6.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/README.md +18 -5
- package/dist/index.js +310 -135
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,14 +38,27 @@ Flags:
|
|
|
38
38
|
|
|
39
39
|
### `keepgoing save`
|
|
40
40
|
|
|
41
|
-
Save a new checkpoint
|
|
42
|
-
|
|
43
|
-
1. What did you work on? (required)
|
|
44
|
-
2. What's your next step? (required)
|
|
45
|
-
3. Any blockers? (optional)
|
|
41
|
+
Save a new checkpoint. By default, auto-generates the summary and next step from recent git activity (commits and touched files). No interactive prompts.
|
|
46
42
|
|
|
47
43
|
Git branch and touched files are auto-detected from the workspace.
|
|
48
44
|
|
|
45
|
+
Flags:
|
|
46
|
+
|
|
47
|
+
- `-m, --message <text>` — use a custom summary instead of auto-generating
|
|
48
|
+
- `-n, --next <text>` — use a custom next step instead of auto-generating
|
|
49
|
+
- `--force` — save even if a recent checkpoint exists or there are no detected changes
|
|
50
|
+
- `--json` — output raw JSON
|
|
51
|
+
- `--quiet` — suppress output
|
|
52
|
+
- `--cwd <path>` — override the working directory
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
keepgoing save # auto-generate from git
|
|
58
|
+
keepgoing save -m "Finished auth flow" # custom summary
|
|
59
|
+
keepgoing save --force # save even if no changes
|
|
60
|
+
```
|
|
61
|
+
|
|
49
62
|
### `keepgoing hook install`
|
|
50
63
|
|
|
51
64
|
Install a shell hook that runs `keepgoing status --quiet` automatically whenever you `cd` into a directory that contains `.keepgoing/`.
|
package/dist/index.js
CHANGED
|
@@ -2801,96 +2801,7 @@ function renderEnrichedBriefingQuiet(briefing) {
|
|
|
2801
2801
|
console.log(`KeepGoing \xB7 ${core.lastWorked} \xB7 Focus: ${core.currentFocus} \xB7 Next: ${core.smallNextStep}`);
|
|
2802
2802
|
}
|
|
2803
2803
|
|
|
2804
|
-
// src/updateCheck.ts
|
|
2805
|
-
import { spawn } from "child_process";
|
|
2806
|
-
import { readFileSync, existsSync } from "fs";
|
|
2807
|
-
import path11 from "path";
|
|
2808
|
-
import os5 from "os";
|
|
2809
|
-
var CLI_VERSION = "1.4.0";
|
|
2810
|
-
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@keepgoingdev/cli/latest";
|
|
2811
|
-
var FETCH_TIMEOUT_MS = 5e3;
|
|
2812
|
-
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
2813
|
-
var CACHE_DIR = path11.join(os5.homedir(), ".keepgoing");
|
|
2814
|
-
var CACHE_PATH = path11.join(CACHE_DIR, "update-check.json");
|
|
2815
|
-
function isNewerVersion(current, latest) {
|
|
2816
|
-
const cur = current.split(".").map(Number);
|
|
2817
|
-
const lat = latest.split(".").map(Number);
|
|
2818
|
-
for (let i = 0; i < 3; i++) {
|
|
2819
|
-
if ((lat[i] ?? 0) > (cur[i] ?? 0)) return true;
|
|
2820
|
-
if ((lat[i] ?? 0) < (cur[i] ?? 0)) return false;
|
|
2821
|
-
}
|
|
2822
|
-
return false;
|
|
2823
|
-
}
|
|
2824
|
-
function getCachedUpdateInfo() {
|
|
2825
|
-
try {
|
|
2826
|
-
if (!existsSync(CACHE_PATH)) return null;
|
|
2827
|
-
const raw = readFileSync(CACHE_PATH, "utf-8");
|
|
2828
|
-
const cache = JSON.parse(raw);
|
|
2829
|
-
if (!cache.latest || !cache.checkedAt) return null;
|
|
2830
|
-
const age = Date.now() - new Date(cache.checkedAt).getTime();
|
|
2831
|
-
if (age > CHECK_INTERVAL_MS || cache.current !== CLI_VERSION) return null;
|
|
2832
|
-
return {
|
|
2833
|
-
current: CLI_VERSION,
|
|
2834
|
-
latest: cache.latest,
|
|
2835
|
-
updateAvailable: isNewerVersion(CLI_VERSION, cache.latest)
|
|
2836
|
-
};
|
|
2837
|
-
} catch {
|
|
2838
|
-
return null;
|
|
2839
|
-
}
|
|
2840
|
-
}
|
|
2841
|
-
function spawnBackgroundCheck() {
|
|
2842
|
-
try {
|
|
2843
|
-
if (existsSync(CACHE_PATH)) {
|
|
2844
|
-
const raw = readFileSync(CACHE_PATH, "utf-8");
|
|
2845
|
-
const cache = JSON.parse(raw);
|
|
2846
|
-
const age = Date.now() - new Date(cache.checkedAt).getTime();
|
|
2847
|
-
if (age < CHECK_INTERVAL_MS && cache.current === CLI_VERSION) return;
|
|
2848
|
-
}
|
|
2849
|
-
} catch {
|
|
2850
|
-
}
|
|
2851
|
-
const script = `
|
|
2852
|
-
const https = require('https');
|
|
2853
|
-
const fs = require('fs');
|
|
2854
|
-
const path = require('path');
|
|
2855
|
-
const os = require('os');
|
|
2856
|
-
|
|
2857
|
-
const url = ${JSON.stringify(NPM_REGISTRY_URL)};
|
|
2858
|
-
const cacheDir = path.join(os.homedir(), '.keepgoing');
|
|
2859
|
-
const cachePath = path.join(cacheDir, 'update-check.json');
|
|
2860
|
-
const currentVersion = ${JSON.stringify(CLI_VERSION)};
|
|
2861
|
-
|
|
2862
|
-
const req = https.get(url, { timeout: ${FETCH_TIMEOUT_MS} }, (res) => {
|
|
2863
|
-
let data = '';
|
|
2864
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
2865
|
-
res.on('end', () => {
|
|
2866
|
-
try {
|
|
2867
|
-
const latest = JSON.parse(data).version;
|
|
2868
|
-
if (!latest) process.exit(0);
|
|
2869
|
-
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
2870
|
-
fs.writeFileSync(cachePath, JSON.stringify({
|
|
2871
|
-
latest,
|
|
2872
|
-
current: currentVersion,
|
|
2873
|
-
checkedAt: new Date().toISOString(),
|
|
2874
|
-
}));
|
|
2875
|
-
} catch {}
|
|
2876
|
-
process.exit(0);
|
|
2877
|
-
});
|
|
2878
|
-
});
|
|
2879
|
-
req.on('error', () => process.exit(0));
|
|
2880
|
-
req.on('timeout', () => { req.destroy(); process.exit(0); });
|
|
2881
|
-
`;
|
|
2882
|
-
const child = spawn(process.execPath, ["-e", script], {
|
|
2883
|
-
detached: true,
|
|
2884
|
-
stdio: "ignore",
|
|
2885
|
-
env: { ...process.env }
|
|
2886
|
-
});
|
|
2887
|
-
child.unref();
|
|
2888
|
-
}
|
|
2889
|
-
|
|
2890
2804
|
// src/commands/status.ts
|
|
2891
|
-
var RESET2 = "\x1B[0m";
|
|
2892
|
-
var BOLD2 = "\x1B[1m";
|
|
2893
|
-
var DIM2 = "\x1B[2m";
|
|
2894
2805
|
async function statusCommand(opts) {
|
|
2895
2806
|
const reader = new KeepGoingReader(opts.cwd);
|
|
2896
2807
|
if (!reader.exists()) {
|
|
@@ -2918,15 +2829,10 @@ async function statusCommand(opts) {
|
|
|
2918
2829
|
(Date.now() - new Date(lastSession.timestamp).getTime()) / (1e3 * 60 * 60 * 24)
|
|
2919
2830
|
);
|
|
2920
2831
|
renderCheckpoint(lastSession, daysSince);
|
|
2921
|
-
const cached = getCachedUpdateInfo();
|
|
2922
|
-
if (cached?.updateAvailable) {
|
|
2923
|
-
console.log(`${DIM2}Update available: ${cached.current} \u2192 ${cached.latest}. Run: ${RESET2}${BOLD2}npm install -g @keepgoingdev/cli@latest${RESET2}`);
|
|
2924
|
-
}
|
|
2925
|
-
spawnBackgroundCheck();
|
|
2926
2832
|
}
|
|
2927
2833
|
|
|
2928
2834
|
// src/commands/save.ts
|
|
2929
|
-
import
|
|
2835
|
+
import path11 from "path";
|
|
2930
2836
|
async function saveCommand(opts) {
|
|
2931
2837
|
const { cwd, message, nextStepOverride, json, quiet, force } = opts;
|
|
2932
2838
|
const isManual = !!message;
|
|
@@ -2955,9 +2861,9 @@ async function saveCommand(opts) {
|
|
|
2955
2861
|
sessionStartTime: lastSession?.timestamp ?? now,
|
|
2956
2862
|
lastActivityTime: now
|
|
2957
2863
|
});
|
|
2958
|
-
const summary = message ?? buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map((f) =>
|
|
2864
|
+
const summary = message ?? buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map((f) => path11.basename(f)).join(", ")}`;
|
|
2959
2865
|
const nextStep = nextStepOverride ?? buildSmartNextStep(events);
|
|
2960
|
-
const projectName =
|
|
2866
|
+
const projectName = path11.basename(resolveStorageRoot(cwd));
|
|
2961
2867
|
const sessionId = generateSessionId({
|
|
2962
2868
|
workspaceRoot: cwd,
|
|
2963
2869
|
branch: gitBranch ?? void 0,
|
|
@@ -2991,16 +2897,16 @@ async function saveCommand(opts) {
|
|
|
2991
2897
|
|
|
2992
2898
|
// src/commands/hook.ts
|
|
2993
2899
|
import fs9 from "fs";
|
|
2994
|
-
import
|
|
2995
|
-
import
|
|
2900
|
+
import path12 from "path";
|
|
2901
|
+
import os5 from "os";
|
|
2996
2902
|
import { execSync as execSync2 } from "child_process";
|
|
2997
2903
|
var HOOK_MARKER_START = "# keepgoing-hook-start";
|
|
2998
2904
|
var HOOK_MARKER_END = "# keepgoing-hook-end";
|
|
2999
2905
|
var POST_COMMIT_MARKER_START = "# keepgoing-post-commit-start";
|
|
3000
2906
|
var POST_COMMIT_MARKER_END = "# keepgoing-post-commit-end";
|
|
3001
|
-
var KEEPGOING_HOOKS_DIR =
|
|
3002
|
-
var POST_COMMIT_HOOK_PATH =
|
|
3003
|
-
var KEEPGOING_MANAGED_MARKER =
|
|
2907
|
+
var KEEPGOING_HOOKS_DIR = path12.join(os5.homedir(), ".keepgoing", "hooks");
|
|
2908
|
+
var POST_COMMIT_HOOK_PATH = path12.join(KEEPGOING_HOOKS_DIR, "post-commit");
|
|
2909
|
+
var KEEPGOING_MANAGED_MARKER = path12.join(KEEPGOING_HOOKS_DIR, ".keepgoing-managed");
|
|
3004
2910
|
var POST_COMMIT_HOOK = `#!/bin/sh
|
|
3005
2911
|
${POST_COMMIT_MARKER_START}
|
|
3006
2912
|
# Runs after every git commit. Detects high-signal decisions.
|
|
@@ -3041,7 +2947,7 @@ if command -v keepgoing >/dev/null 2>&1
|
|
|
3041
2947
|
end
|
|
3042
2948
|
${HOOK_MARKER_END}`;
|
|
3043
2949
|
function detectShellInfo(shellOverride) {
|
|
3044
|
-
const home =
|
|
2950
|
+
const home = os5.homedir();
|
|
3045
2951
|
let shell;
|
|
3046
2952
|
if (shellOverride) {
|
|
3047
2953
|
shell = shellOverride.toLowerCase();
|
|
@@ -3064,14 +2970,14 @@ function detectShellInfo(shellOverride) {
|
|
|
3064
2970
|
}
|
|
3065
2971
|
}
|
|
3066
2972
|
if (shell === "zsh") {
|
|
3067
|
-
return { shell: "zsh", rcFile:
|
|
2973
|
+
return { shell: "zsh", rcFile: path12.join(home, ".zshrc") };
|
|
3068
2974
|
}
|
|
3069
2975
|
if (shell === "bash") {
|
|
3070
|
-
return { shell: "bash", rcFile:
|
|
2976
|
+
return { shell: "bash", rcFile: path12.join(home, ".bashrc") };
|
|
3071
2977
|
}
|
|
3072
2978
|
if (shell === "fish") {
|
|
3073
|
-
const xdgConfig = process.env["XDG_CONFIG_HOME"] ||
|
|
3074
|
-
return { shell: "fish", rcFile:
|
|
2979
|
+
const xdgConfig = process.env["XDG_CONFIG_HOME"] || path12.join(home, ".config");
|
|
2980
|
+
return { shell: "fish", rcFile: path12.join(xdgConfig, "fish", "config.fish") };
|
|
3075
2981
|
}
|
|
3076
2982
|
return void 0;
|
|
3077
2983
|
}
|
|
@@ -3102,11 +3008,11 @@ function resolveGlobalGitignorePath() {
|
|
|
3102
3008
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3103
3009
|
}).trim();
|
|
3104
3010
|
if (configured) {
|
|
3105
|
-
return configured.startsWith("~") ?
|
|
3011
|
+
return configured.startsWith("~") ? path12.join(os5.homedir(), configured.slice(1)) : configured;
|
|
3106
3012
|
}
|
|
3107
3013
|
} catch {
|
|
3108
3014
|
}
|
|
3109
|
-
return
|
|
3015
|
+
return path12.join(os5.homedir(), ".gitignore_global");
|
|
3110
3016
|
}
|
|
3111
3017
|
function performGlobalGitignore() {
|
|
3112
3018
|
const ignorePath = resolveGlobalGitignorePath();
|
|
@@ -3426,12 +3332,12 @@ async function briefingCommand(opts) {
|
|
|
3426
3332
|
}
|
|
3427
3333
|
|
|
3428
3334
|
// src/commands/init.ts
|
|
3429
|
-
var
|
|
3430
|
-
var
|
|
3335
|
+
var RESET2 = "\x1B[0m";
|
|
3336
|
+
var BOLD2 = "\x1B[1m";
|
|
3431
3337
|
var GREEN2 = "\x1B[32m";
|
|
3432
3338
|
var YELLOW2 = "\x1B[33m";
|
|
3433
3339
|
var CYAN2 = "\x1B[36m";
|
|
3434
|
-
var
|
|
3340
|
+
var DIM2 = "\x1B[2m";
|
|
3435
3341
|
function initCommand(options) {
|
|
3436
3342
|
const scope = options.scope === "user" ? "user" : "project";
|
|
3437
3343
|
const result = setupProject({
|
|
@@ -3439,7 +3345,7 @@ function initCommand(options) {
|
|
|
3439
3345
|
scope
|
|
3440
3346
|
});
|
|
3441
3347
|
console.log(`
|
|
3442
|
-
${
|
|
3348
|
+
${BOLD2}KeepGoing Init${RESET2} ${DIM2}(${scope} scope)${RESET2}
|
|
3443
3349
|
`);
|
|
3444
3350
|
for (const msg of result.messages) {
|
|
3445
3351
|
const colonIdx = msg.indexOf(":");
|
|
@@ -3450,16 +3356,16 @@ ${BOLD3}KeepGoing Init${RESET3} ${DIM3}(${scope} scope)${RESET3}
|
|
|
3450
3356
|
const label = msg.slice(0, colonIdx + 1);
|
|
3451
3357
|
const body = msg.slice(colonIdx + 1);
|
|
3452
3358
|
if (label.startsWith("Warning")) {
|
|
3453
|
-
console.log(` ${YELLOW2}${label}${
|
|
3359
|
+
console.log(` ${YELLOW2}${label}${RESET2}${body}`);
|
|
3454
3360
|
} else if (body.includes("Added")) {
|
|
3455
|
-
console.log(` ${GREEN2}${label}${
|
|
3361
|
+
console.log(` ${GREEN2}${label}${RESET2}${body}`);
|
|
3456
3362
|
} else {
|
|
3457
|
-
console.log(` ${CYAN2}${label}${
|
|
3363
|
+
console.log(` ${CYAN2}${label}${RESET2}${body}`);
|
|
3458
3364
|
}
|
|
3459
3365
|
}
|
|
3460
3366
|
if (result.changed) {
|
|
3461
3367
|
console.log(`
|
|
3462
|
-
${GREEN2}Done!${
|
|
3368
|
+
${GREEN2}Done!${RESET2} KeepGoing is set up for this project.
|
|
3463
3369
|
`);
|
|
3464
3370
|
} else {
|
|
3465
3371
|
console.log(`
|
|
@@ -3470,8 +3376,8 @@ Everything was already configured. No changes made.
|
|
|
3470
3376
|
|
|
3471
3377
|
// src/commands/setup.ts
|
|
3472
3378
|
import fs10 from "fs";
|
|
3473
|
-
import
|
|
3474
|
-
import
|
|
3379
|
+
import path13 from "path";
|
|
3380
|
+
import os6 from "os";
|
|
3475
3381
|
import { execSync as execSync3, exec as exec2 } from "child_process";
|
|
3476
3382
|
import { promisify as promisify2 } from "util";
|
|
3477
3383
|
|
|
@@ -5396,14 +5302,15 @@ function cancelAndExit() {
|
|
|
5396
5302
|
|
|
5397
5303
|
// src/commands/setup.ts
|
|
5398
5304
|
var execAsync = promisify2(exec2);
|
|
5399
|
-
var VALID_PRESETS = ["vscode", "claude", "copilot", "cursor", "windsurf", "jetbrains"];
|
|
5305
|
+
var VALID_PRESETS = ["vscode", "claude", "copilot", "cursor", "windsurf", "jetbrains", "desktop-tray"];
|
|
5400
5306
|
var PRESET_DEFAULTS = {
|
|
5401
5307
|
vscode: { ide: ["vscode"] },
|
|
5402
5308
|
jetbrains: { ide: ["jetbrains"] },
|
|
5403
5309
|
claude: { tools: ["claude-code"] },
|
|
5404
5310
|
copilot: { tools: ["copilot"] },
|
|
5405
5311
|
cursor: { tools: ["cursor"] },
|
|
5406
|
-
windsurf: { tools: ["windsurf"] }
|
|
5312
|
+
windsurf: { tools: ["windsurf"] },
|
|
5313
|
+
"desktop-tray": { desktopTray: true }
|
|
5407
5314
|
};
|
|
5408
5315
|
var PRESET_LABELS = {
|
|
5409
5316
|
vscode: "VS Code",
|
|
@@ -5411,7 +5318,8 @@ var PRESET_LABELS = {
|
|
|
5411
5318
|
claude: "Claude Code",
|
|
5412
5319
|
copilot: "GitHub Copilot",
|
|
5413
5320
|
cursor: "Cursor",
|
|
5414
|
-
windsurf: "Windsurf"
|
|
5321
|
+
windsurf: "Windsurf",
|
|
5322
|
+
"desktop-tray": "Desktop Tray"
|
|
5415
5323
|
};
|
|
5416
5324
|
var JETBRAINS_PLUGIN_URL = "https://plugins.jetbrains.com/plugin/30449-keepgoing";
|
|
5417
5325
|
var JETBRAINS_PLUGIN_ID = "com.keepgoing.plugin";
|
|
@@ -5433,14 +5341,14 @@ function detectJetBrainsIdes() {
|
|
|
5433
5341
|
if (process.platform !== "darwin") return [];
|
|
5434
5342
|
return JETBRAINS_IDES.filter((ide) => {
|
|
5435
5343
|
try {
|
|
5436
|
-
return fs10.statSync(
|
|
5344
|
+
return fs10.statSync(path13.join("/Applications", ide.app)).isDirectory();
|
|
5437
5345
|
} catch {
|
|
5438
5346
|
return false;
|
|
5439
5347
|
}
|
|
5440
5348
|
});
|
|
5441
5349
|
}
|
|
5442
5350
|
async function setupCommand(options) {
|
|
5443
|
-
const displayPath = options.cwd.startsWith(
|
|
5351
|
+
const displayPath = options.cwd.startsWith(os6.homedir()) ? "~" + options.cwd.slice(os6.homedir().length) : options.cwd;
|
|
5444
5352
|
const presetDefaults = options.preset ? PRESET_DEFAULTS[options.preset] : void 0;
|
|
5445
5353
|
const presetLabel = options.preset ? PRESET_LABELS[options.preset] : void 0;
|
|
5446
5354
|
dist_exports.intro(presetLabel ? `KeepGoing Setup - ${presetLabel}` : "KeepGoing Setup Wizard");
|
|
@@ -5461,7 +5369,7 @@ async function setupCommand(options) {
|
|
|
5461
5369
|
tools = presetDefaults.tools;
|
|
5462
5370
|
dist_exports.log.step(`AI tool: ${tools.map((t2) => t2 === "claude-code" ? "Claude Code" : t2.charAt(0).toUpperCase() + t2.slice(1)).join(", ")}`);
|
|
5463
5371
|
const addMore = await dist_exports.multiselect({
|
|
5464
|
-
message: "Also configure any of these?",
|
|
5372
|
+
message: "Also configure any of these? (enter to skip)",
|
|
5465
5373
|
options: [
|
|
5466
5374
|
...!tools.includes("claude-code") ? [{ label: "Claude Code", value: "claude-code" }] : [],
|
|
5467
5375
|
...!tools.includes("copilot") ? [{ label: "GitHub Copilot", value: "copilot" }] : [],
|
|
@@ -5500,12 +5408,14 @@ async function setupCommand(options) {
|
|
|
5500
5408
|
if (isCancel(pluginAnswer)) cancelAndExit();
|
|
5501
5409
|
claudePlugin = pluginAnswer === "plugin";
|
|
5502
5410
|
}
|
|
5411
|
+
const IDE_TOOLS = ["cursor", "windsurf"];
|
|
5412
|
+
const hasIdeTool = tools.some((t2) => IDE_TOOLS.includes(t2));
|
|
5503
5413
|
let ide;
|
|
5504
5414
|
if (presetDefaults?.ide) {
|
|
5505
5415
|
ide = presetDefaults.ide;
|
|
5506
5416
|
dist_exports.log.step(`IDE: ${ide.map((i) => i === "vscode" ? "VS Code" : i === "jetbrains" ? "JetBrains" : "Terminal").join(", ")}`);
|
|
5507
5417
|
const addMoreIde = await dist_exports.multiselect({
|
|
5508
|
-
message: "Also configure any of these?",
|
|
5418
|
+
message: "Also configure any of these? (enter to skip)",
|
|
5509
5419
|
options: [
|
|
5510
5420
|
...!ide.includes("vscode") ? [{ label: "VS Code", value: "vscode" }] : [],
|
|
5511
5421
|
...!ide.includes("jetbrains") ? [{ label: "JetBrains", hint: "IntelliJ, WebStorm, PyCharm, etc.", value: "jetbrains" }] : [],
|
|
@@ -5516,6 +5426,19 @@ async function setupCommand(options) {
|
|
|
5516
5426
|
if (!isCancel(addMoreIde) && addMoreIde.length > 0) {
|
|
5517
5427
|
ide = [...ide, ...addMoreIde];
|
|
5518
5428
|
}
|
|
5429
|
+
} else if (hasIdeTool) {
|
|
5430
|
+
ide = [];
|
|
5431
|
+
const addIdeExtensions = await dist_exports.multiselect({
|
|
5432
|
+
message: "Also configure IDE extensions? (enter to skip)",
|
|
5433
|
+
options: [
|
|
5434
|
+
{ label: "VS Code", value: "vscode" },
|
|
5435
|
+
{ label: "JetBrains", hint: "IntelliJ, WebStorm, PyCharm, etc.", value: "jetbrains" }
|
|
5436
|
+
],
|
|
5437
|
+
required: false
|
|
5438
|
+
});
|
|
5439
|
+
if (!isCancel(addIdeExtensions) && addIdeExtensions.length > 0) {
|
|
5440
|
+
ide = addIdeExtensions;
|
|
5441
|
+
}
|
|
5519
5442
|
} else {
|
|
5520
5443
|
const ideAnswer = await dist_exports.multiselect({
|
|
5521
5444
|
message: "Which IDE(s) do you use?",
|
|
@@ -5735,6 +5658,83 @@ async function setupCommand(options) {
|
|
|
5735
5658
|
}
|
|
5736
5659
|
}
|
|
5737
5660
|
}
|
|
5661
|
+
let installDesktopTray = false;
|
|
5662
|
+
const trayPreset = presetDefaults?.desktopTray === true;
|
|
5663
|
+
if (process.platform === "darwin") {
|
|
5664
|
+
const alreadyInstalled = fs10.existsSync("/Applications/KeepGoing.app");
|
|
5665
|
+
if (alreadyInstalled) {
|
|
5666
|
+
dist_exports.log.message(`${DIM5}Desktop tray: Already installed${RESET5}`);
|
|
5667
|
+
installDesktopTray = true;
|
|
5668
|
+
} else {
|
|
5669
|
+
if (!trayPreset) {
|
|
5670
|
+
dist_exports.note(
|
|
5671
|
+
"A menubar app that shows re-entry briefings, active sessions,\nand momentum across all your projects. Editor-agnostic, free.",
|
|
5672
|
+
"Desktop Tray"
|
|
5673
|
+
);
|
|
5674
|
+
}
|
|
5675
|
+
const wantsTray = trayPreset || await (async () => {
|
|
5676
|
+
const trayAnswer = await dist_exports.confirm({
|
|
5677
|
+
message: "Install the desktop tray app?",
|
|
5678
|
+
initialValue: trayPreset,
|
|
5679
|
+
active: "Yes",
|
|
5680
|
+
inactive: "No, skip"
|
|
5681
|
+
});
|
|
5682
|
+
return !isCancel(trayAnswer) && trayAnswer;
|
|
5683
|
+
})();
|
|
5684
|
+
if (wantsTray) {
|
|
5685
|
+
installDesktopTray = true;
|
|
5686
|
+
let installed = false;
|
|
5687
|
+
let hasHomebrew = false;
|
|
5688
|
+
try {
|
|
5689
|
+
execSync3("brew --version", { stdio: "pipe" });
|
|
5690
|
+
hasHomebrew = true;
|
|
5691
|
+
} catch {
|
|
5692
|
+
}
|
|
5693
|
+
if (hasHomebrew) {
|
|
5694
|
+
const ts = dist_exports.spinner();
|
|
5695
|
+
ts.start("Installing desktop tray via Homebrew...");
|
|
5696
|
+
try {
|
|
5697
|
+
const { stderr } = await execAsync(
|
|
5698
|
+
"brew tap keepgoing-dev/tap && brew install --cask keepgoing",
|
|
5699
|
+
{ timeout: 12e4 }
|
|
5700
|
+
);
|
|
5701
|
+
if (stderr?.includes("already installed")) {
|
|
5702
|
+
ts.stop("Desktop tray: Already installed via Homebrew");
|
|
5703
|
+
} else {
|
|
5704
|
+
ts.stop("Desktop tray installed");
|
|
5705
|
+
}
|
|
5706
|
+
installed = true;
|
|
5707
|
+
try {
|
|
5708
|
+
execSync3("open -a KeepGoing", { stdio: "pipe" });
|
|
5709
|
+
} catch {
|
|
5710
|
+
}
|
|
5711
|
+
} catch {
|
|
5712
|
+
ts.stop("Homebrew install failed");
|
|
5713
|
+
}
|
|
5714
|
+
}
|
|
5715
|
+
if (!installed) {
|
|
5716
|
+
dist_exports.log.info(
|
|
5717
|
+
"Install manually:\n 1. Download from https://github.com/keepgoing-dev/releases/releases/latest\n 2. Drag KeepGoing.app to /Applications\n 3. Run: xattr -cr /Applications/KeepGoing.app\n 4. Open KeepGoing from Applications"
|
|
5718
|
+
);
|
|
5719
|
+
}
|
|
5720
|
+
} else {
|
|
5721
|
+
dist_exports.log.info("Desktop tray: Install anytime with:\n brew tap keepgoing-dev/tap && brew install --cask keepgoing");
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
if (installDesktopTray) {
|
|
5725
|
+
try {
|
|
5726
|
+
const existing = readTrayConfigProjects();
|
|
5727
|
+
if (!existing.includes(options.cwd)) {
|
|
5728
|
+
const trayConfigDir = path13.join(os6.homedir(), ".keepgoing");
|
|
5729
|
+
fs10.mkdirSync(trayConfigDir, { recursive: true });
|
|
5730
|
+
const trayConfigPath = path13.join(trayConfigDir, "tray-config.json");
|
|
5731
|
+
const updated = { projects: [...existing, options.cwd] };
|
|
5732
|
+
fs10.writeFileSync(trayConfigPath, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
5733
|
+
}
|
|
5734
|
+
} catch {
|
|
5735
|
+
}
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
5738
|
if (hasLicense) {
|
|
5739
5739
|
const key = await dist_exports.text({
|
|
5740
5740
|
message: "Enter your license key",
|
|
@@ -5791,12 +5791,13 @@ async function setupCommand(options) {
|
|
|
5791
5791
|
scope,
|
|
5792
5792
|
shellHook: installShellHook,
|
|
5793
5793
|
claudePlugin,
|
|
5794
|
+
desktopTray: installDesktopTray,
|
|
5794
5795
|
licensed
|
|
5795
5796
|
};
|
|
5796
|
-
const keepgoingDir =
|
|
5797
|
+
const keepgoingDir = path13.join(os6.homedir(), ".keepgoing");
|
|
5797
5798
|
fs10.mkdirSync(keepgoingDir, { recursive: true });
|
|
5798
5799
|
fs10.writeFileSync(
|
|
5799
|
-
|
|
5800
|
+
path13.join(keepgoingDir, "setup-profile.json"),
|
|
5800
5801
|
JSON.stringify(profile, null, 2) + "\n",
|
|
5801
5802
|
"utf-8"
|
|
5802
5803
|
);
|
|
@@ -6007,8 +6008,8 @@ function filterDecisions(decisions, opts) {
|
|
|
6007
6008
|
}
|
|
6008
6009
|
|
|
6009
6010
|
// src/commands/log.ts
|
|
6010
|
-
var
|
|
6011
|
-
var
|
|
6011
|
+
var RESET3 = "\x1B[0m";
|
|
6012
|
+
var DIM3 = "\x1B[2m";
|
|
6012
6013
|
function logSessions(reader, opts) {
|
|
6013
6014
|
const { effectiveBranch } = reader.resolveBranchScope(opts.branch || void 0);
|
|
6014
6015
|
let sessions = reader.getSessions();
|
|
@@ -6019,7 +6020,7 @@ function logSessions(reader, opts) {
|
|
|
6019
6020
|
sessions = filterSessions(sessions, opts);
|
|
6020
6021
|
const totalFiltered = sessions.length;
|
|
6021
6022
|
if (totalFiltered === 0) {
|
|
6022
|
-
console.log(`${
|
|
6023
|
+
console.log(`${DIM3}No checkpoints match the given filters.${RESET3}`);
|
|
6023
6024
|
return;
|
|
6024
6025
|
}
|
|
6025
6026
|
const displayed = sessions.slice(0, opts.count);
|
|
@@ -6043,7 +6044,7 @@ function logSessions(reader, opts) {
|
|
|
6043
6044
|
}
|
|
6044
6045
|
}
|
|
6045
6046
|
if (totalFiltered > opts.count) {
|
|
6046
|
-
console.log(`${
|
|
6047
|
+
console.log(`${DIM3}(showing ${displayed.length} of ${totalFiltered} checkpoints)${RESET3}`);
|
|
6047
6048
|
}
|
|
6048
6049
|
}
|
|
6049
6050
|
function renderGrouped(sessions, showStat) {
|
|
@@ -6082,7 +6083,7 @@ async function logDecisions(reader, opts) {
|
|
|
6082
6083
|
decisions = filterDecisions(decisions, opts);
|
|
6083
6084
|
const totalFiltered = decisions.length;
|
|
6084
6085
|
if (totalFiltered === 0) {
|
|
6085
|
-
console.log(`${
|
|
6086
|
+
console.log(`${DIM3}No decisions match the given filters.${RESET3}`);
|
|
6086
6087
|
return;
|
|
6087
6088
|
}
|
|
6088
6089
|
const displayed = decisions.slice(0, opts.count);
|
|
@@ -6104,7 +6105,7 @@ async function logDecisions(reader, opts) {
|
|
|
6104
6105
|
}
|
|
6105
6106
|
}
|
|
6106
6107
|
if (totalFiltered > opts.count) {
|
|
6107
|
-
console.log(`${
|
|
6108
|
+
console.log(`${DIM3}(showing ${displayed.length} of ${totalFiltered} decisions)${RESET3}`);
|
|
6108
6109
|
}
|
|
6109
6110
|
}
|
|
6110
6111
|
async function logCommand(opts) {
|
|
@@ -6245,6 +6246,154 @@ function hotCommand(opts) {
|
|
|
6245
6246
|
}
|
|
6246
6247
|
}
|
|
6247
6248
|
|
|
6249
|
+
// src/commands/update.ts
|
|
6250
|
+
import { execSync as execSync5 } from "child_process";
|
|
6251
|
+
|
|
6252
|
+
// src/updateCheck.ts
|
|
6253
|
+
import { spawn } from "child_process";
|
|
6254
|
+
import { readFileSync, existsSync } from "fs";
|
|
6255
|
+
import path14 from "path";
|
|
6256
|
+
import os7 from "os";
|
|
6257
|
+
var CLI_VERSION = "1.6.0";
|
|
6258
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@keepgoingdev/cli/latest";
|
|
6259
|
+
var FETCH_TIMEOUT_MS = 5e3;
|
|
6260
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
6261
|
+
var CACHE_DIR = path14.join(os7.homedir(), ".keepgoing");
|
|
6262
|
+
var CACHE_PATH = path14.join(CACHE_DIR, "update-check.json");
|
|
6263
|
+
function isNewerVersion(current, latest) {
|
|
6264
|
+
const cur = current.split(".").map(Number);
|
|
6265
|
+
const lat = latest.split(".").map(Number);
|
|
6266
|
+
for (let i = 0; i < 3; i++) {
|
|
6267
|
+
if ((lat[i] ?? 0) > (cur[i] ?? 0)) return true;
|
|
6268
|
+
if ((lat[i] ?? 0) < (cur[i] ?? 0)) return false;
|
|
6269
|
+
}
|
|
6270
|
+
return false;
|
|
6271
|
+
}
|
|
6272
|
+
function getCachedUpdateInfo() {
|
|
6273
|
+
try {
|
|
6274
|
+
if (!existsSync(CACHE_PATH)) return null;
|
|
6275
|
+
const raw = readFileSync(CACHE_PATH, "utf-8");
|
|
6276
|
+
const cache = JSON.parse(raw);
|
|
6277
|
+
if (!cache.latest || !cache.checkedAt) return null;
|
|
6278
|
+
const age = Date.now() - new Date(cache.checkedAt).getTime();
|
|
6279
|
+
if (age > CHECK_INTERVAL_MS || cache.current !== CLI_VERSION) return null;
|
|
6280
|
+
return {
|
|
6281
|
+
current: CLI_VERSION,
|
|
6282
|
+
latest: cache.latest,
|
|
6283
|
+
updateAvailable: isNewerVersion(CLI_VERSION, cache.latest)
|
|
6284
|
+
};
|
|
6285
|
+
} catch {
|
|
6286
|
+
return null;
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
function spawnBackgroundCheck() {
|
|
6290
|
+
try {
|
|
6291
|
+
if (existsSync(CACHE_PATH)) {
|
|
6292
|
+
const raw = readFileSync(CACHE_PATH, "utf-8");
|
|
6293
|
+
const cache = JSON.parse(raw);
|
|
6294
|
+
const age = Date.now() - new Date(cache.checkedAt).getTime();
|
|
6295
|
+
if (age < CHECK_INTERVAL_MS && cache.current === CLI_VERSION) return;
|
|
6296
|
+
}
|
|
6297
|
+
} catch {
|
|
6298
|
+
}
|
|
6299
|
+
const script = `
|
|
6300
|
+
const https = require('https');
|
|
6301
|
+
const fs = require('fs');
|
|
6302
|
+
const path = require('path');
|
|
6303
|
+
const os = require('os');
|
|
6304
|
+
|
|
6305
|
+
const url = ${JSON.stringify(NPM_REGISTRY_URL)};
|
|
6306
|
+
const cacheDir = path.join(os.homedir(), '.keepgoing');
|
|
6307
|
+
const cachePath = path.join(cacheDir, 'update-check.json');
|
|
6308
|
+
const currentVersion = ${JSON.stringify(CLI_VERSION)};
|
|
6309
|
+
|
|
6310
|
+
const req = https.get(url, { timeout: ${FETCH_TIMEOUT_MS} }, (res) => {
|
|
6311
|
+
let data = '';
|
|
6312
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
6313
|
+
res.on('end', () => {
|
|
6314
|
+
try {
|
|
6315
|
+
const latest = JSON.parse(data).version;
|
|
6316
|
+
if (!latest) process.exit(0);
|
|
6317
|
+
if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
|
|
6318
|
+
fs.writeFileSync(cachePath, JSON.stringify({
|
|
6319
|
+
latest,
|
|
6320
|
+
current: currentVersion,
|
|
6321
|
+
checkedAt: new Date().toISOString(),
|
|
6322
|
+
}));
|
|
6323
|
+
} catch {}
|
|
6324
|
+
process.exit(0);
|
|
6325
|
+
});
|
|
6326
|
+
});
|
|
6327
|
+
req.on('error', () => process.exit(0));
|
|
6328
|
+
req.on('timeout', () => { req.destroy(); process.exit(0); });
|
|
6329
|
+
`;
|
|
6330
|
+
const child = spawn(process.execPath, ["-e", script], {
|
|
6331
|
+
detached: true,
|
|
6332
|
+
stdio: "ignore",
|
|
6333
|
+
env: { ...process.env }
|
|
6334
|
+
});
|
|
6335
|
+
child.unref();
|
|
6336
|
+
}
|
|
6337
|
+
|
|
6338
|
+
// src/commands/update.ts
|
|
6339
|
+
var RESET4 = "\x1B[0m";
|
|
6340
|
+
var BOLD3 = "\x1B[1m";
|
|
6341
|
+
var DIM4 = "\x1B[2m";
|
|
6342
|
+
var GREEN3 = "\x1B[32m";
|
|
6343
|
+
var YELLOW3 = "\x1B[33m";
|
|
6344
|
+
var CLI_VERSION2 = "1.6.0";
|
|
6345
|
+
async function updateCommand() {
|
|
6346
|
+
console.log(`
|
|
6347
|
+
${BOLD3}KeepGoing CLI${RESET4} ${DIM4}v${CLI_VERSION2}${RESET4}
|
|
6348
|
+
`);
|
|
6349
|
+
console.log(`${DIM4}Checking for updates...${RESET4}`);
|
|
6350
|
+
let latest;
|
|
6351
|
+
try {
|
|
6352
|
+
latest = execSync5("npm view @keepgoingdev/cli version", {
|
|
6353
|
+
encoding: "utf-8",
|
|
6354
|
+
timeout: 1e4,
|
|
6355
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6356
|
+
}).trim();
|
|
6357
|
+
} catch {
|
|
6358
|
+
console.error(`${YELLOW3}Could not reach the npm registry. Check your network connection.${RESET4}
|
|
6359
|
+
`);
|
|
6360
|
+
process.exit(1);
|
|
6361
|
+
}
|
|
6362
|
+
if (!latest) {
|
|
6363
|
+
console.error(`${YELLOW3}Could not determine the latest version.${RESET4}
|
|
6364
|
+
`);
|
|
6365
|
+
process.exit(1);
|
|
6366
|
+
}
|
|
6367
|
+
const current = CLI_VERSION2;
|
|
6368
|
+
const updateAvailable = isNewerVersion(current, latest);
|
|
6369
|
+
if (!updateAvailable) {
|
|
6370
|
+
console.log(`${GREEN3}Already up to date.${RESET4}
|
|
6371
|
+
`);
|
|
6372
|
+
return;
|
|
6373
|
+
}
|
|
6374
|
+
console.log(`${YELLOW3}Update available:${RESET4} ${DIM4}${current}${RESET4} -> ${BOLD3}${latest}${RESET4}
|
|
6375
|
+
`);
|
|
6376
|
+
console.log(`${DIM4}Installing @keepgoingdev/cli@${latest}...${RESET4}
|
|
6377
|
+
`);
|
|
6378
|
+
try {
|
|
6379
|
+
execSync5(`npm install -g @keepgoingdev/cli@${latest}`, {
|
|
6380
|
+
encoding: "utf-8",
|
|
6381
|
+
timeout: 6e4,
|
|
6382
|
+
stdio: "inherit"
|
|
6383
|
+
});
|
|
6384
|
+
console.log(`
|
|
6385
|
+
${GREEN3}Updated to v${latest}${RESET4}
|
|
6386
|
+
`);
|
|
6387
|
+
} catch {
|
|
6388
|
+
console.error(`
|
|
6389
|
+
${YELLOW3}Update failed.${RESET4} Try manually:
|
|
6390
|
+
`);
|
|
6391
|
+
console.error(` ${BOLD3}npm install -g @keepgoingdev/cli@latest${RESET4}
|
|
6392
|
+
`);
|
|
6393
|
+
process.exit(1);
|
|
6394
|
+
}
|
|
6395
|
+
}
|
|
6396
|
+
|
|
6248
6397
|
// src/index.ts
|
|
6249
6398
|
var HELP_TEXT = `
|
|
6250
6399
|
keepgoing: resume side projects without the mental friction
|
|
@@ -6264,6 +6413,7 @@ Commands:
|
|
|
6264
6413
|
continue Export context for use in another AI tool
|
|
6265
6414
|
save Save a checkpoint (auto-generates from git)
|
|
6266
6415
|
hook Manage the shell hook (zsh, bash, fish)
|
|
6416
|
+
update Update the CLI to the latest version
|
|
6267
6417
|
activate <key> Activate a Pro license on this device
|
|
6268
6418
|
deactivate Deactivate the Pro license from this device
|
|
6269
6419
|
|
|
@@ -6301,6 +6451,7 @@ Presets:
|
|
|
6301
6451
|
windsurf Pre-select Windsurf
|
|
6302
6452
|
vscode Pre-select VS Code IDE
|
|
6303
6453
|
jetbrains Pre-select JetBrains IDE
|
|
6454
|
+
desktop-tray Install the desktop tray app
|
|
6304
6455
|
|
|
6305
6456
|
Options:
|
|
6306
6457
|
--cwd <path> Override the working directory
|
|
@@ -6428,6 +6579,13 @@ Usage: keepgoing activate <key>
|
|
|
6428
6579
|
|
|
6429
6580
|
Example:
|
|
6430
6581
|
keepgoing activate XXXX-XXXX-XXXX-XXXX
|
|
6582
|
+
`,
|
|
6583
|
+
update: `
|
|
6584
|
+
keepgoing update: Update the CLI to the latest version
|
|
6585
|
+
|
|
6586
|
+
Usage: keepgoing update
|
|
6587
|
+
|
|
6588
|
+
Checks the npm registry for a newer version and installs it globally.
|
|
6431
6589
|
`,
|
|
6432
6590
|
deactivate: `
|
|
6433
6591
|
keepgoing deactivate: Deactivate the Pro license from this device
|
|
@@ -6709,8 +6867,11 @@ async function main() {
|
|
|
6709
6867
|
console.log(COMMAND_HELP.hook);
|
|
6710
6868
|
}
|
|
6711
6869
|
break;
|
|
6870
|
+
case "update":
|
|
6871
|
+
await updateCommand();
|
|
6872
|
+
break;
|
|
6712
6873
|
case "version":
|
|
6713
|
-
console.log(`keepgoing v${"1.
|
|
6874
|
+
console.log(`keepgoing v${"1.6.0"}`);
|
|
6714
6875
|
break;
|
|
6715
6876
|
case "activate":
|
|
6716
6877
|
await activateCommand({ licenseKey: subcommand });
|
|
@@ -6725,6 +6886,20 @@ async function main() {
|
|
|
6725
6886
|
console.error(`Unknown command: "${command}". Run "keepgoing --help" for usage.`);
|
|
6726
6887
|
process.exit(1);
|
|
6727
6888
|
}
|
|
6889
|
+
if (command && command !== "update" && command !== "version" && command !== "help" && !json && !quiet && !parsed.help) {
|
|
6890
|
+
showUpdateHint();
|
|
6891
|
+
}
|
|
6892
|
+
}
|
|
6893
|
+
function showUpdateHint() {
|
|
6894
|
+
const cached = getCachedUpdateInfo();
|
|
6895
|
+
if (cached?.updateAvailable) {
|
|
6896
|
+
const DIM5 = "\x1B[2m";
|
|
6897
|
+
const BOLD4 = "\x1B[1m";
|
|
6898
|
+
const RESET5 = "\x1B[0m";
|
|
6899
|
+
console.log(`
|
|
6900
|
+
${DIM5}Update available: ${cached.current} -> ${cached.latest}. Run:${RESET5} ${BOLD4}keepgoing update${RESET5}`);
|
|
6901
|
+
}
|
|
6902
|
+
spawnBackgroundCheck();
|
|
6728
6903
|
}
|
|
6729
6904
|
main().catch((err) => {
|
|
6730
6905
|
console.error(err instanceof Error ? err.message : String(err));
|