@keepgoingdev/mcp-server 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1844,7 +1844,6 @@ function setupProject(options) {
1844
1844
  scope = "project",
1845
1845
  sessionHooks = true,
1846
1846
  claudeMd = true,
1847
- hasProLicense = false,
1848
1847
  statusline
1849
1848
  } = options;
1850
1849
  const messages = [];
@@ -1869,7 +1868,7 @@ function setupProject(options) {
1869
1868
  messages.push(`Warning: ${conflict}`);
1870
1869
  }
1871
1870
  }
1872
- if (scope === "project" && hasProLicense) {
1871
+ if (scope === "project") {
1873
1872
  const needsUpdate = settings.statusLine?.command && statusline?.isLegacy?.(settings.statusLine.command);
1874
1873
  if (!settings.statusLine || needsUpdate) {
1875
1874
  settings.statusLine = {
@@ -2531,13 +2530,11 @@ function registerSetupProject(server, workspacePath) {
2531
2530
  scope: z6.enum(["project", "user"]).optional().default("project").describe('Where to write config: "user" for global (~/.claude/), "project" for per-project (.claude/)')
2532
2531
  },
2533
2532
  async ({ sessionHooks, claudeMd, scope }) => {
2534
- const hasProLicense = process.env.KEEPGOING_PRO_BYPASS === "1" || !!getLicenseForFeature("session-awareness");
2535
2533
  const result = setupProject({
2536
2534
  workspacePath,
2537
2535
  scope,
2538
2536
  sessionHooks,
2539
2537
  claudeMd,
2540
- hasProLicense,
2541
2538
  statusline: {
2542
2539
  isLegacy: isLegacyStatusline,
2543
2540
  cleanup: cleanupLegacyScript
@@ -2983,6 +2980,126 @@ async function handleSaveCheckpoint() {
2983
2980
  process.exit(0);
2984
2981
  }
2985
2982
 
2983
+ // src/cli/transcriptUtils.ts
2984
+ import fs7 from "fs";
2985
+ var TAIL_READ_BYTES = 8192;
2986
+ var TOOL_VERB_MAP = {
2987
+ Edit: "editing",
2988
+ MultiEdit: "editing",
2989
+ Write: "editing",
2990
+ Read: "researching",
2991
+ Glob: "researching",
2992
+ Grep: "researching",
2993
+ Bash: "running",
2994
+ Agent: "delegating",
2995
+ WebFetch: "browsing",
2996
+ WebSearch: "browsing",
2997
+ TodoWrite: "planning"
2998
+ };
2999
+ function truncateAtWord(text, max) {
3000
+ if (text.length <= max) return text;
3001
+ const cut = text.slice(0, max);
3002
+ const lastSpace = cut.lastIndexOf(" ");
3003
+ return (lastSpace > max / 2 ? cut.slice(0, lastSpace) : cut.slice(0, max - 1)) + "\u2026";
3004
+ }
3005
+ var FILLER_PREFIX_RE = /^(i want to|can you|please|let['']?s|could you|help me|i need to|i['']d like to|implement the following plan[:\s]*|implement this plan[:\s]*)\s*/i;
3006
+ var MARKDOWN_HEADING_RE = /^#+\s+/;
3007
+ function extractTextFromContent(content) {
3008
+ if (typeof content === "string") return content;
3009
+ if (!Array.isArray(content)) return "";
3010
+ let text = "";
3011
+ for (const part of content) {
3012
+ if (part.type === "text" && typeof part.text === "string") {
3013
+ text += part.text + " ";
3014
+ }
3015
+ }
3016
+ return text.trim();
3017
+ }
3018
+ function isUserEntry(entry) {
3019
+ return entry.type === "user" && entry.message?.role === "user";
3020
+ }
3021
+ function getToolUseFromEntry(entry) {
3022
+ const content = entry.message?.content;
3023
+ if (!Array.isArray(content)) return null;
3024
+ for (const part of content.slice().reverse()) {
3025
+ if (part.type === "tool_use" && typeof part.name === "string") {
3026
+ return part.name;
3027
+ }
3028
+ }
3029
+ return null;
3030
+ }
3031
+ function isAssistantEntry(entry) {
3032
+ return entry.message?.role === "assistant";
3033
+ }
3034
+ function extractSessionLabel(transcriptPath) {
3035
+ if (!transcriptPath || !fs7.existsSync(transcriptPath)) return null;
3036
+ try {
3037
+ const raw = fs7.readFileSync(transcriptPath, "utf-8");
3038
+ for (const line of raw.split("\n")) {
3039
+ const trimmed = line.trim();
3040
+ if (!trimmed) continue;
3041
+ let entry;
3042
+ try {
3043
+ entry = JSON.parse(trimmed);
3044
+ } catch {
3045
+ continue;
3046
+ }
3047
+ if (!isUserEntry(entry)) continue;
3048
+ let text = extractTextFromContent(entry.message?.content);
3049
+ if (!text) continue;
3050
+ if (text.startsWith("[") || /^<[a-z][\w-]*>/.test(text)) continue;
3051
+ text = text.replace(/@[\w./\-]+/g, "").trim();
3052
+ text = text.replace(FILLER_PREFIX_RE, "").trim();
3053
+ text = text.replace(MARKDOWN_HEADING_RE, "").trim();
3054
+ text = text.replace(/\s+/g, " ").trim();
3055
+ if (text.length < 20) continue;
3056
+ if (text.length > 80) {
3057
+ text = text.slice(0, 80);
3058
+ }
3059
+ return text;
3060
+ }
3061
+ } catch {
3062
+ }
3063
+ return null;
3064
+ }
3065
+ function extractCurrentAction(transcriptPath) {
3066
+ if (!transcriptPath || !fs7.existsSync(transcriptPath)) return null;
3067
+ try {
3068
+ const stat = fs7.statSync(transcriptPath);
3069
+ const fileSize = stat.size;
3070
+ if (fileSize === 0) return null;
3071
+ const readSize = Math.min(fileSize, TAIL_READ_BYTES);
3072
+ const offset = fileSize - readSize;
3073
+ const buf = Buffer.alloc(readSize);
3074
+ const fd = fs7.openSync(transcriptPath, "r");
3075
+ try {
3076
+ fs7.readSync(fd, buf, 0, readSize, offset);
3077
+ } finally {
3078
+ fs7.closeSync(fd);
3079
+ }
3080
+ const tail = buf.toString("utf-8");
3081
+ const lines = tail.split("\n").reverse();
3082
+ for (const line of lines) {
3083
+ const trimmed = line.trim();
3084
+ if (!trimmed) continue;
3085
+ let entry;
3086
+ try {
3087
+ entry = JSON.parse(trimmed);
3088
+ } catch {
3089
+ continue;
3090
+ }
3091
+ if (!isAssistantEntry(entry)) continue;
3092
+ const toolName = getToolUseFromEntry(entry);
3093
+ if (toolName) {
3094
+ return TOOL_VERB_MAP[toolName] ?? "working";
3095
+ }
3096
+ return "done";
3097
+ }
3098
+ } catch {
3099
+ }
3100
+ return null;
3101
+ }
3102
+
2986
3103
  // src/cli/updateTask.ts
2987
3104
  async function handleUpdateTask() {
2988
3105
  const args = process.argv.slice(2);
@@ -3034,7 +3151,8 @@ async function handleUpdateTaskFromHook() {
3034
3151
  const writer = new KeepGoingWriter(wsPath);
3035
3152
  const existing = writer.readCurrentTasks();
3036
3153
  const sessionIdFromHook = hookData.session_id;
3037
- const cachedBranch = sessionIdFromHook ? existing.find((t) => t.sessionId === sessionIdFromHook)?.branch : void 0;
3154
+ const existingSession = sessionIdFromHook ? existing.find((t) => t.sessionId === sessionIdFromHook) : void 0;
3155
+ const cachedBranch = existingSession?.branch;
3038
3156
  const branch = cachedBranch ?? getCurrentBranch(wsPath) ?? void 0;
3039
3157
  const task = {
3040
3158
  taskSummary: fileName ? `${toolName} ${fileName}` : `Used ${toolName}`,
@@ -3046,6 +3164,10 @@ async function handleUpdateTaskFromHook() {
3046
3164
  };
3047
3165
  const sessionId = hookData.session_id || generateSessionId({ ...task, workspaceRoot: wsPath });
3048
3166
  task.sessionId = sessionId;
3167
+ if (!existingSession?.sessionLabel && hookData.transcript_path) {
3168
+ const label = extractSessionLabel(hookData.transcript_path);
3169
+ if (label) task.sessionLabel = label;
3170
+ }
3049
3171
  writer.upsertSession(task);
3050
3172
  } catch {
3051
3173
  }
@@ -3055,7 +3177,7 @@ async function handleUpdateTaskFromHook() {
3055
3177
  }
3056
3178
 
3057
3179
  // src/cli/statusline.ts
3058
- import fs7 from "fs";
3180
+ import fs8 from "fs";
3059
3181
  import path11 from "path";
3060
3182
  var STDIN_TIMEOUT_MS2 = 3e3;
3061
3183
  async function handleStatusline() {
@@ -3070,36 +3192,43 @@ async function handleStatusline() {
3070
3192
  clearTimeout(timeout);
3071
3193
  try {
3072
3194
  const raw = Buffer.concat(chunks).toString("utf-8").trim();
3073
- if (!raw) {
3074
- process.exit(0);
3075
- }
3195
+ if (!raw) process.exit(0);
3076
3196
  const input = JSON.parse(raw);
3077
3197
  const dir = input.workspace?.current_dir ?? input.cwd;
3078
- if (!dir) {
3079
- process.exit(0);
3080
- }
3081
- const gitRoot = findGitRoot(dir);
3082
- const tasksFile = path11.join(gitRoot, ".keepgoing", "current-tasks.json");
3083
- if (!fs7.existsSync(tasksFile)) {
3084
- process.exit(0);
3085
- }
3086
- const data = JSON.parse(fs7.readFileSync(tasksFile, "utf-8"));
3087
- const branch = getCurrentBranch(gitRoot) ?? "";
3088
- const active = pruneStaleTasks(data.tasks ?? []).filter((t) => t.sessionActive && t.branch === branch);
3089
- if (active.length === 0) {
3090
- process.exit(0);
3198
+ if (!dir) process.exit(0);
3199
+ const transcriptPath = input.transcript_path;
3200
+ const sessionId = input.session_id;
3201
+ let label = null;
3202
+ if (input.agent?.name) {
3203
+ label = input.agent.name;
3204
+ }
3205
+ if (!label) {
3206
+ try {
3207
+ const gitRoot = findGitRoot(dir);
3208
+ const tasksFile = path11.join(gitRoot, ".keepgoing", "current-tasks.json");
3209
+ if (fs8.existsSync(tasksFile)) {
3210
+ const data = JSON.parse(fs8.readFileSync(tasksFile, "utf-8"));
3211
+ const tasks = pruneStaleTasks(data.tasks ?? []);
3212
+ const match = sessionId ? tasks.find((t) => t.sessionId === sessionId) : void 0;
3213
+ if (match?.sessionLabel) {
3214
+ label = match.sessionLabel;
3215
+ }
3216
+ }
3217
+ } catch {
3218
+ }
3091
3219
  }
3092
- active.sort((a, b) => a.updatedAt.localeCompare(b.updatedAt));
3093
- const task = active[active.length - 1];
3094
- const summary = task.taskSummary;
3095
- if (!summary) {
3096
- process.exit(0);
3220
+ if (!label && transcriptPath) {
3221
+ label = extractSessionLabel(transcriptPath);
3097
3222
  }
3098
- if (branch) {
3099
- process.stdout.write(`[KG] ${branch}: ${summary}
3223
+ if (!label) process.exit(0);
3224
+ const action = transcriptPath ? extractCurrentAction(transcriptPath) : null;
3225
+ const budget = action ? 40 : 55;
3226
+ const displayLabel = truncateAtWord(label, budget);
3227
+ if (action) {
3228
+ process.stdout.write(`[KG] ${displayLabel} \xB7 ${action}
3100
3229
  `);
3101
3230
  } else {
3102
- process.stdout.write(`[KG] ${summary}
3231
+ process.stdout.write(`[KG] ${displayLabel}
3103
3232
  `);
3104
3233
  }
3105
3234
  } catch {