@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 +159 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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"
|
|
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
|
|
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
|
|
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
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
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
|
-
|
|
3093
|
-
|
|
3094
|
-
const summary = task.taskSummary;
|
|
3095
|
-
if (!summary) {
|
|
3096
|
-
process.exit(0);
|
|
3220
|
+
if (!label && transcriptPath) {
|
|
3221
|
+
label = extractSessionLabel(transcriptPath);
|
|
3097
3222
|
}
|
|
3098
|
-
if (
|
|
3099
|
-
|
|
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] ${
|
|
3231
|
+
process.stdout.write(`[KG] ${displayLabel}
|
|
3103
3232
|
`);
|
|
3104
3233
|
}
|
|
3105
3234
|
} catch {
|