@keepgoingdev/cli 1.1.1 → 1.2.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/index.js +371 -110
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -320,7 +320,8 @@ function buildRecentActivity(lastSession, recentSessions, recentCommitMessages)
|
|
|
320
320
|
parts.push("1 recent session");
|
|
321
321
|
}
|
|
322
322
|
if (lastSession.summary) {
|
|
323
|
-
|
|
323
|
+
const brief = lastSession.summary.length > 120 ? lastSession.summary.slice(0, 117) + "..." : lastSession.summary;
|
|
324
|
+
parts.push(`Last: ${brief}`);
|
|
324
325
|
}
|
|
325
326
|
if (lastSession.touchedFiles.length > 0) {
|
|
326
327
|
parts.push(`${lastSession.touchedFiles.length} files touched`);
|
|
@@ -585,9 +586,59 @@ function formatContinueOnPrompt(context, options) {
|
|
|
585
586
|
}
|
|
586
587
|
|
|
587
588
|
// ../../packages/shared/src/storage.ts
|
|
589
|
+
import fs2 from "fs";
|
|
590
|
+
import path4 from "path";
|
|
591
|
+
import { randomUUID as randomUUID2, createHash } from "crypto";
|
|
592
|
+
|
|
593
|
+
// ../../packages/shared/src/registry.ts
|
|
588
594
|
import fs from "fs";
|
|
595
|
+
import os from "os";
|
|
589
596
|
import path3 from "path";
|
|
590
|
-
|
|
597
|
+
var KEEPGOING_DIR = path3.join(os.homedir(), ".keepgoing");
|
|
598
|
+
var KNOWN_PROJECTS_FILE = path3.join(KEEPGOING_DIR, "known-projects.json");
|
|
599
|
+
var STALE_PROJECT_MS = 90 * 24 * 60 * 60 * 1e3;
|
|
600
|
+
function readKnownProjects() {
|
|
601
|
+
try {
|
|
602
|
+
if (fs.existsSync(KNOWN_PROJECTS_FILE)) {
|
|
603
|
+
const raw = JSON.parse(fs.readFileSync(KNOWN_PROJECTS_FILE, "utf-8"));
|
|
604
|
+
if (raw && Array.isArray(raw.projects)) {
|
|
605
|
+
return raw;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
} catch {
|
|
609
|
+
}
|
|
610
|
+
return { version: 1, projects: [] };
|
|
611
|
+
}
|
|
612
|
+
function writeKnownProjects(data) {
|
|
613
|
+
if (!fs.existsSync(KEEPGOING_DIR)) {
|
|
614
|
+
fs.mkdirSync(KEEPGOING_DIR, { recursive: true });
|
|
615
|
+
}
|
|
616
|
+
const tmpFile = KNOWN_PROJECTS_FILE + ".tmp";
|
|
617
|
+
fs.writeFileSync(tmpFile, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
618
|
+
fs.renameSync(tmpFile, KNOWN_PROJECTS_FILE);
|
|
619
|
+
}
|
|
620
|
+
function registerProject(projectPath, projectName) {
|
|
621
|
+
try {
|
|
622
|
+
const data = readKnownProjects();
|
|
623
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
624
|
+
const name = projectName || path3.basename(projectPath);
|
|
625
|
+
const existingIdx = data.projects.findIndex((p) => p.path === projectPath);
|
|
626
|
+
if (existingIdx >= 0) {
|
|
627
|
+
data.projects[existingIdx].lastSeen = now;
|
|
628
|
+
if (projectName) {
|
|
629
|
+
data.projects[existingIdx].name = name;
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
data.projects.push({ path: projectPath, name, lastSeen: now });
|
|
633
|
+
}
|
|
634
|
+
const cutoff = Date.now() - STALE_PROJECT_MS;
|
|
635
|
+
data.projects = data.projects.filter((p) => new Date(p.lastSeen).getTime() > cutoff);
|
|
636
|
+
writeKnownProjects(data);
|
|
637
|
+
} catch {
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// ../../packages/shared/src/storage.ts
|
|
591
642
|
var STORAGE_DIR = ".keepgoing";
|
|
592
643
|
var META_FILE = "meta.json";
|
|
593
644
|
var SESSIONS_FILE = "sessions.json";
|
|
@@ -609,23 +660,23 @@ var KeepGoingWriter = class {
|
|
|
609
660
|
currentTasksFilePath;
|
|
610
661
|
constructor(workspacePath) {
|
|
611
662
|
const mainRoot = resolveStorageRoot(workspacePath);
|
|
612
|
-
this.storagePath =
|
|
613
|
-
this.sessionsFilePath =
|
|
614
|
-
this.stateFilePath =
|
|
615
|
-
this.metaFilePath =
|
|
616
|
-
this.currentTasksFilePath =
|
|
663
|
+
this.storagePath = path4.join(mainRoot, STORAGE_DIR);
|
|
664
|
+
this.sessionsFilePath = path4.join(this.storagePath, SESSIONS_FILE);
|
|
665
|
+
this.stateFilePath = path4.join(this.storagePath, STATE_FILE);
|
|
666
|
+
this.metaFilePath = path4.join(this.storagePath, META_FILE);
|
|
667
|
+
this.currentTasksFilePath = path4.join(this.storagePath, CURRENT_TASKS_FILE);
|
|
617
668
|
}
|
|
618
669
|
ensureDir() {
|
|
619
|
-
if (!
|
|
620
|
-
|
|
670
|
+
if (!fs2.existsSync(this.storagePath)) {
|
|
671
|
+
fs2.mkdirSync(this.storagePath, { recursive: true });
|
|
621
672
|
}
|
|
622
673
|
}
|
|
623
674
|
saveCheckpoint(checkpoint, projectName) {
|
|
624
675
|
this.ensureDir();
|
|
625
676
|
let sessionsData;
|
|
626
677
|
try {
|
|
627
|
-
if (
|
|
628
|
-
const raw = JSON.parse(
|
|
678
|
+
if (fs2.existsSync(this.sessionsFilePath)) {
|
|
679
|
+
const raw = JSON.parse(fs2.readFileSync(this.sessionsFilePath, "utf-8"));
|
|
629
680
|
if (Array.isArray(raw)) {
|
|
630
681
|
sessionsData = { version: 1, project: projectName, sessions: raw };
|
|
631
682
|
} else {
|
|
@@ -643,20 +694,22 @@ var KeepGoingWriter = class {
|
|
|
643
694
|
if (sessionsData.sessions.length > MAX_SESSIONS) {
|
|
644
695
|
sessionsData.sessions = sessionsData.sessions.slice(-MAX_SESSIONS);
|
|
645
696
|
}
|
|
646
|
-
|
|
697
|
+
fs2.writeFileSync(this.sessionsFilePath, JSON.stringify(sessionsData, null, 2), "utf-8");
|
|
647
698
|
const state = {
|
|
648
699
|
lastSessionId: checkpoint.id,
|
|
649
700
|
lastKnownBranch: checkpoint.gitBranch,
|
|
650
701
|
lastActivityAt: checkpoint.timestamp
|
|
651
702
|
};
|
|
652
|
-
|
|
703
|
+
fs2.writeFileSync(this.stateFilePath, JSON.stringify(state, null, 2), "utf-8");
|
|
653
704
|
this.updateMeta(checkpoint.timestamp);
|
|
705
|
+
const mainRoot = resolveStorageRoot(this.storagePath);
|
|
706
|
+
registerProject(mainRoot, projectName);
|
|
654
707
|
}
|
|
655
708
|
updateMeta(timestamp) {
|
|
656
709
|
let meta;
|
|
657
710
|
try {
|
|
658
|
-
if (
|
|
659
|
-
meta = JSON.parse(
|
|
711
|
+
if (fs2.existsSync(this.metaFilePath)) {
|
|
712
|
+
meta = JSON.parse(fs2.readFileSync(this.metaFilePath, "utf-8"));
|
|
660
713
|
meta.lastUpdated = timestamp;
|
|
661
714
|
} else {
|
|
662
715
|
meta = {
|
|
@@ -672,7 +725,7 @@ var KeepGoingWriter = class {
|
|
|
672
725
|
lastUpdated: timestamp
|
|
673
726
|
};
|
|
674
727
|
}
|
|
675
|
-
|
|
728
|
+
fs2.writeFileSync(this.metaFilePath, JSON.stringify(meta, null, 2), "utf-8");
|
|
676
729
|
}
|
|
677
730
|
// ---------------------------------------------------------------------------
|
|
678
731
|
// Multi-session API
|
|
@@ -680,8 +733,8 @@ var KeepGoingWriter = class {
|
|
|
680
733
|
/** Read all current tasks from current-tasks.json. Auto-prunes stale sessions. */
|
|
681
734
|
readCurrentTasks() {
|
|
682
735
|
try {
|
|
683
|
-
if (
|
|
684
|
-
const raw = JSON.parse(
|
|
736
|
+
if (fs2.existsSync(this.currentTasksFilePath)) {
|
|
737
|
+
const raw = JSON.parse(fs2.readFileSync(this.currentTasksFilePath, "utf-8"));
|
|
685
738
|
const tasks = Array.isArray(raw) ? raw : raw.tasks ?? [];
|
|
686
739
|
return this.pruneStale(tasks);
|
|
687
740
|
}
|
|
@@ -696,6 +749,8 @@ var KeepGoingWriter = class {
|
|
|
696
749
|
upsertSession(update) {
|
|
697
750
|
this.ensureDir();
|
|
698
751
|
this.upsertSessionCore(update);
|
|
752
|
+
const mainRoot = resolveStorageRoot(this.storagePath);
|
|
753
|
+
registerProject(mainRoot);
|
|
699
754
|
}
|
|
700
755
|
/** Core upsert logic: merges the update into current-tasks.json and returns the pruned task list. */
|
|
701
756
|
upsertSessionCore(update) {
|
|
@@ -734,8 +789,8 @@ var KeepGoingWriter = class {
|
|
|
734
789
|
// ---------------------------------------------------------------------------
|
|
735
790
|
readAllTasksRaw() {
|
|
736
791
|
try {
|
|
737
|
-
if (
|
|
738
|
-
const raw = JSON.parse(
|
|
792
|
+
if (fs2.existsSync(this.currentTasksFilePath)) {
|
|
793
|
+
const raw = JSON.parse(fs2.readFileSync(this.currentTasksFilePath, "utf-8"));
|
|
739
794
|
return Array.isArray(raw) ? [...raw] : [...raw.tasks ?? []];
|
|
740
795
|
}
|
|
741
796
|
} catch {
|
|
@@ -747,7 +802,7 @@ var KeepGoingWriter = class {
|
|
|
747
802
|
}
|
|
748
803
|
writeTasksFile(tasks) {
|
|
749
804
|
const data = { version: 1, tasks };
|
|
750
|
-
|
|
805
|
+
fs2.writeFileSync(this.currentTasksFilePath, JSON.stringify(data, null, 2), "utf-8");
|
|
751
806
|
}
|
|
752
807
|
};
|
|
753
808
|
function generateSessionId(context) {
|
|
@@ -926,35 +981,35 @@ function capitalize(s) {
|
|
|
926
981
|
}
|
|
927
982
|
|
|
928
983
|
// ../../packages/shared/src/decisionStorage.ts
|
|
929
|
-
import
|
|
930
|
-
import
|
|
984
|
+
import fs4 from "fs";
|
|
985
|
+
import path6 from "path";
|
|
931
986
|
|
|
932
987
|
// ../../packages/shared/src/license.ts
|
|
933
988
|
import crypto from "crypto";
|
|
934
|
-
import
|
|
935
|
-
import
|
|
936
|
-
import
|
|
989
|
+
import fs3 from "fs";
|
|
990
|
+
import os2 from "os";
|
|
991
|
+
import path5 from "path";
|
|
937
992
|
var LICENSE_FILE = "license.json";
|
|
938
993
|
var DEVICE_ID_FILE = "device-id";
|
|
939
994
|
function getGlobalLicenseDir() {
|
|
940
|
-
return
|
|
995
|
+
return path5.join(os2.homedir(), ".keepgoing");
|
|
941
996
|
}
|
|
942
997
|
function getGlobalLicensePath() {
|
|
943
|
-
return
|
|
998
|
+
return path5.join(getGlobalLicenseDir(), LICENSE_FILE);
|
|
944
999
|
}
|
|
945
1000
|
function getDeviceId() {
|
|
946
1001
|
const dir = getGlobalLicenseDir();
|
|
947
|
-
const filePath =
|
|
1002
|
+
const filePath = path5.join(dir, DEVICE_ID_FILE);
|
|
948
1003
|
try {
|
|
949
|
-
const existing =
|
|
1004
|
+
const existing = fs3.readFileSync(filePath, "utf-8").trim();
|
|
950
1005
|
if (existing) return existing;
|
|
951
1006
|
} catch {
|
|
952
1007
|
}
|
|
953
1008
|
const id = crypto.randomUUID();
|
|
954
|
-
if (!
|
|
955
|
-
|
|
1009
|
+
if (!fs3.existsSync(dir)) {
|
|
1010
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
956
1011
|
}
|
|
957
|
-
|
|
1012
|
+
fs3.writeFileSync(filePath, id, "utf-8");
|
|
958
1013
|
return id;
|
|
959
1014
|
}
|
|
960
1015
|
var DECISION_DETECTION_VARIANT_ID = 1361527;
|
|
@@ -988,10 +1043,10 @@ function readLicenseStore() {
|
|
|
988
1043
|
const licensePath = getGlobalLicensePath();
|
|
989
1044
|
let store;
|
|
990
1045
|
try {
|
|
991
|
-
if (!
|
|
1046
|
+
if (!fs3.existsSync(licensePath)) {
|
|
992
1047
|
store = { version: 2, licenses: [] };
|
|
993
1048
|
} else {
|
|
994
|
-
const raw =
|
|
1049
|
+
const raw = fs3.readFileSync(licensePath, "utf-8");
|
|
995
1050
|
const data = JSON.parse(raw);
|
|
996
1051
|
if (data?.version === 2 && Array.isArray(data.licenses)) {
|
|
997
1052
|
store = data;
|
|
@@ -1008,11 +1063,11 @@ function readLicenseStore() {
|
|
|
1008
1063
|
}
|
|
1009
1064
|
function writeLicenseStore(store) {
|
|
1010
1065
|
const dirPath = getGlobalLicenseDir();
|
|
1011
|
-
if (!
|
|
1012
|
-
|
|
1066
|
+
if (!fs3.existsSync(dirPath)) {
|
|
1067
|
+
fs3.mkdirSync(dirPath, { recursive: true });
|
|
1013
1068
|
}
|
|
1014
|
-
const licensePath =
|
|
1015
|
-
|
|
1069
|
+
const licensePath = path5.join(dirPath, LICENSE_FILE);
|
|
1070
|
+
fs3.writeFileSync(licensePath, JSON.stringify(store, null, 2), "utf-8");
|
|
1016
1071
|
_cachedStore = store;
|
|
1017
1072
|
_cacheTimestamp = Date.now();
|
|
1018
1073
|
}
|
|
@@ -1052,8 +1107,8 @@ var DefaultFeatureGate = class {
|
|
|
1052
1107
|
var currentGate = new DefaultFeatureGate();
|
|
1053
1108
|
|
|
1054
1109
|
// ../../packages/shared/src/reader.ts
|
|
1055
|
-
import
|
|
1056
|
-
import
|
|
1110
|
+
import fs5 from "fs";
|
|
1111
|
+
import path7 from "path";
|
|
1057
1112
|
var STORAGE_DIR2 = ".keepgoing";
|
|
1058
1113
|
var META_FILE2 = "meta.json";
|
|
1059
1114
|
var SESSIONS_FILE2 = "sessions.json";
|
|
@@ -1075,16 +1130,16 @@ var KeepGoingReader = class {
|
|
|
1075
1130
|
this.workspacePath = workspacePath;
|
|
1076
1131
|
const mainRoot = resolveStorageRoot(workspacePath);
|
|
1077
1132
|
this._isWorktree = mainRoot !== workspacePath;
|
|
1078
|
-
this.storagePath =
|
|
1079
|
-
this.metaFilePath =
|
|
1080
|
-
this.sessionsFilePath =
|
|
1081
|
-
this.decisionsFilePath =
|
|
1082
|
-
this.stateFilePath =
|
|
1083
|
-
this.currentTasksFilePath =
|
|
1133
|
+
this.storagePath = path7.join(mainRoot, STORAGE_DIR2);
|
|
1134
|
+
this.metaFilePath = path7.join(this.storagePath, META_FILE2);
|
|
1135
|
+
this.sessionsFilePath = path7.join(this.storagePath, SESSIONS_FILE2);
|
|
1136
|
+
this.decisionsFilePath = path7.join(this.storagePath, DECISIONS_FILE);
|
|
1137
|
+
this.stateFilePath = path7.join(this.storagePath, STATE_FILE2);
|
|
1138
|
+
this.currentTasksFilePath = path7.join(this.storagePath, CURRENT_TASKS_FILE2);
|
|
1084
1139
|
}
|
|
1085
1140
|
/** Check if .keepgoing/ directory exists. */
|
|
1086
1141
|
exists() {
|
|
1087
|
-
return
|
|
1142
|
+
return fs5.existsSync(this.storagePath);
|
|
1088
1143
|
}
|
|
1089
1144
|
/** Read state.json, returns undefined if missing or corrupt. */
|
|
1090
1145
|
getState() {
|
|
@@ -1322,10 +1377,10 @@ var KeepGoingReader = class {
|
|
|
1322
1377
|
}
|
|
1323
1378
|
readJsonFile(filePath) {
|
|
1324
1379
|
try {
|
|
1325
|
-
if (!
|
|
1380
|
+
if (!fs5.existsSync(filePath)) {
|
|
1326
1381
|
return void 0;
|
|
1327
1382
|
}
|
|
1328
|
-
const raw =
|
|
1383
|
+
const raw = fs5.readFileSync(filePath, "utf-8");
|
|
1329
1384
|
return JSON.parse(raw);
|
|
1330
1385
|
} catch {
|
|
1331
1386
|
return void 0;
|
|
@@ -1334,9 +1389,9 @@ var KeepGoingReader = class {
|
|
|
1334
1389
|
};
|
|
1335
1390
|
|
|
1336
1391
|
// ../../packages/shared/src/setup.ts
|
|
1337
|
-
import
|
|
1338
|
-
import
|
|
1339
|
-
import
|
|
1392
|
+
import fs6 from "fs";
|
|
1393
|
+
import os3 from "os";
|
|
1394
|
+
import path8 from "path";
|
|
1340
1395
|
var KEEPGOING_MARKER = "@keepgoingdev/mcp-server";
|
|
1341
1396
|
var SESSION_START_HOOK = {
|
|
1342
1397
|
matcher: "",
|
|
@@ -1365,36 +1420,55 @@ var POST_TOOL_USE_HOOK = {
|
|
|
1365
1420
|
}
|
|
1366
1421
|
]
|
|
1367
1422
|
};
|
|
1368
|
-
var
|
|
1423
|
+
var SESSION_END_HOOK = {
|
|
1424
|
+
matcher: "",
|
|
1425
|
+
hooks: [
|
|
1426
|
+
{
|
|
1427
|
+
type: "command",
|
|
1428
|
+
command: "npx -y @keepgoingdev/mcp-server --save-checkpoint"
|
|
1429
|
+
}
|
|
1430
|
+
]
|
|
1431
|
+
};
|
|
1432
|
+
var KEEPGOING_RULES_VERSION = 1;
|
|
1433
|
+
var KEEPGOING_RULES_CONTENT = `<!-- @keepgoingdev/mcp-server v${KEEPGOING_RULES_VERSION} -->
|
|
1369
1434
|
## KeepGoing
|
|
1370
1435
|
|
|
1371
1436
|
After completing a task or meaningful piece of work, call the \`save_checkpoint\` MCP tool with:
|
|
1372
|
-
- \`summary\`: What
|
|
1373
|
-
- \`nextStep\`: What
|
|
1437
|
+
- \`summary\`: 1-2 sentences. What changed and why \u2014 no file paths, no implementation details (those are captured from git).
|
|
1438
|
+
- \`nextStep\`: What to do next
|
|
1374
1439
|
- \`blocker\`: Any blocker (if applicable)
|
|
1375
1440
|
`;
|
|
1441
|
+
function getRulesFileVersion(content) {
|
|
1442
|
+
const match = content.match(/<!-- @keepgoingdev\/mcp-server v(\d+) -->/);
|
|
1443
|
+
return match ? parseInt(match[1], 10) : null;
|
|
1444
|
+
}
|
|
1376
1445
|
var STATUSLINE_CMD = "npx -y @keepgoingdev/mcp-server --statusline";
|
|
1446
|
+
function detectClaudeDir() {
|
|
1447
|
+
return process.env.CLAUDE_CONFIG_DIR || path8.join(os3.homedir(), ".claude");
|
|
1448
|
+
}
|
|
1377
1449
|
function hasKeepGoingHook(hookEntries) {
|
|
1378
1450
|
return hookEntries.some(
|
|
1379
1451
|
(entry) => entry?.hooks?.some((h) => typeof h?.command === "string" && h.command.includes(KEEPGOING_MARKER))
|
|
1380
1452
|
);
|
|
1381
1453
|
}
|
|
1382
|
-
function resolveScopePaths(scope, workspacePath) {
|
|
1454
|
+
function resolveScopePaths(scope, workspacePath, overrideClaudeDir) {
|
|
1383
1455
|
if (scope === "user") {
|
|
1384
|
-
const claudeDir2 =
|
|
1456
|
+
const claudeDir2 = overrideClaudeDir || detectClaudeDir();
|
|
1385
1457
|
return {
|
|
1386
1458
|
claudeDir: claudeDir2,
|
|
1387
|
-
settingsPath:
|
|
1388
|
-
claudeMdPath:
|
|
1459
|
+
settingsPath: path8.join(claudeDir2, "settings.json"),
|
|
1460
|
+
claudeMdPath: path8.join(claudeDir2, "CLAUDE.md"),
|
|
1461
|
+
rulesPath: path8.join(claudeDir2, "rules", "keepgoing.md")
|
|
1389
1462
|
};
|
|
1390
1463
|
}
|
|
1391
|
-
const claudeDir =
|
|
1392
|
-
const dotClaudeMdPath =
|
|
1393
|
-
const rootClaudeMdPath =
|
|
1464
|
+
const claudeDir = path8.join(workspacePath, ".claude");
|
|
1465
|
+
const dotClaudeMdPath = path8.join(workspacePath, ".claude", "CLAUDE.md");
|
|
1466
|
+
const rootClaudeMdPath = path8.join(workspacePath, "CLAUDE.md");
|
|
1394
1467
|
return {
|
|
1395
1468
|
claudeDir,
|
|
1396
|
-
settingsPath:
|
|
1397
|
-
claudeMdPath:
|
|
1469
|
+
settingsPath: path8.join(claudeDir, "settings.json"),
|
|
1470
|
+
claudeMdPath: fs6.existsSync(dotClaudeMdPath) ? dotClaudeMdPath : rootClaudeMdPath,
|
|
1471
|
+
rulesPath: path8.join(workspacePath, ".claude", "rules", "keepgoing.md")
|
|
1398
1472
|
};
|
|
1399
1473
|
}
|
|
1400
1474
|
function writeHooksToSettings(settings) {
|
|
@@ -1423,15 +1497,22 @@ function writeHooksToSettings(settings) {
|
|
|
1423
1497
|
settings.hooks.PostToolUse.push(POST_TOOL_USE_HOOK);
|
|
1424
1498
|
changed = true;
|
|
1425
1499
|
}
|
|
1500
|
+
if (!Array.isArray(settings.hooks.SessionEnd)) {
|
|
1501
|
+
settings.hooks.SessionEnd = [];
|
|
1502
|
+
}
|
|
1503
|
+
if (!hasKeepGoingHook(settings.hooks.SessionEnd)) {
|
|
1504
|
+
settings.hooks.SessionEnd.push(SESSION_END_HOOK);
|
|
1505
|
+
changed = true;
|
|
1506
|
+
}
|
|
1426
1507
|
return changed;
|
|
1427
1508
|
}
|
|
1428
1509
|
function checkHookConflict(scope, workspacePath) {
|
|
1429
1510
|
const otherPaths = resolveScopePaths(scope === "user" ? "project" : "user", workspacePath);
|
|
1430
|
-
if (!
|
|
1511
|
+
if (!fs6.existsSync(otherPaths.settingsPath)) {
|
|
1431
1512
|
return null;
|
|
1432
1513
|
}
|
|
1433
1514
|
try {
|
|
1434
|
-
const otherSettings = JSON.parse(
|
|
1515
|
+
const otherSettings = JSON.parse(fs6.readFileSync(otherPaths.settingsPath, "utf-8"));
|
|
1435
1516
|
const hooks = otherSettings?.hooks;
|
|
1436
1517
|
if (!hooks) return null;
|
|
1437
1518
|
const hasConflict = Array.isArray(hooks.SessionStart) && hasKeepGoingHook(hooks.SessionStart) || Array.isArray(hooks.Stop) && hasKeepGoingHook(hooks.Stop);
|
|
@@ -1450,15 +1531,20 @@ function setupProject(options) {
|
|
|
1450
1531
|
scope = "project",
|
|
1451
1532
|
sessionHooks = true,
|
|
1452
1533
|
claudeMd = true,
|
|
1534
|
+
claudeDir: claudeDirOverride,
|
|
1453
1535
|
statusline
|
|
1454
1536
|
} = options;
|
|
1455
1537
|
const messages = [];
|
|
1456
1538
|
let changed = false;
|
|
1457
|
-
const { claudeDir, settingsPath, claudeMdPath } = resolveScopePaths(
|
|
1458
|
-
|
|
1539
|
+
const { claudeDir, settingsPath, claudeMdPath, rulesPath } = resolveScopePaths(
|
|
1540
|
+
scope,
|
|
1541
|
+
workspacePath,
|
|
1542
|
+
claudeDirOverride
|
|
1543
|
+
);
|
|
1544
|
+
const scopeLabel = scope === "user" ? path8.join("~", path8.relative(os3.homedir(), claudeDir), "settings.json").replace(/\\/g, "/") : ".claude/settings.json";
|
|
1459
1545
|
let settings = {};
|
|
1460
|
-
if (
|
|
1461
|
-
settings = JSON.parse(
|
|
1546
|
+
if (fs6.existsSync(settingsPath)) {
|
|
1547
|
+
settings = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
|
|
1462
1548
|
}
|
|
1463
1549
|
let settingsChanged = false;
|
|
1464
1550
|
if (sessionHooks) {
|
|
@@ -1489,29 +1575,45 @@ function setupProject(options) {
|
|
|
1489
1575
|
statusline?.cleanup?.();
|
|
1490
1576
|
}
|
|
1491
1577
|
if (settingsChanged) {
|
|
1492
|
-
if (!
|
|
1493
|
-
|
|
1578
|
+
if (!fs6.existsSync(claudeDir)) {
|
|
1579
|
+
fs6.mkdirSync(claudeDir, { recursive: true });
|
|
1494
1580
|
}
|
|
1495
|
-
|
|
1581
|
+
fs6.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1496
1582
|
changed = true;
|
|
1497
1583
|
}
|
|
1498
1584
|
if (claudeMd) {
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1585
|
+
const rulesDir = path8.dirname(rulesPath);
|
|
1586
|
+
const rulesLabel = scope === "user" ? path8.join(path8.relative(os3.homedir(), path8.dirname(rulesPath)), "keepgoing.md").replace(/\\/g, "/") : ".claude/rules/keepgoing.md";
|
|
1587
|
+
if (fs6.existsSync(rulesPath)) {
|
|
1588
|
+
const existing = fs6.readFileSync(rulesPath, "utf-8");
|
|
1589
|
+
const existingVersion = getRulesFileVersion(existing);
|
|
1590
|
+
if (existingVersion === null) {
|
|
1591
|
+
messages.push(`Rules file: Custom file found at ${rulesLabel}, skipping`);
|
|
1592
|
+
} else if (existingVersion >= KEEPGOING_RULES_VERSION) {
|
|
1593
|
+
messages.push(`Rules file: Already up to date (v${existingVersion}), skipped`);
|
|
1594
|
+
} else {
|
|
1595
|
+
if (!fs6.existsSync(rulesDir)) {
|
|
1596
|
+
fs6.mkdirSync(rulesDir, { recursive: true });
|
|
1597
|
+
}
|
|
1598
|
+
fs6.writeFileSync(rulesPath, KEEPGOING_RULES_CONTENT);
|
|
1599
|
+
changed = true;
|
|
1600
|
+
messages.push(`Rules file: Updated v${existingVersion} \u2192 v${KEEPGOING_RULES_VERSION} at ${rulesLabel}`);
|
|
1601
|
+
}
|
|
1506
1602
|
} else {
|
|
1507
|
-
const
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
fs5.mkdirSync(mdDir, { recursive: true });
|
|
1603
|
+
const existingClaudeMd = fs6.existsSync(claudeMdPath) ? fs6.readFileSync(claudeMdPath, "utf-8") : "";
|
|
1604
|
+
if (!fs6.existsSync(rulesDir)) {
|
|
1605
|
+
fs6.mkdirSync(rulesDir, { recursive: true });
|
|
1511
1606
|
}
|
|
1512
|
-
|
|
1607
|
+
fs6.writeFileSync(rulesPath, KEEPGOING_RULES_CONTENT);
|
|
1513
1608
|
changed = true;
|
|
1514
|
-
|
|
1609
|
+
if (existingClaudeMd.includes("## KeepGoing")) {
|
|
1610
|
+
const mdLabel = scope === "user" ? "~/.claude/CLAUDE.md" : "CLAUDE.md";
|
|
1611
|
+
messages.push(
|
|
1612
|
+
`Rules file: Created ${rulesLabel} (you can now remove the ## KeepGoing section from ${mdLabel})`
|
|
1613
|
+
);
|
|
1614
|
+
} else {
|
|
1615
|
+
messages.push(`Rules file: Created ${rulesLabel}`);
|
|
1616
|
+
}
|
|
1515
1617
|
}
|
|
1516
1618
|
}
|
|
1517
1619
|
return { messages, changed };
|
|
@@ -1919,14 +2021,14 @@ function renderEnrichedBriefingQuiet(briefing) {
|
|
|
1919
2021
|
// src/updateCheck.ts
|
|
1920
2022
|
import { spawn } from "child_process";
|
|
1921
2023
|
import { readFileSync, existsSync } from "fs";
|
|
1922
|
-
import
|
|
1923
|
-
import
|
|
1924
|
-
var CLI_VERSION = "1.
|
|
2024
|
+
import path9 from "path";
|
|
2025
|
+
import os4 from "os";
|
|
2026
|
+
var CLI_VERSION = "1.2.0";
|
|
1925
2027
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@keepgoingdev/cli/latest";
|
|
1926
2028
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
1927
2029
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1928
|
-
var CACHE_DIR =
|
|
1929
|
-
var CACHE_PATH =
|
|
2030
|
+
var CACHE_DIR = path9.join(os4.homedir(), ".keepgoing");
|
|
2031
|
+
var CACHE_PATH = path9.join(CACHE_DIR, "update-check.json");
|
|
1930
2032
|
function isNewerVersion(current, latest) {
|
|
1931
2033
|
const cur = current.split(".").map(Number);
|
|
1932
2034
|
const lat = latest.split(".").map(Number);
|
|
@@ -2041,7 +2143,7 @@ async function statusCommand(opts) {
|
|
|
2041
2143
|
}
|
|
2042
2144
|
|
|
2043
2145
|
// src/commands/save.ts
|
|
2044
|
-
import
|
|
2146
|
+
import path10 from "path";
|
|
2045
2147
|
async function saveCommand(opts) {
|
|
2046
2148
|
const { cwd, message, nextStepOverride, json, quiet, force } = opts;
|
|
2047
2149
|
const isManual = !!message;
|
|
@@ -2070,9 +2172,9 @@ async function saveCommand(opts) {
|
|
|
2070
2172
|
sessionStartTime: lastSession?.timestamp ?? now,
|
|
2071
2173
|
lastActivityTime: now
|
|
2072
2174
|
});
|
|
2073
|
-
const summary = message ?? buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map((f) =>
|
|
2175
|
+
const summary = message ?? buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map((f) => path10.basename(f)).join(", ")}`;
|
|
2074
2176
|
const nextStep = nextStepOverride ?? buildSmartNextStep(events);
|
|
2075
|
-
const projectName =
|
|
2177
|
+
const projectName = path10.basename(resolveStorageRoot(cwd));
|
|
2076
2178
|
const sessionId = generateSessionId({
|
|
2077
2179
|
workspaceRoot: cwd,
|
|
2078
2180
|
branch: gitBranch ?? void 0,
|
|
@@ -2105,12 +2207,25 @@ async function saveCommand(opts) {
|
|
|
2105
2207
|
}
|
|
2106
2208
|
|
|
2107
2209
|
// src/commands/hook.ts
|
|
2108
|
-
import
|
|
2109
|
-
import
|
|
2110
|
-
import
|
|
2210
|
+
import fs7 from "fs";
|
|
2211
|
+
import path11 from "path";
|
|
2212
|
+
import os5 from "os";
|
|
2111
2213
|
import { execSync } from "child_process";
|
|
2112
2214
|
var HOOK_MARKER_START = "# keepgoing-hook-start";
|
|
2113
2215
|
var HOOK_MARKER_END = "# keepgoing-hook-end";
|
|
2216
|
+
var POST_COMMIT_MARKER_START = "# keepgoing-post-commit-start";
|
|
2217
|
+
var POST_COMMIT_MARKER_END = "# keepgoing-post-commit-end";
|
|
2218
|
+
var KEEPGOING_HOOKS_DIR = path11.join(os5.homedir(), ".keepgoing", "hooks");
|
|
2219
|
+
var POST_COMMIT_HOOK_PATH = path11.join(KEEPGOING_HOOKS_DIR, "post-commit");
|
|
2220
|
+
var KEEPGOING_MANAGED_MARKER = path11.join(KEEPGOING_HOOKS_DIR, ".keepgoing-managed");
|
|
2221
|
+
var POST_COMMIT_HOOK = `#!/bin/sh
|
|
2222
|
+
${POST_COMMIT_MARKER_START}
|
|
2223
|
+
# Runs after every git commit. Detects high-signal decisions.
|
|
2224
|
+
if command -v keepgoing-mcp-server >/dev/null 2>&1; then
|
|
2225
|
+
keepgoing-mcp-server --detect-decisions 2>/dev/null &
|
|
2226
|
+
fi
|
|
2227
|
+
${POST_COMMIT_MARKER_END}
|
|
2228
|
+
`;
|
|
2114
2229
|
var ZSH_HOOK = `${HOOK_MARKER_START}
|
|
2115
2230
|
# KeepGoing shell hook \u2014 auto-injected by 'keepgoing hook install'
|
|
2116
2231
|
if command -v keepgoing >/dev/null 2>&1; then
|
|
@@ -2143,7 +2258,7 @@ if command -v keepgoing >/dev/null 2>&1
|
|
|
2143
2258
|
end
|
|
2144
2259
|
${HOOK_MARKER_END}`;
|
|
2145
2260
|
function detectShellRcFile(shellOverride) {
|
|
2146
|
-
const home =
|
|
2261
|
+
const home = os5.homedir();
|
|
2147
2262
|
let shell;
|
|
2148
2263
|
if (shellOverride) {
|
|
2149
2264
|
shell = shellOverride.toLowerCase();
|
|
@@ -2166,17 +2281,155 @@ function detectShellRcFile(shellOverride) {
|
|
|
2166
2281
|
}
|
|
2167
2282
|
}
|
|
2168
2283
|
if (shell === "zsh") {
|
|
2169
|
-
return { shell: "zsh", rcFile:
|
|
2284
|
+
return { shell: "zsh", rcFile: path11.join(home, ".zshrc") };
|
|
2170
2285
|
}
|
|
2171
2286
|
if (shell === "bash") {
|
|
2172
|
-
return { shell: "bash", rcFile:
|
|
2287
|
+
return { shell: "bash", rcFile: path11.join(home, ".bashrc") };
|
|
2173
2288
|
}
|
|
2174
2289
|
if (shell === "fish") {
|
|
2175
|
-
const xdgConfig = process.env["XDG_CONFIG_HOME"] ||
|
|
2176
|
-
return { shell: "fish", rcFile:
|
|
2290
|
+
const xdgConfig = process.env["XDG_CONFIG_HOME"] || path11.join(home, ".config");
|
|
2291
|
+
return { shell: "fish", rcFile: path11.join(xdgConfig, "fish", "config.fish") };
|
|
2177
2292
|
}
|
|
2178
2293
|
return void 0;
|
|
2179
2294
|
}
|
|
2295
|
+
function resolveGlobalGitignorePath() {
|
|
2296
|
+
try {
|
|
2297
|
+
const configured = execSync("git config --global core.excludesfile", {
|
|
2298
|
+
encoding: "utf-8",
|
|
2299
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2300
|
+
}).trim();
|
|
2301
|
+
if (configured) {
|
|
2302
|
+
return configured.startsWith("~") ? path11.join(os5.homedir(), configured.slice(1)) : configured;
|
|
2303
|
+
}
|
|
2304
|
+
} catch {
|
|
2305
|
+
}
|
|
2306
|
+
return path11.join(os5.homedir(), ".gitignore_global");
|
|
2307
|
+
}
|
|
2308
|
+
function installGlobalGitignore() {
|
|
2309
|
+
const ignorePath = resolveGlobalGitignorePath();
|
|
2310
|
+
let existing = "";
|
|
2311
|
+
try {
|
|
2312
|
+
existing = fs7.readFileSync(ignorePath, "utf-8");
|
|
2313
|
+
} catch {
|
|
2314
|
+
}
|
|
2315
|
+
if (existing.split("\n").some((line) => line.trim() === ".keepgoing")) {
|
|
2316
|
+
console.log(`Global gitignore: .keepgoing already present in ${ignorePath}`);
|
|
2317
|
+
} else {
|
|
2318
|
+
const suffix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
2319
|
+
fs7.appendFileSync(ignorePath, `${suffix}.keepgoing
|
|
2320
|
+
`, "utf-8");
|
|
2321
|
+
console.log(`Global gitignore: .keepgoing added to ${ignorePath}`);
|
|
2322
|
+
}
|
|
2323
|
+
try {
|
|
2324
|
+
execSync("git config --global core.excludesfile", {
|
|
2325
|
+
encoding: "utf-8",
|
|
2326
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2327
|
+
}).trim();
|
|
2328
|
+
} catch {
|
|
2329
|
+
execSync(`git config --global core.excludesfile "${ignorePath}"`, {
|
|
2330
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2331
|
+
});
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
function uninstallGlobalGitignore() {
|
|
2335
|
+
const ignorePath = resolveGlobalGitignorePath();
|
|
2336
|
+
let existing = "";
|
|
2337
|
+
try {
|
|
2338
|
+
existing = fs7.readFileSync(ignorePath, "utf-8");
|
|
2339
|
+
} catch {
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
const lines = existing.split("\n");
|
|
2343
|
+
const filtered = lines.filter((line) => line.trim() !== ".keepgoing");
|
|
2344
|
+
if (filtered.length !== lines.length) {
|
|
2345
|
+
fs7.writeFileSync(ignorePath, filtered.join("\n"), "utf-8");
|
|
2346
|
+
console.log(`Global gitignore: .keepgoing removed from ${ignorePath}`);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
function installPostCommitHook() {
|
|
2350
|
+
fs7.mkdirSync(KEEPGOING_HOOKS_DIR, { recursive: true });
|
|
2351
|
+
if (fs7.existsSync(POST_COMMIT_HOOK_PATH)) {
|
|
2352
|
+
const existing = fs7.readFileSync(POST_COMMIT_HOOK_PATH, "utf-8");
|
|
2353
|
+
if (existing.includes(POST_COMMIT_MARKER_START)) {
|
|
2354
|
+
console.log(`Git post-commit hook: already installed at ${POST_COMMIT_HOOK_PATH}`);
|
|
2355
|
+
} else {
|
|
2356
|
+
const suffix = existing.endsWith("\n") ? "" : "\n";
|
|
2357
|
+
const block = `${suffix}
|
|
2358
|
+
${POST_COMMIT_MARKER_START}
|
|
2359
|
+
# Runs after every git commit. Detects high-signal decisions.
|
|
2360
|
+
if command -v keepgoing-mcp-server >/dev/null 2>&1; then
|
|
2361
|
+
keepgoing-mcp-server --detect-decisions 2>/dev/null &
|
|
2362
|
+
fi
|
|
2363
|
+
${POST_COMMIT_MARKER_END}
|
|
2364
|
+
`;
|
|
2365
|
+
fs7.appendFileSync(POST_COMMIT_HOOK_PATH, block, "utf-8");
|
|
2366
|
+
console.log(`Git post-commit hook: appended to existing ${POST_COMMIT_HOOK_PATH}`);
|
|
2367
|
+
}
|
|
2368
|
+
} else {
|
|
2369
|
+
fs7.writeFileSync(POST_COMMIT_HOOK_PATH, POST_COMMIT_HOOK, { mode: 493 });
|
|
2370
|
+
console.log(`Git post-commit hook: installed at ${POST_COMMIT_HOOK_PATH}`);
|
|
2371
|
+
}
|
|
2372
|
+
fs7.chmodSync(POST_COMMIT_HOOK_PATH, 493);
|
|
2373
|
+
let currentHooksPath;
|
|
2374
|
+
try {
|
|
2375
|
+
currentHooksPath = execSync("git config --global core.hooksPath", {
|
|
2376
|
+
encoding: "utf-8",
|
|
2377
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2378
|
+
}).trim();
|
|
2379
|
+
} catch {
|
|
2380
|
+
}
|
|
2381
|
+
if (currentHooksPath === KEEPGOING_HOOKS_DIR) {
|
|
2382
|
+
console.log(`Git hooks path: already set to ${KEEPGOING_HOOKS_DIR}`);
|
|
2383
|
+
} else if (currentHooksPath) {
|
|
2384
|
+
console.log(`Git hooks path: set to ${currentHooksPath}`);
|
|
2385
|
+
console.log(` To use KeepGoing's post-commit hook, add the following to ${currentHooksPath}/post-commit:`);
|
|
2386
|
+
console.log(` if command -v keepgoing-mcp-server >/dev/null 2>&1; then`);
|
|
2387
|
+
console.log(` keepgoing-mcp-server --detect-decisions 2>/dev/null &`);
|
|
2388
|
+
console.log(` fi`);
|
|
2389
|
+
} else {
|
|
2390
|
+
console.log(`Git post-commit hook: saved to ${POST_COMMIT_HOOK_PATH}`);
|
|
2391
|
+
console.log(` To activate it globally, run:`);
|
|
2392
|
+
console.log(` git config --global core.hooksPath "${KEEPGOING_HOOKS_DIR}"`);
|
|
2393
|
+
console.log(` Note: this overrides per-repo .git/hooks/ for all repositories.`);
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
function uninstallPostCommitHook() {
|
|
2397
|
+
if (fs7.existsSync(POST_COMMIT_HOOK_PATH)) {
|
|
2398
|
+
const existing = fs7.readFileSync(POST_COMMIT_HOOK_PATH, "utf-8");
|
|
2399
|
+
if (existing.includes(POST_COMMIT_MARKER_START)) {
|
|
2400
|
+
const pattern = new RegExp(
|
|
2401
|
+
`
|
|
2402
|
+
?${POST_COMMIT_MARKER_START}[\\s\\S]*?${POST_COMMIT_MARKER_END}
|
|
2403
|
+
?`,
|
|
2404
|
+
"g"
|
|
2405
|
+
);
|
|
2406
|
+
const updated = existing.replace(pattern, "").trim();
|
|
2407
|
+
if (!updated || updated === "#!/bin/sh") {
|
|
2408
|
+
fs7.unlinkSync(POST_COMMIT_HOOK_PATH);
|
|
2409
|
+
console.log(`Git post-commit hook: removed ${POST_COMMIT_HOOK_PATH}`);
|
|
2410
|
+
} else {
|
|
2411
|
+
fs7.writeFileSync(POST_COMMIT_HOOK_PATH, updated + "\n", "utf-8");
|
|
2412
|
+
console.log(`Git post-commit hook: KeepGoing block removed from ${POST_COMMIT_HOOK_PATH}`);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
try {
|
|
2417
|
+
const currentHooksPath = execSync("git config --global core.hooksPath", {
|
|
2418
|
+
encoding: "utf-8",
|
|
2419
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2420
|
+
}).trim();
|
|
2421
|
+
if (currentHooksPath === KEEPGOING_HOOKS_DIR) {
|
|
2422
|
+
execSync("git config --global --unset core.hooksPath", {
|
|
2423
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2424
|
+
});
|
|
2425
|
+
console.log("Git hooks path: global core.hooksPath unset");
|
|
2426
|
+
}
|
|
2427
|
+
} catch {
|
|
2428
|
+
}
|
|
2429
|
+
if (fs7.existsSync(KEEPGOING_MANAGED_MARKER)) {
|
|
2430
|
+
fs7.unlinkSync(KEEPGOING_MANAGED_MARKER);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2180
2433
|
function hookInstallCommand(shellOverride) {
|
|
2181
2434
|
const detected = detectShellRcFile(shellOverride);
|
|
2182
2435
|
if (!detected) {
|
|
@@ -2189,18 +2442,23 @@ function hookInstallCommand(shellOverride) {
|
|
|
2189
2442
|
const hookBlock = shell === "zsh" ? ZSH_HOOK : shell === "fish" ? FISH_HOOK : BASH_HOOK;
|
|
2190
2443
|
let existing = "";
|
|
2191
2444
|
try {
|
|
2192
|
-
existing =
|
|
2445
|
+
existing = fs7.readFileSync(rcFile, "utf-8");
|
|
2193
2446
|
} catch {
|
|
2194
2447
|
}
|
|
2195
2448
|
if (existing.includes(HOOK_MARKER_START)) {
|
|
2196
2449
|
console.log(`KeepGoing hook is already installed in ${rcFile}.`);
|
|
2450
|
+
installGlobalGitignore();
|
|
2451
|
+
installPostCommitHook();
|
|
2197
2452
|
return;
|
|
2198
2453
|
}
|
|
2199
|
-
|
|
2454
|
+
fs7.appendFileSync(rcFile, `
|
|
2200
2455
|
${hookBlock}
|
|
2201
2456
|
`, "utf-8");
|
|
2202
2457
|
console.log(`KeepGoing hook installed in ${rcFile}.`);
|
|
2203
|
-
|
|
2458
|
+
installGlobalGitignore();
|
|
2459
|
+
installPostCommitHook();
|
|
2460
|
+
console.log(`
|
|
2461
|
+
Reload your shell config to activate it:
|
|
2204
2462
|
`);
|
|
2205
2463
|
console.log(` source ${rcFile}`);
|
|
2206
2464
|
}
|
|
@@ -2215,7 +2473,7 @@ function hookUninstallCommand(shellOverride) {
|
|
|
2215
2473
|
const { rcFile } = detected;
|
|
2216
2474
|
let existing = "";
|
|
2217
2475
|
try {
|
|
2218
|
-
existing =
|
|
2476
|
+
existing = fs7.readFileSync(rcFile, "utf-8");
|
|
2219
2477
|
} catch {
|
|
2220
2478
|
console.log(`${rcFile} not found \u2014 nothing to remove.`);
|
|
2221
2479
|
return;
|
|
@@ -2231,9 +2489,12 @@ function hookUninstallCommand(shellOverride) {
|
|
|
2231
2489
|
"g"
|
|
2232
2490
|
);
|
|
2233
2491
|
const updated = existing.replace(pattern, "").replace(/\n{3,}/g, "\n\n");
|
|
2234
|
-
|
|
2492
|
+
fs7.writeFileSync(rcFile, updated, "utf-8");
|
|
2235
2493
|
console.log(`KeepGoing hook removed from ${rcFile}.`);
|
|
2236
|
-
|
|
2494
|
+
uninstallGlobalGitignore();
|
|
2495
|
+
uninstallPostCommitHook();
|
|
2496
|
+
console.log(`
|
|
2497
|
+
Reload your shell config to deactivate it:
|
|
2237
2498
|
`);
|
|
2238
2499
|
console.log(` source ${rcFile}`);
|
|
2239
2500
|
}
|
|
@@ -3216,7 +3477,7 @@ async function main() {
|
|
|
3216
3477
|
}
|
|
3217
3478
|
break;
|
|
3218
3479
|
case "version":
|
|
3219
|
-
console.log(`keepgoing v${"1.
|
|
3480
|
+
console.log(`keepgoing v${"1.2.0"}`);
|
|
3220
3481
|
break;
|
|
3221
3482
|
case "activate":
|
|
3222
3483
|
await activateCommand({ licenseKey: subcommand });
|