@keepgoingdev/cli 1.0.0 → 1.1.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.
Files changed (2) hide show
  1. package/dist/index.js +526 -80
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -159,6 +159,62 @@ function getTouchedFiles(workspacePath) {
159
159
  }
160
160
  }
161
161
 
162
+ // ../../packages/shared/src/briefingTier.ts
163
+ var MODEL_TIERS = {
164
+ // Standard: small/fast models (compact is only reachable via explicit tier or context window)
165
+ "claude-3-haiku": "standard",
166
+ "claude-3.5-haiku": "standard",
167
+ "claude-haiku-4-5": "standard",
168
+ "gpt-4o-mini": "standard",
169
+ "gemini-flash": "standard",
170
+ // Detailed: mid-tier models
171
+ "claude-3.5-sonnet": "detailed",
172
+ "claude-sonnet-4": "detailed",
173
+ "claude-sonnet-4-5": "detailed",
174
+ "claude-sonnet-4-6": "detailed",
175
+ "gpt-4o": "detailed",
176
+ "gemini-pro": "detailed",
177
+ // Full: large context models
178
+ "claude-opus-4": "full",
179
+ "claude-opus-4-5": "full",
180
+ "claude-opus-4-6": "full",
181
+ "o1": "full",
182
+ "o3": "full",
183
+ "gemini-ultra": "full"
184
+ };
185
+ function resolveTier(opts) {
186
+ if (opts?.tier) {
187
+ return opts.tier;
188
+ }
189
+ if (opts?.model) {
190
+ const fromModel = tierFromModelName(opts.model);
191
+ if (fromModel) return fromModel;
192
+ }
193
+ if (opts?.contextWindow !== void 0) {
194
+ return tierFromContextWindow(opts.contextWindow);
195
+ }
196
+ return "standard";
197
+ }
198
+ function tierFromModelName(model) {
199
+ const normalized = model.toLowerCase().trim();
200
+ if (MODEL_TIERS[normalized]) {
201
+ return MODEL_TIERS[normalized];
202
+ }
203
+ const entries = Object.entries(MODEL_TIERS).sort((a, b) => b[0].length - a[0].length);
204
+ for (const [key, tier] of entries) {
205
+ if (normalized.startsWith(key)) {
206
+ return tier;
207
+ }
208
+ }
209
+ return void 0;
210
+ }
211
+ function tierFromContextWindow(tokens) {
212
+ if (tokens < 16e3) return "compact";
213
+ if (tokens < 64e3) return "standard";
214
+ if (tokens < 2e5) return "detailed";
215
+ return "full";
216
+ }
217
+
162
218
  // ../../packages/shared/src/reentry.ts
163
219
  var RECENT_SESSION_COUNT = 5;
164
220
  function generateBriefing(lastSession, recentSessions, projectState, gitBranch, recentCommitMessages) {
@@ -184,6 +240,61 @@ function generateBriefing(lastSession, recentSessions, projectState, gitBranch,
184
240
  function getRecentSessions(allSessions, count = RECENT_SESSION_COUNT) {
185
241
  return allSessions.slice(-count).reverse();
186
242
  }
243
+ function generateEnrichedBriefing(opts) {
244
+ const tier = resolveTier({
245
+ tier: opts.tier,
246
+ model: opts.model,
247
+ contextWindow: opts.contextWindow
248
+ });
249
+ const core = generateBriefing(
250
+ opts.lastSession,
251
+ opts.recentSessions,
252
+ opts.projectState,
253
+ opts.gitBranch,
254
+ opts.recentCommits
255
+ );
256
+ if (!core) return void 0;
257
+ const enriched = { tier, core };
258
+ if (tier === "compact") {
259
+ return enriched;
260
+ }
261
+ enriched.blocker = opts.lastSession?.blocker;
262
+ enriched.gitBranch = opts.gitBranch;
263
+ enriched.isWorktree = opts.isWorktree;
264
+ if (tier === "standard") {
265
+ return enriched;
266
+ }
267
+ if (opts.decisions && opts.decisions.length > 0) {
268
+ enriched.decisions = opts.decisions.slice(0, tier === "detailed" ? 3 : 10);
269
+ }
270
+ if (opts.allTouchedFiles && opts.allTouchedFiles.length > 0) {
271
+ enriched.touchedFiles = tier === "detailed" ? opts.allTouchedFiles.slice(0, 10) : opts.allTouchedFiles;
272
+ } else if (opts.lastSession?.touchedFiles && opts.lastSession.touchedFiles.length > 0) {
273
+ enriched.touchedFiles = tier === "detailed" ? opts.lastSession.touchedFiles.slice(0, 10) : opts.lastSession.touchedFiles;
274
+ }
275
+ if (tier === "detailed") {
276
+ return enriched;
277
+ }
278
+ if (opts.allSessions && opts.allSessions.length > 0) {
279
+ const recent = opts.allSessions.slice(-5).reverse();
280
+ enriched.sessionHistory = recent.map((s) => ({
281
+ timestamp: s.timestamp,
282
+ summary: s.summary || "",
283
+ nextStep: s.nextStep || "",
284
+ branch: s.gitBranch
285
+ }));
286
+ }
287
+ if (opts.recentCommits && opts.recentCommits.length > 0) {
288
+ enriched.recentCommits = opts.recentCommits;
289
+ }
290
+ if (opts.fileConflicts && opts.fileConflicts.length > 0) {
291
+ enriched.fileConflicts = opts.fileConflicts;
292
+ }
293
+ if (opts.branchOverlaps && opts.branchOverlaps.length > 0) {
294
+ enriched.branchOverlaps = opts.branchOverlaps;
295
+ }
296
+ return enriched;
297
+ }
187
298
  function buildCurrentFocus(lastSession, projectState, gitBranch) {
188
299
  if (projectState.derivedCurrentFocus) {
189
300
  return projectState.derivedCurrentFocus;
@@ -359,9 +470,123 @@ function inferFocusFromFiles(files) {
359
470
  return names.join(", ");
360
471
  }
361
472
 
473
+ // ../../packages/shared/src/continueOn.ts
474
+ import path2 from "path";
475
+ function gatherContinueOnContext(reader, workspacePath) {
476
+ const projectName = path2.basename(workspacePath);
477
+ const gitBranch = reader.getCurrentBranch();
478
+ if (!reader.exists()) {
479
+ return {
480
+ projectName,
481
+ gitBranch,
482
+ recentCheckpoints: [],
483
+ recentDecisions: [],
484
+ currentTasks: [],
485
+ recentCommitMessages: []
486
+ };
487
+ }
488
+ const { session: lastCheckpoint } = reader.getScopedLastSession();
489
+ const recentCheckpoints = reader.getScopedRecentSessions(5);
490
+ const recentDecisions = reader.getScopedRecentDecisions(3);
491
+ const currentTasks = reader.getCurrentTasks();
492
+ const state = reader.getState() ?? {};
493
+ const sinceTimestamp = lastCheckpoint?.timestamp;
494
+ const recentCommitMessages = sinceTimestamp ? getCommitMessagesSince(workspacePath, sinceTimestamp) : [];
495
+ const briefing = generateBriefing(
496
+ lastCheckpoint,
497
+ recentCheckpoints,
498
+ state,
499
+ gitBranch,
500
+ recentCommitMessages
501
+ );
502
+ return {
503
+ projectName,
504
+ gitBranch,
505
+ briefing,
506
+ lastCheckpoint,
507
+ recentCheckpoints,
508
+ recentDecisions,
509
+ currentTasks,
510
+ recentCommitMessages
511
+ };
512
+ }
513
+ function formatContinueOnPrompt(context, options) {
514
+ const opts = { includeCommits: true, includeFiles: true, ...options };
515
+ const lines = [];
516
+ lines.push(`# Project Context: ${context.projectName}`);
517
+ lines.push("");
518
+ lines.push("I'm continuing work on a project. Here is my development context from KeepGoing.");
519
+ lines.push("");
520
+ lines.push("## Current Status");
521
+ if (context.gitBranch) {
522
+ lines.push(`- **Branch:** ${context.gitBranch}`);
523
+ }
524
+ if (context.briefing) {
525
+ lines.push(`- **Last worked:** ${context.briefing.lastWorked}`);
526
+ lines.push(`- **Focus:** ${context.briefing.currentFocus}`);
527
+ } else if (context.lastCheckpoint) {
528
+ lines.push(`- **Last worked:** ${formatRelativeTime(context.lastCheckpoint.timestamp)}`);
529
+ if (context.lastCheckpoint.summary) {
530
+ lines.push(`- **Focus:** ${context.lastCheckpoint.summary}`);
531
+ }
532
+ }
533
+ if (context.lastCheckpoint) {
534
+ lines.push("");
535
+ lines.push("## Last Session");
536
+ if (context.lastCheckpoint.summary) {
537
+ lines.push(`- **Summary:** ${context.lastCheckpoint.summary}`);
538
+ }
539
+ if (context.lastCheckpoint.nextStep) {
540
+ lines.push(`- **Next step:** ${context.lastCheckpoint.nextStep}`);
541
+ }
542
+ if (context.lastCheckpoint.blocker) {
543
+ lines.push(`- **Blocker:** ${context.lastCheckpoint.blocker}`);
544
+ }
545
+ if (opts.includeFiles && context.lastCheckpoint.touchedFiles.length > 0) {
546
+ const files = context.lastCheckpoint.touchedFiles.slice(0, 10).join(", ");
547
+ const extra = context.lastCheckpoint.touchedFiles.length > 10 ? ` (+${context.lastCheckpoint.touchedFiles.length - 10} more)` : "";
548
+ lines.push(`- **Files touched:** ${files}${extra}`);
549
+ }
550
+ }
551
+ const activeTasks = context.currentTasks.filter((t) => t.sessionActive);
552
+ if (activeTasks.length > 0) {
553
+ lines.push("");
554
+ lines.push("## Active Sessions");
555
+ for (const task of activeTasks) {
556
+ const label = task.agentLabel || "Unknown agent";
557
+ const branch = task.branch ? ` on \`${task.branch}\`` : "";
558
+ lines.push(`- **${label}**${branch}: ${task.taskSummary || "Working"}`);
559
+ }
560
+ }
561
+ if (context.recentDecisions.length > 0) {
562
+ lines.push("");
563
+ lines.push("## Recent Decisions");
564
+ for (const d of context.recentDecisions) {
565
+ lines.push(`- ${d.classification.category}: ${d.commitMessage}`);
566
+ }
567
+ }
568
+ if (opts.includeCommits && context.recentCommitMessages.length > 0) {
569
+ lines.push("");
570
+ lines.push("## Recent Commits");
571
+ const commits = context.recentCommitMessages.slice(0, 10);
572
+ for (const msg of commits) {
573
+ lines.push(`- ${msg}`);
574
+ }
575
+ }
576
+ lines.push("");
577
+ lines.push("---");
578
+ const nextStep = context.lastCheckpoint?.nextStep || context.briefing?.suggestedNext || "continue where I left off";
579
+ lines.push(`Please help me continue this work. My next step is: ${nextStep}.`);
580
+ let result = lines.join("\n");
581
+ if (opts.maxLength && result.length > opts.maxLength) {
582
+ result = result.slice(0, opts.maxLength - 3) + "...";
583
+ }
584
+ return result;
585
+ }
586
+
362
587
  // ../../packages/shared/src/storage.ts
363
588
  import fs from "fs";
364
- import path2 from "path";
589
+ import path3 from "path";
365
590
  import { randomUUID as randomUUID2, createHash } from "crypto";
366
591
  var STORAGE_DIR = ".keepgoing";
367
592
  var META_FILE = "meta.json";
@@ -384,11 +609,11 @@ var KeepGoingWriter = class {
384
609
  currentTasksFilePath;
385
610
  constructor(workspacePath) {
386
611
  const mainRoot = resolveStorageRoot(workspacePath);
387
- this.storagePath = path2.join(mainRoot, STORAGE_DIR);
388
- this.sessionsFilePath = path2.join(this.storagePath, SESSIONS_FILE);
389
- this.stateFilePath = path2.join(this.storagePath, STATE_FILE);
390
- this.metaFilePath = path2.join(this.storagePath, META_FILE);
391
- this.currentTasksFilePath = path2.join(this.storagePath, CURRENT_TASKS_FILE);
612
+ this.storagePath = path3.join(mainRoot, STORAGE_DIR);
613
+ this.sessionsFilePath = path3.join(this.storagePath, SESSIONS_FILE);
614
+ this.stateFilePath = path3.join(this.storagePath, STATE_FILE);
615
+ this.metaFilePath = path3.join(this.storagePath, META_FILE);
616
+ this.currentTasksFilePath = path3.join(this.storagePath, CURRENT_TASKS_FILE);
392
617
  }
393
618
  ensureDir() {
394
619
  if (!fs.existsSync(this.storagePath)) {
@@ -702,24 +927,24 @@ function capitalize(s) {
702
927
 
703
928
  // ../../packages/shared/src/decisionStorage.ts
704
929
  import fs3 from "fs";
705
- import path4 from "path";
930
+ import path5 from "path";
706
931
 
707
932
  // ../../packages/shared/src/license.ts
708
933
  import crypto from "crypto";
709
934
  import fs2 from "fs";
710
935
  import os from "os";
711
- import path3 from "path";
936
+ import path4 from "path";
712
937
  var LICENSE_FILE = "license.json";
713
938
  var DEVICE_ID_FILE = "device-id";
714
939
  function getGlobalLicenseDir() {
715
- return path3.join(os.homedir(), ".keepgoing");
940
+ return path4.join(os.homedir(), ".keepgoing");
716
941
  }
717
942
  function getGlobalLicensePath() {
718
- return path3.join(getGlobalLicenseDir(), LICENSE_FILE);
943
+ return path4.join(getGlobalLicenseDir(), LICENSE_FILE);
719
944
  }
720
945
  function getDeviceId() {
721
946
  const dir = getGlobalLicenseDir();
722
- const filePath = path3.join(dir, DEVICE_ID_FILE);
947
+ const filePath = path4.join(dir, DEVICE_ID_FILE);
723
948
  try {
724
949
  const existing = fs2.readFileSync(filePath, "utf-8").trim();
725
950
  if (existing) return existing;
@@ -786,7 +1011,7 @@ function writeLicenseStore(store) {
786
1011
  if (!fs2.existsSync(dirPath)) {
787
1012
  fs2.mkdirSync(dirPath, { recursive: true });
788
1013
  }
789
- const licensePath = path3.join(dirPath, LICENSE_FILE);
1014
+ const licensePath = path4.join(dirPath, LICENSE_FILE);
790
1015
  fs2.writeFileSync(licensePath, JSON.stringify(store, null, 2), "utf-8");
791
1016
  _cachedStore = store;
792
1017
  _cacheTimestamp = Date.now();
@@ -828,7 +1053,7 @@ var currentGate = new DefaultFeatureGate();
828
1053
 
829
1054
  // ../../packages/shared/src/reader.ts
830
1055
  import fs4 from "fs";
831
- import path5 from "path";
1056
+ import path6 from "path";
832
1057
  var STORAGE_DIR2 = ".keepgoing";
833
1058
  var META_FILE2 = "meta.json";
834
1059
  var SESSIONS_FILE2 = "sessions.json";
@@ -850,12 +1075,12 @@ var KeepGoingReader = class {
850
1075
  this.workspacePath = workspacePath;
851
1076
  const mainRoot = resolveStorageRoot(workspacePath);
852
1077
  this._isWorktree = mainRoot !== workspacePath;
853
- this.storagePath = path5.join(mainRoot, STORAGE_DIR2);
854
- this.metaFilePath = path5.join(this.storagePath, META_FILE2);
855
- this.sessionsFilePath = path5.join(this.storagePath, SESSIONS_FILE2);
856
- this.decisionsFilePath = path5.join(this.storagePath, DECISIONS_FILE);
857
- this.stateFilePath = path5.join(this.storagePath, STATE_FILE2);
858
- this.currentTasksFilePath = path5.join(this.storagePath, CURRENT_TASKS_FILE2);
1078
+ this.storagePath = path6.join(mainRoot, STORAGE_DIR2);
1079
+ this.metaFilePath = path6.join(this.storagePath, META_FILE2);
1080
+ this.sessionsFilePath = path6.join(this.storagePath, SESSIONS_FILE2);
1081
+ this.decisionsFilePath = path6.join(this.storagePath, DECISIONS_FILE);
1082
+ this.stateFilePath = path6.join(this.storagePath, STATE_FILE2);
1083
+ this.currentTasksFilePath = path6.join(this.storagePath, CURRENT_TASKS_FILE2);
859
1084
  }
860
1085
  /** Check if .keepgoing/ directory exists. */
861
1086
  exists() {
@@ -1111,7 +1336,7 @@ var KeepGoingReader = class {
1111
1336
  // ../../packages/shared/src/setup.ts
1112
1337
  import fs5 from "fs";
1113
1338
  import os2 from "os";
1114
- import path6 from "path";
1339
+ import path7 from "path";
1115
1340
  var KEEPGOING_MARKER = "@keepgoingdev/mcp-server";
1116
1341
  var SESSION_START_HOOK = {
1117
1342
  matcher: "",
@@ -1156,19 +1381,19 @@ function hasKeepGoingHook(hookEntries) {
1156
1381
  }
1157
1382
  function resolveScopePaths(scope, workspacePath) {
1158
1383
  if (scope === "user") {
1159
- const claudeDir2 = path6.join(os2.homedir(), ".claude");
1384
+ const claudeDir2 = path7.join(os2.homedir(), ".claude");
1160
1385
  return {
1161
1386
  claudeDir: claudeDir2,
1162
- settingsPath: path6.join(claudeDir2, "settings.json"),
1163
- claudeMdPath: path6.join(claudeDir2, "CLAUDE.md")
1387
+ settingsPath: path7.join(claudeDir2, "settings.json"),
1388
+ claudeMdPath: path7.join(claudeDir2, "CLAUDE.md")
1164
1389
  };
1165
1390
  }
1166
- const claudeDir = path6.join(workspacePath, ".claude");
1167
- const dotClaudeMdPath = path6.join(workspacePath, ".claude", "CLAUDE.md");
1168
- const rootClaudeMdPath = path6.join(workspacePath, "CLAUDE.md");
1391
+ const claudeDir = path7.join(workspacePath, ".claude");
1392
+ const dotClaudeMdPath = path7.join(workspacePath, ".claude", "CLAUDE.md");
1393
+ const rootClaudeMdPath = path7.join(workspacePath, "CLAUDE.md");
1169
1394
  return {
1170
1395
  claudeDir,
1171
- settingsPath: path6.join(claudeDir, "settings.json"),
1396
+ settingsPath: path7.join(claudeDir, "settings.json"),
1172
1397
  claudeMdPath: fs5.existsSync(dotClaudeMdPath) ? dotClaudeMdPath : rootClaudeMdPath
1173
1398
  };
1174
1399
  }
@@ -1225,7 +1450,6 @@ function setupProject(options) {
1225
1450
  scope = "project",
1226
1451
  sessionHooks = true,
1227
1452
  claudeMd = true,
1228
- hasProLicense = false,
1229
1453
  statusline
1230
1454
  } = options;
1231
1455
  const messages = [];
@@ -1250,7 +1474,7 @@ function setupProject(options) {
1250
1474
  messages.push(`Warning: ${conflict}`);
1251
1475
  }
1252
1476
  }
1253
- if (scope === "project" && hasProLicense) {
1477
+ if (scope === "project") {
1254
1478
  const needsUpdate = settings.statusLine?.command && statusline?.isLegacy?.(settings.statusLine.command);
1255
1479
  if (!settings.statusLine || needsUpdate) {
1256
1480
  settings.statusLine = {
@@ -1281,7 +1505,7 @@ function setupProject(options) {
1281
1505
  messages.push(`CLAUDE.md: KeepGoing section already present in ${mdLabel}, skipped`);
1282
1506
  } else {
1283
1507
  const updated = existing + CLAUDE_MD_SECTION;
1284
- const mdDir = path6.dirname(claudeMdPath);
1508
+ const mdDir = path7.dirname(claudeMdPath);
1285
1509
  if (!fs5.existsSync(mdDir)) {
1286
1510
  fs5.mkdirSync(mdDir, { recursive: true });
1287
1511
  }
@@ -1430,29 +1654,6 @@ function renderNoData() {
1430
1654
  `No KeepGoing data found. Run ${BOLD}keepgoing save${RESET} to save your first checkpoint.`
1431
1655
  );
1432
1656
  }
1433
- function renderBriefing(briefing, decisions) {
1434
- const label = (s) => `${CYAN}${s}${RESET}`;
1435
- console.log(`
1436
- ${BOLD}KeepGoing Re-entry Briefing${RESET}
1437
- `);
1438
- console.log(` ${label("Last worked:")} ${briefing.lastWorked}`);
1439
- console.log(` ${label("Focus:")} ${briefing.currentFocus}`);
1440
- console.log(` ${label("Activity:")} ${briefing.recentActivity}`);
1441
- console.log(` ${label("Next:")} ${briefing.suggestedNext}`);
1442
- console.log(` ${label("Quick start:")} ${briefing.smallNextStep}`);
1443
- if (decisions && decisions.length > 0) {
1444
- console.log(`
1445
- ${label("Recent decisions:")}`);
1446
- for (const d of decisions) {
1447
- const relTime = formatRelativeTime(d.timestamp);
1448
- console.log(` ${d.classification.category}: ${d.commitMessage} ${DIM}(${relTime})${RESET}`);
1449
- }
1450
- }
1451
- console.log("");
1452
- }
1453
- function renderBriefingQuiet(briefing) {
1454
- console.log(`KeepGoing \xB7 ${briefing.lastWorked} \xB7 Focus: ${briefing.currentFocus} \xB7 Next: ${briefing.suggestedNext}`);
1455
- }
1456
1657
  function renderMomentum(checkpoint, ctx) {
1457
1658
  const relTime = formatRelativeTime(checkpoint.timestamp);
1458
1659
  const label = (s) => `${CYAN}${s}${RESET}`;
@@ -1602,18 +1803,130 @@ function renderSessionGroupHeader(sessionId, count) {
1602
1803
  const shortId = sessionId.slice(0, 8);
1603
1804
  console.log(`${BOLD}Session ${shortId}${RESET} ${DIM}(${count} checkpoint${count !== 1 ? "s" : ""})${RESET}`);
1604
1805
  }
1806
+ function renderContinueOn(prompt, copied, target) {
1807
+ console.log(`
1808
+ ${BOLD}KeepGoing Continue On${RESET}
1809
+ `);
1810
+ if (copied) {
1811
+ console.log(`${GREEN}\u2714 Context copied to clipboard${RESET}`);
1812
+ } else {
1813
+ console.log(`${YELLOW}\u26A0 Could not copy to clipboard. Prompt printed below.${RESET}`);
1814
+ }
1815
+ if (target) {
1816
+ console.log(`${DIM}Target: ${target}${RESET}`);
1817
+ }
1818
+ console.log("");
1819
+ console.log(`${DIM}--- prompt start ---${RESET}`);
1820
+ console.log(prompt);
1821
+ console.log(`${DIM}--- prompt end ---${RESET}`);
1822
+ console.log("");
1823
+ }
1824
+ function renderContinueOnQuiet(copied, target) {
1825
+ const targetStr = target ? ` (${target})` : "";
1826
+ if (copied) {
1827
+ console.log(`KeepGoing \xB7 Context copied to clipboard${targetStr}`);
1828
+ } else {
1829
+ console.log(`KeepGoing \xB7 Failed to copy context to clipboard${targetStr}`);
1830
+ }
1831
+ }
1832
+ function renderEnrichedBriefing(briefing) {
1833
+ const { tier, core } = briefing;
1834
+ const label = (s) => `${CYAN}${s}${RESET}`;
1835
+ console.log(`
1836
+ ${BOLD}KeepGoing Re-entry Briefing${RESET} ${DIM}(${tier})${RESET}
1837
+ `);
1838
+ if (briefing.isWorktree && briefing.gitBranch) {
1839
+ console.log(` ${DIM}Worktree: scoped to ${briefing.gitBranch}${RESET}
1840
+ `);
1841
+ }
1842
+ if (tier === "compact") {
1843
+ console.log(` ${label("Last worked:")} ${core.lastWorked}`);
1844
+ console.log(` ${label("Focus:")} ${core.currentFocus}`);
1845
+ console.log(` ${label("Quick start:")} ${core.smallNextStep}`);
1846
+ console.log("");
1847
+ return;
1848
+ }
1849
+ console.log(` ${label("Last worked:")} ${core.lastWorked}`);
1850
+ console.log(` ${label("Focus:")} ${core.currentFocus}`);
1851
+ console.log(` ${label("Activity:")} ${core.recentActivity}`);
1852
+ console.log(` ${label("Next:")} ${core.suggestedNext}`);
1853
+ console.log(` ${label("Quick start:")} ${core.smallNextStep}`);
1854
+ if (briefing.blocker) {
1855
+ console.log(` ${label("Blocker:")} ${YELLOW}${briefing.blocker}${RESET}`);
1856
+ }
1857
+ if (briefing.decisions && briefing.decisions.length > 0) {
1858
+ console.log(`
1859
+ ${label("Recent decisions:")}`);
1860
+ for (const d of briefing.decisions) {
1861
+ const relTime = formatRelativeTime(d.timestamp);
1862
+ console.log(` ${d.classification.category}: ${d.commitMessage} ${DIM}(${relTime})${RESET}`);
1863
+ }
1864
+ }
1865
+ if (briefing.touchedFiles && briefing.touchedFiles.length > 0) {
1866
+ console.log(`
1867
+ ${label(`Files touched (${briefing.touchedFiles.length}):`)}`);
1868
+ const MAX_FILES = tier === "full" ? 20 : 10;
1869
+ const shown = briefing.touchedFiles.slice(0, MAX_FILES);
1870
+ for (const f of shown) {
1871
+ console.log(` ${DIM}${f}${RESET}`);
1872
+ }
1873
+ if (briefing.touchedFiles.length > MAX_FILES) {
1874
+ console.log(` ${DIM}...and ${briefing.touchedFiles.length - MAX_FILES} more${RESET}`);
1875
+ }
1876
+ }
1877
+ if (briefing.sessionHistory && briefing.sessionHistory.length > 0) {
1878
+ console.log(`
1879
+ ${label("Session history:")}`);
1880
+ for (const s of briefing.sessionHistory) {
1881
+ const relTime = formatRelativeTime(s.timestamp);
1882
+ const branch = s.branch ? ` ${GREEN}(${s.branch})${RESET}` : "";
1883
+ console.log(` ${DIM}${relTime}${branch}${RESET} ${s.summary || "No summary"}`);
1884
+ if (s.nextStep) {
1885
+ console.log(` ${CYAN}\u2192${RESET} ${s.nextStep}`);
1886
+ }
1887
+ }
1888
+ }
1889
+ if (briefing.recentCommits && briefing.recentCommits.length > 0) {
1890
+ console.log(`
1891
+ ${label("Recent commits:")}`);
1892
+ for (const msg of briefing.recentCommits.slice(0, 10)) {
1893
+ console.log(` ${DIM}${msg}${RESET}`);
1894
+ }
1895
+ }
1896
+ if (briefing.fileConflicts && briefing.fileConflicts.length > 0) {
1897
+ console.log(`
1898
+ ${YELLOW}File conflicts:${RESET}`);
1899
+ for (const c of briefing.fileConflicts) {
1900
+ const labels = c.sessions.map((s) => s.agentLabel || s.sessionId).join(", ");
1901
+ console.log(` ${c.file}: ${labels}`);
1902
+ }
1903
+ }
1904
+ if (briefing.branchOverlaps && briefing.branchOverlaps.length > 0) {
1905
+ console.log(`
1906
+ ${YELLOW}Branch overlaps:${RESET}`);
1907
+ for (const o of briefing.branchOverlaps) {
1908
+ const labels = o.sessions.map((s) => s.agentLabel || s.sessionId).join(", ");
1909
+ console.log(` ${o.branch}: ${labels}`);
1910
+ }
1911
+ }
1912
+ console.log("");
1913
+ }
1914
+ function renderEnrichedBriefingQuiet(briefing) {
1915
+ const { core } = briefing;
1916
+ console.log(`KeepGoing \xB7 ${core.lastWorked} \xB7 Focus: ${core.currentFocus} \xB7 Next: ${core.smallNextStep}`);
1917
+ }
1605
1918
 
1606
1919
  // src/updateCheck.ts
1607
1920
  import { spawn } from "child_process";
1608
1921
  import { readFileSync, existsSync } from "fs";
1609
- import path7 from "path";
1922
+ import path8 from "path";
1610
1923
  import os3 from "os";
1611
- var CLI_VERSION = "1.0.0";
1924
+ var CLI_VERSION = "1.1.1";
1612
1925
  var NPM_REGISTRY_URL = "https://registry.npmjs.org/@keepgoingdev/cli/latest";
1613
1926
  var FETCH_TIMEOUT_MS = 5e3;
1614
1927
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
1615
- var CACHE_DIR = path7.join(os3.homedir(), ".keepgoing");
1616
- var CACHE_PATH = path7.join(CACHE_DIR, "update-check.json");
1928
+ var CACHE_DIR = path8.join(os3.homedir(), ".keepgoing");
1929
+ var CACHE_PATH = path8.join(CACHE_DIR, "update-check.json");
1617
1930
  function isNewerVersion(current, latest) {
1618
1931
  const cur = current.split(".").map(Number);
1619
1932
  const lat = latest.split(".").map(Number);
@@ -1728,7 +2041,7 @@ async function statusCommand(opts) {
1728
2041
  }
1729
2042
 
1730
2043
  // src/commands/save.ts
1731
- import path8 from "path";
2044
+ import path9 from "path";
1732
2045
  async function saveCommand(opts) {
1733
2046
  const { cwd, message, nextStepOverride, json, quiet, force } = opts;
1734
2047
  const isManual = !!message;
@@ -1757,9 +2070,9 @@ async function saveCommand(opts) {
1757
2070
  sessionStartTime: lastSession?.timestamp ?? now,
1758
2071
  lastActivityTime: now
1759
2072
  });
1760
- const summary = message ?? buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map((f) => path8.basename(f)).join(", ")}`;
2073
+ const summary = message ?? buildSmartSummary(events) ?? `Worked on ${touchedFiles.slice(0, 5).map((f) => path9.basename(f)).join(", ")}`;
1761
2074
  const nextStep = nextStepOverride ?? buildSmartNextStep(events);
1762
- const projectName = path8.basename(resolveStorageRoot(cwd));
2075
+ const projectName = path9.basename(resolveStorageRoot(cwd));
1763
2076
  const sessionId = generateSessionId({
1764
2077
  workspaceRoot: cwd,
1765
2078
  branch: gitBranch ?? void 0,
@@ -1793,7 +2106,7 @@ async function saveCommand(opts) {
1793
2106
 
1794
2107
  // src/commands/hook.ts
1795
2108
  import fs6 from "fs";
1796
- import path9 from "path";
2109
+ import path10 from "path";
1797
2110
  import os4 from "os";
1798
2111
  import { execSync } from "child_process";
1799
2112
  var HOOK_MARKER_START = "# keepgoing-hook-start";
@@ -1853,14 +2166,14 @@ function detectShellRcFile(shellOverride) {
1853
2166
  }
1854
2167
  }
1855
2168
  if (shell === "zsh") {
1856
- return { shell: "zsh", rcFile: path9.join(home, ".zshrc") };
2169
+ return { shell: "zsh", rcFile: path10.join(home, ".zshrc") };
1857
2170
  }
1858
2171
  if (shell === "bash") {
1859
- return { shell: "bash", rcFile: path9.join(home, ".bashrc") };
2172
+ return { shell: "bash", rcFile: path10.join(home, ".bashrc") };
1860
2173
  }
1861
2174
  if (shell === "fish") {
1862
- const xdgConfig = process.env["XDG_CONFIG_HOME"] || path9.join(home, ".config");
1863
- return { shell: "fish", rcFile: path9.join(xdgConfig, "fish", "config.fish") };
2175
+ const xdgConfig = process.env["XDG_CONFIG_HOME"] || path10.join(home, ".config");
2176
+ return { shell: "fish", rcFile: path10.join(xdgConfig, "fish", "config.fish") };
1864
2177
  }
1865
2178
  return void 0;
1866
2179
  }
@@ -2030,30 +2343,43 @@ async function briefingCommand(opts) {
2030
2343
  const state = reader.getState() ?? {};
2031
2344
  const sinceTimestamp = lastSession?.timestamp;
2032
2345
  const recentCommits = sinceTimestamp ? getCommitMessagesSince(opts.cwd, sinceTimestamp) : [];
2033
- const briefing = generateBriefing(
2346
+ const decisions = reader.getScopedRecentDecisions(10);
2347
+ const allSessions = reader.getSessions();
2348
+ const fileConflicts = reader.detectFileConflicts();
2349
+ const branchOverlaps = reader.detectBranchOverlap();
2350
+ const effectiveTier = opts.quiet ? "compact" : opts.tier;
2351
+ const validTiers = ["compact", "standard", "detailed", "full"];
2352
+ const tier = effectiveTier && validTiers.includes(effectiveTier) ? effectiveTier : void 0;
2353
+ const briefing = generateEnrichedBriefing({
2354
+ tier,
2355
+ model: opts.model,
2034
2356
  lastSession,
2035
2357
  recentSessions,
2036
- state,
2358
+ projectState: state,
2037
2359
  gitBranch,
2038
- recentCommits
2039
- );
2360
+ recentCommits,
2361
+ decisions,
2362
+ allTouchedFiles: lastSession?.touchedFiles,
2363
+ allSessions,
2364
+ fileConflicts,
2365
+ branchOverlaps,
2366
+ isWorktree: reader.isWorktree
2367
+ });
2040
2368
  if (!briefing) {
2041
2369
  if (!opts.quiet) {
2042
2370
  console.log("No session data available to generate a briefing.");
2043
2371
  }
2044
2372
  return;
2045
2373
  }
2046
- const decisions = reader.getScopedRecentDecisions(3);
2047
2374
  if (opts.json) {
2048
- const output = decisions.length > 0 ? { ...briefing, decisions } : briefing;
2049
- console.log(JSON.stringify(output, null, 2));
2375
+ console.log(JSON.stringify(briefing, null, 2));
2050
2376
  return;
2051
2377
  }
2052
2378
  if (opts.quiet) {
2053
- renderBriefingQuiet(briefing);
2379
+ renderEnrichedBriefingQuiet(briefing);
2054
2380
  return;
2055
2381
  }
2056
- renderBriefing(briefing, decisions);
2382
+ renderEnrichedBriefing(briefing);
2057
2383
  }
2058
2384
 
2059
2385
  // src/commands/init.ts
@@ -2065,11 +2391,9 @@ var CYAN2 = "\x1B[36m";
2065
2391
  var DIM3 = "\x1B[2m";
2066
2392
  function initCommand(options) {
2067
2393
  const scope = options.scope === "user" ? "user" : "project";
2068
- const hasProLicense = process.env.KEEPGOING_PRO_BYPASS === "1" || !!getLicenseForFeature("session-awareness");
2069
2394
  const result = setupProject({
2070
2395
  workspacePath: options.cwd,
2071
- scope,
2072
- hasProLicense
2396
+ scope
2073
2397
  });
2074
2398
  console.log(`
2075
2399
  ${BOLD3}KeepGoing Init${RESET3} ${DIM3}(${scope} scope)${RESET3}
@@ -2415,6 +2739,83 @@ async function logCommand(opts) {
2415
2739
  }
2416
2740
  }
2417
2741
 
2742
+ // src/platform.ts
2743
+ import { execSync as execSync2, spawnSync } from "child_process";
2744
+ function copyToClipboard(text) {
2745
+ try {
2746
+ const platform = process.platform;
2747
+ if (platform === "darwin") {
2748
+ execSync2("pbcopy", { input: text, stdio: ["pipe", "ignore", "ignore"] });
2749
+ } else if (platform === "win32") {
2750
+ execSync2("clip.exe", { input: text, stdio: ["pipe", "ignore", "ignore"] });
2751
+ } else {
2752
+ try {
2753
+ execSync2("xclip -selection clipboard", { input: text, stdio: ["pipe", "ignore", "ignore"] });
2754
+ } catch {
2755
+ execSync2("xsel --clipboard --input", { input: text, stdio: ["pipe", "ignore", "ignore"] });
2756
+ }
2757
+ }
2758
+ return true;
2759
+ } catch {
2760
+ return false;
2761
+ }
2762
+ }
2763
+ function openUrl(url) {
2764
+ try {
2765
+ const platform = process.platform;
2766
+ if (platform === "darwin") {
2767
+ spawnSync("open", [url], { stdio: "ignore" });
2768
+ } else if (platform === "win32") {
2769
+ spawnSync("cmd", ["/c", "start", "", url], { stdio: "ignore" });
2770
+ } else {
2771
+ spawnSync("xdg-open", [url], { stdio: "ignore" });
2772
+ }
2773
+ } catch {
2774
+ }
2775
+ }
2776
+
2777
+ // src/commands/continue.ts
2778
+ var TARGET_URLS = {
2779
+ chatgpt: "https://chat.openai.com",
2780
+ gemini: "https://gemini.google.com/app",
2781
+ copilot: "https://github.com/copilot",
2782
+ claude: "https://claude.ai/new"
2783
+ };
2784
+ async function continueCommand(opts) {
2785
+ const reader = new KeepGoingReader(opts.cwd);
2786
+ if (!reader.exists()) {
2787
+ if (!opts.quiet) {
2788
+ renderNoData();
2789
+ }
2790
+ return;
2791
+ }
2792
+ const context = gatherContinueOnContext(reader, opts.cwd);
2793
+ if (!context.lastCheckpoint && !context.briefing) {
2794
+ if (!opts.quiet) {
2795
+ console.log("No session data available. Save a checkpoint first.");
2796
+ }
2797
+ return;
2798
+ }
2799
+ const formatOpts = {};
2800
+ if (opts.target && opts.target in TARGET_URLS) {
2801
+ formatOpts.target = opts.target;
2802
+ }
2803
+ const prompt = formatContinueOnPrompt(context, formatOpts);
2804
+ if (opts.json) {
2805
+ console.log(JSON.stringify({ prompt, context }, null, 2));
2806
+ return;
2807
+ }
2808
+ const copied = copyToClipboard(prompt);
2809
+ if (opts.quiet) {
2810
+ renderContinueOnQuiet(copied, opts.target);
2811
+ return;
2812
+ }
2813
+ renderContinueOn(prompt, copied, opts.target);
2814
+ if (opts.open && opts.target && TARGET_URLS[opts.target]) {
2815
+ openUrl(TARGET_URLS[opts.target]);
2816
+ }
2817
+ }
2818
+
2418
2819
  // src/index.ts
2419
2820
  var HELP_TEXT = `
2420
2821
  keepgoing: resume side projects without the mental friction
@@ -2428,6 +2829,7 @@ Commands:
2428
2829
  briefing Get a re-entry briefing for this project
2429
2830
  decisions View decision history (Pro)
2430
2831
  log Browse session checkpoints
2832
+ continue Export context for use in another AI tool
2431
2833
  save Save a checkpoint (auto-generates from git)
2432
2834
  hook Manage the shell hook (zsh, bash, fish)
2433
2835
  activate <key> Activate a Pro license on this device
@@ -2487,6 +2889,8 @@ keepgoing briefing: Get a re-entry briefing for this project
2487
2889
  Usage: keepgoing briefing [options]
2488
2890
 
2489
2891
  Options:
2892
+ --tier <tier> Detail level: compact, standard (default), detailed, full
2893
+ --model <name> Auto-resolve tier from model name (e.g. "claude-opus-4")
2490
2894
  --json Output raw JSON
2491
2895
  --quiet Suppress output
2492
2896
  --cwd <path> Override the working directory
@@ -2582,6 +2986,23 @@ Example:
2582
2986
  keepgoing deactivate: Deactivate the Pro license from this device
2583
2987
 
2584
2988
  Usage: keepgoing deactivate [<key>]
2989
+ `,
2990
+ continue: `
2991
+ keepgoing continue: Export context for use in another AI tool
2992
+
2993
+ Usage: keepgoing continue [options]
2994
+
2995
+ Options:
2996
+ --target <tool> Target AI tool (chatgpt, gemini, copilot, claude, general)
2997
+ --open Auto-open the target tool's URL in your browser
2998
+ --json Output raw JSON (prompt + context)
2999
+ --quiet Suppress output (just copy to clipboard)
3000
+ --cwd <path> Override the working directory
3001
+
3002
+ Examples:
3003
+ keepgoing continue Copy context to clipboard
3004
+ keepgoing continue --target chatgpt --open Copy and open ChatGPT
3005
+ keepgoing continue --json Output as JSON
2585
3006
  `
2586
3007
  };
2587
3008
  function parseArgs(argv) {
@@ -2611,6 +3032,10 @@ function parseArgs(argv) {
2611
3032
  let today = false;
2612
3033
  let week = false;
2613
3034
  let sessions = false;
3035
+ let target = "";
3036
+ let open = false;
3037
+ let tier = "";
3038
+ let model = "";
2614
3039
  for (let i = 0; i < args.length; i++) {
2615
3040
  const arg = args[i];
2616
3041
  if (arg === "--cwd" && i + 1 < args.length) {
@@ -2633,6 +3058,10 @@ function parseArgs(argv) {
2633
3058
  branch = args[++i];
2634
3059
  } else if (arg === "--limit" && i + 1 < args.length) {
2635
3060
  limit = parseInt(args[++i], 10) || 10;
3061
+ } else if (arg === "--target" && i + 1 < args.length) {
3062
+ target = args[++i];
3063
+ } else if (arg === "--open") {
3064
+ open = true;
2636
3065
  } else if (arg === "--json") {
2637
3066
  json = true;
2638
3067
  } else if (arg === "--quiet") {
@@ -2661,6 +3090,10 @@ function parseArgs(argv) {
2661
3090
  week = true;
2662
3091
  } else if (arg === "--sessions") {
2663
3092
  sessions = true;
3093
+ } else if (arg === "--tier" && i + 1 < args.length) {
3094
+ tier = args[++i];
3095
+ } else if (arg === "--model" && i + 1 < args.length) {
3096
+ model = args[++i];
2664
3097
  } else if (arg === "-v" || arg === "--version") {
2665
3098
  command = "version";
2666
3099
  } else if (arg === "-h" || arg === "--help") {
@@ -2697,7 +3130,11 @@ function parseArgs(argv) {
2697
3130
  blockerOnly,
2698
3131
  today,
2699
3132
  week,
2700
- sessions
3133
+ sessions,
3134
+ target,
3135
+ open,
3136
+ tier,
3137
+ model
2701
3138
  };
2702
3139
  }
2703
3140
  async function main() {
@@ -2724,7 +3161,13 @@ async function main() {
2724
3161
  await momentumCommand({ cwd, json, quiet });
2725
3162
  break;
2726
3163
  case "briefing":
2727
- await briefingCommand({ cwd, json, quiet });
3164
+ await briefingCommand({
3165
+ cwd,
3166
+ json,
3167
+ quiet,
3168
+ tier: parsed.tier || void 0,
3169
+ model: parsed.model || void 0
3170
+ });
2728
3171
  break;
2729
3172
  case "decisions":
2730
3173
  await decisionsCommand({ cwd, json, quiet, branch, limit });
@@ -2750,6 +3193,9 @@ async function main() {
2750
3193
  sessions: parsed.sessions
2751
3194
  });
2752
3195
  break;
3196
+ case "continue":
3197
+ await continueCommand({ cwd, json, quiet, target: parsed.target, open: parsed.open });
3198
+ break;
2753
3199
  case "save":
2754
3200
  await saveCommand({
2755
3201
  cwd,
@@ -2770,7 +3216,7 @@ async function main() {
2770
3216
  }
2771
3217
  break;
2772
3218
  case "version":
2773
- console.log(`keepgoing v${"1.0.0"}`);
3219
+ console.log(`keepgoing v${"1.1.1"}`);
2774
3220
  break;
2775
3221
  case "activate":
2776
3222
  await activateCommand({ licenseKey: subcommand });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keepgoingdev/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Terminal CLI for KeepGoing. Resume side projects without the mental friction.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",