@kmugalkhod/lightcode 0.6.0 → 0.7.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.
Files changed (4) hide show
  1. package/README.md +28 -0
  2. package/cli.js +633 -236
  3. package/package.json +1 -1
  4. package/server.js +220 -104
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kmugalkhod/lightcode",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Lightcode — a personal AI coding agent with a terminal UI and local session storage.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/server.js CHANGED
@@ -147,7 +147,7 @@ var init_src = __esm(() => {
147
147
  });
148
148
 
149
149
  // packages/ai/src/constants.ts
150
- var MAX_TOOL_TEXT_OUTPUT_CHARS = 12000, DEFAULT_TOOL_TEXT_OUTPUT_CHARS = 6000, MAX_TOOL_LIST_ENTRIES = 200, MAX_TOOL_SEARCH_RESULTS = 200, ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET = 24;
150
+ var MAX_TOOL_TEXT_OUTPUT_CHARS = 12000, DEFAULT_TOOL_TEXT_OUTPUT_CHARS = 6000, MAX_TOOL_LIST_ENTRIES = 200, MAX_TOOL_SEARCH_RESULTS = 200, ANTHROPIC_TOOL_OPTIONAL_PARAMETER_BUDGET = 24, TODO_CONTENT_MAX_CHARS = 200, TODO_ACTIVE_FORM_MAX_CHARS = 80, PROGRESS_NOTE_PRUNE_MIN_CHARS = 200;
151
151
 
152
152
  // packages/ai/src/coding-agent-modes.ts
153
153
  import { z } from "zod";
@@ -1045,20 +1045,22 @@ var init_schema10 = __esm(() => {
1045
1045
  todoPrioritySchema = z14.enum(["low", "medium", "high"]);
1046
1046
  todoItemSchema = z14.object({
1047
1047
  id: z14.string().min(1).max(120).optional(),
1048
- content: z14.string().min(1).max(500),
1048
+ content: z14.string().min(1).max(TODO_CONTENT_MAX_CHARS),
1049
1049
  status: todoStatusSchema,
1050
1050
  priority: todoPrioritySchema.optional().default("medium"),
1051
- notes: z14.string().max(1000).optional()
1051
+ notes: z14.string().max(1000).optional(),
1052
+ activeForm: z14.string().min(1).max(TODO_ACTIVE_FORM_MAX_CHARS).optional()
1052
1053
  });
1053
1054
  todoItemProviderSchema = z14.object({
1054
- content: z14.string().min(1).max(500),
1055
- status: todoStatusSchema
1055
+ content: z14.string().min(1).max(TODO_CONTENT_MAX_CHARS),
1056
+ status: todoStatusSchema,
1057
+ activeForm: z14.string().min(1).max(TODO_ACTIVE_FORM_MAX_CHARS).optional().describe(`Short present-tense active label for this task, e.g. "Adding tests" or "Editing schema". Set it primarily for the in_progress item so the UI can show what's actively happening; the UI falls back to content when omitted.`)
1056
1058
  });
1057
1059
  todoWriteInputSchema = z14.object({
1058
1060
  todos: z14.array(todoItemSchema).max(50)
1059
1061
  });
1060
1062
  todoWriteProviderInputSchema = z14.object({
1061
- todos: z14.array(todoItemProviderSchema)
1063
+ todos: z14.array(todoItemProviderSchema).max(20)
1062
1064
  });
1063
1065
  todoWriteOutputSchema = z14.object({
1064
1066
  sessionId: z14.string(),
@@ -1070,7 +1072,8 @@ var init_schema10 = __esm(() => {
1070
1072
  inProgress: z14.number().int().nonnegative(),
1071
1073
  completed: z14.number().int().nonnegative(),
1072
1074
  canceled: z14.number().int().nonnegative()
1073
- })
1075
+ }),
1076
+ warnings: z14.array(z14.string()).optional()
1074
1077
  });
1075
1078
  });
1076
1079
 
@@ -1904,14 +1907,139 @@ ${environmentContext.trim()}` : "");
1904
1907
  var cwdPromptPlaceholder = "{cwd}", defaultCodingAgentSystemPrompt, toolCallingDisciplineAddendum;
1905
1908
  var init_coding_agent_prompt = __esm(() => {
1906
1909
  init_coding_agent_modes();
1907
- defaultCodingAgentSystemPrompt = "You are a coding agent working inside the user's project at this working directory: {cwd}. " + 'You can already see the project \u2014 proactively read it. When the user asks you to review, explain, or work on "my code"/"this", inspect the working directory yourself with list_files, glob_search, grep, and read_file; NEVER ask the user to paste or share code that lives in this directory \u2014 open it. ' + "Use tools for filesystem and codebase tasks instead of guessing, and only interact with files under the working directory. " + "Prefer dedicated tools \u2014 glob_search for paths, grep for text search, structured git tools for repository inspection \u2014 and bash only when those are not enough. " + `Use todo_write to keep multi-step work visible and current, inspect context before risky operations, and emit a brief, natural progress note before major tool actions and after important findings.
1910
+ defaultCodingAgentSystemPrompt = "You are a coding agent working inside the user's project at this working directory: {cwd}. " + 'You can already see the project \u2014 proactively read it. When the user asks you to review, explain, or work on "my code"/"this", inspect the working directory yourself with list_files, glob_search, grep, and read_file; NEVER ask the user to paste or share code that lives in this directory \u2014 open it. ' + "Use tools for filesystem and codebase tasks instead of guessing, and only interact with files under the working directory. " + "Prefer dedicated tools \u2014 glob_search for paths, grep for text search, structured git tools for repository inspection \u2014 and bash only when those are not enough. " + `Inspect context before risky operations. Progress notes: before a major tool action you may emit ONE short note (one sentence, ~15 words) saying what you are about to do. After a tool returns, mention a finding only if it changes your plan, and state it as fact grounded in what the tool actually returned \u2014 cite the file/line/symbol. NEVER claim you found, changed, or fixed something before a tool result confirms it. Skip notes for routine reads, and do not restate the todo list in prose.
1911
+
1912
+ ` + `Task list discipline \u2014 use the todo_write tool to keep a visible, evolving checklist:
1913
+ ` + `- Create the list FIRST. For any task with roughly three or more distinct steps, or any multi-file or multi-stage work, call todo_write as your very first action \u2014 before reading or editing \u2014 with every step listed and all of them set to pending.
1914
+ ` + `- Keep exactly ONE item in_progress at a time. Mark an item in_progress immediately before you start it; never have two items in_progress at once.
1915
+ ` + `- Mark an item completed only after a tool result confirms it is actually done (test passed, file written, build clean) \u2014 not on intent. Do this the MOMENT it is confirmed; do not batch completions or wait until the end of the turn.
1916
+ ` + `- Completions are monotonic: once a task is completed, do not silently move it back to pending or in_progress. Reopen it only if it genuinely regressed.
1917
+ ` + `- Every todo_write call is a FULL REWRITE of the list: always send every task with its current status (re-state the completed and pending ones too \u2014 never send only the changed item). Keep each item terse (a short label, not a paragraph).
1918
+ ` + `- Skip the list for trivial, single-step, or purely conversational requests \u2014 a checklist there is just noise.
1908
1919
 
1909
1920
  ` + `You are an agent \u2014 keep working until the request is fully resolved. Never stop with a partial answer; if you say you will do something, do it in the same turn by calling a tool. Do not finish while any todo is pending or in_progress \u2014 end your turn only when the task is complete or you are blocked on input that only the user can provide.
1910
1921
 
1922
+ ` + `When the task is complete, end with a short summary (a few lines) grounded in the todos you actually completed and the real file diffs \u2014 name the files you edited, do not list steps you did not verify, and do not repeat the todo card verbatim. If anything is incomplete or you are blocked, say so plainly.
1923
+
1911
1924
  ` + "Long-running processes (dev servers, watchers): never run them as plain foreground bash \u2014 the bash tool enforces a timeout and will kill them. Start them detached with output redirected to a log " + '(Windows: start /B cmd /c "npm run dev > dev.log 2>&1"; Unix: nohup npm run dev > dev.log 2>&1 &), then read the log to confirm startup and the actual port (do not assume the default). ' + "Never kill processes or free ports you did not open yourself.";
1912
1925
  toolCallingDisciplineAddendum = "Invoke tools only through the function-calling interface. " + "Never write tool calls as XML or plain text such as <tool_call>, <function_call>, <invoke>, or bare JSON in your message. " + "Tool arguments must be valid JSON matching the tool's schema. " + "Call one tool at a time and wait for its result before deciding the next step.";
1913
1926
  });
1914
1927
 
1928
+ // packages/ai/src/common/coerce-tool-input.ts
1929
+ function coercePrimitive(current, expected) {
1930
+ if (current === undefined || current === null) {
1931
+ return NO_CHANGE;
1932
+ }
1933
+ switch (expected) {
1934
+ case "number": {
1935
+ if (typeof current === "string") {
1936
+ const trimmed = current.trim();
1937
+ if (trimmed !== "" && Number.isFinite(Number(trimmed))) {
1938
+ return Number(trimmed);
1939
+ }
1940
+ }
1941
+ return NO_CHANGE;
1942
+ }
1943
+ case "boolean": {
1944
+ if (typeof current === "string") {
1945
+ const lowered = current.trim().toLowerCase();
1946
+ if (lowered === "true" || lowered === "1" || lowered === "yes") {
1947
+ return true;
1948
+ }
1949
+ if (lowered === "false" || lowered === "0" || lowered === "no") {
1950
+ return false;
1951
+ }
1952
+ }
1953
+ if (current === 1)
1954
+ return true;
1955
+ if (current === 0)
1956
+ return false;
1957
+ return NO_CHANGE;
1958
+ }
1959
+ case "string": {
1960
+ if (typeof current === "number" || typeof current === "boolean") {
1961
+ return String(current);
1962
+ }
1963
+ return NO_CHANGE;
1964
+ }
1965
+ case "object":
1966
+ case "array": {
1967
+ if (typeof current === "string") {
1968
+ try {
1969
+ const parsed = JSON.parse(current);
1970
+ const isArray = Array.isArray(parsed);
1971
+ const matches = expected === "array" ? isArray : parsed !== null && typeof parsed === "object" && !isArray;
1972
+ if (matches) {
1973
+ return parsed;
1974
+ }
1975
+ } catch {}
1976
+ }
1977
+ return NO_CHANGE;
1978
+ }
1979
+ default:
1980
+ return NO_CHANGE;
1981
+ }
1982
+ }
1983
+ function coerceAtPath(root, path3, expected) {
1984
+ if (path3.length === 0 || root === null || typeof root !== "object") {
1985
+ return false;
1986
+ }
1987
+ let parent = root;
1988
+ for (let index = 0;index < path3.length - 1; index += 1) {
1989
+ const next = parent[path3[index]];
1990
+ if (next === null || typeof next !== "object") {
1991
+ return false;
1992
+ }
1993
+ parent = next;
1994
+ }
1995
+ const key = path3[path3.length - 1];
1996
+ const coerced = coercePrimitive(parent[key], expected);
1997
+ if (coerced === NO_CHANGE) {
1998
+ return false;
1999
+ }
2000
+ parent[key] = coerced;
2001
+ return true;
2002
+ }
2003
+ function coerceToolInputToSchema(value, schema) {
2004
+ if (value === null || typeof value !== "object") {
2005
+ return null;
2006
+ }
2007
+ let current;
2008
+ try {
2009
+ current = structuredClone(value);
2010
+ } catch {
2011
+ current = value;
2012
+ }
2013
+ for (let pass = 0;pass < MAX_PASSES; pass += 1) {
2014
+ const result = schema.safeParse(current);
2015
+ if (result.success) {
2016
+ return result.data;
2017
+ }
2018
+ let changed = false;
2019
+ for (const issue of result.error.issues) {
2020
+ if (issue.code !== "invalid_type") {
2021
+ continue;
2022
+ }
2023
+ const expected = issue.expected;
2024
+ if (typeof expected !== "string") {
2025
+ continue;
2026
+ }
2027
+ if (coerceAtPath(current, issue.path, expected)) {
2028
+ changed = true;
2029
+ }
2030
+ }
2031
+ if (!changed) {
2032
+ break;
2033
+ }
2034
+ }
2035
+ const final = schema.safeParse(current);
2036
+ return final.success ? final.data : null;
2037
+ }
2038
+ var NO_CHANGE, MAX_PASSES = 4;
2039
+ var init_coerce_tool_input = __esm(() => {
2040
+ NO_CHANGE = Symbol("no-change");
2041
+ });
2042
+
1915
2043
  // packages/ai/src/providers/tool-call-json-repair.ts
1916
2044
  import { parsePartialJson } from "ai";
1917
2045
  async function repairToolJson(raw) {
@@ -2106,15 +2234,19 @@ async function repairCodingAgentToolCall({
2106
2234
  if (!repairedInput || typeof repairedInput !== "object") {
2107
2235
  return null;
2108
2236
  }
2237
+ const schema = codingToolProviderInputSchemas[toolCall.toolName];
2238
+ const coerced = schema ? coerceToolInputToSchema(repairedInput, schema) : null;
2109
2239
  return {
2110
2240
  ...toolCall,
2111
- input: JSON.stringify(repairedInput)
2241
+ input: JSON.stringify(coerced ?? repairedInput)
2112
2242
  };
2113
2243
  }
2114
2244
  return null;
2115
2245
  }
2116
2246
  var toolNameAliases;
2117
2247
  var init_coding_agent_tool_repair = __esm(() => {
2248
+ init_agent_tools();
2249
+ init_coerce_tool_input();
2118
2250
  init_tool_call_json_repair();
2119
2251
  toolNameAliases = {
2120
2252
  read: "read_file",
@@ -2203,19 +2335,6 @@ function getLastUserText(messages, prompt) {
2203
2335
  }
2204
2336
  return collectTextFromUnknown(prompt).trim();
2205
2337
  }
2206
- function pushUniqueTool(tools, toolName) {
2207
- if (!tools.includes(toolName)) {
2208
- tools.push(toolName);
2209
- }
2210
- }
2211
- function includesAny(text, tokens) {
2212
- return tokens.some((token) => {
2213
- if (token.includes("://") || token.includes(" ")) {
2214
- return text.includes(token);
2215
- }
2216
- return new RegExp(`(^|[^a-z0-9])${token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^a-z0-9]|$)`).test(text);
2217
- });
2218
- }
2219
2338
  function selectCodingAgentIntentTools({
2220
2339
  mode,
2221
2340
  prompt,
@@ -2226,75 +2345,7 @@ function selectCodingAgentIntentTools({
2226
2345
  if (!normalizedText || casualPromptPattern.test(normalizedText)) {
2227
2346
  return [];
2228
2347
  }
2229
- const situational = [];
2230
- const hasGitIntent = includesAny(normalizedText, [
2231
- "git",
2232
- "diff",
2233
- "status",
2234
- "commit",
2235
- "log",
2236
- "show",
2237
- "revision",
2238
- "changes"
2239
- ]);
2240
- if (hasGitIntent) {
2241
- if (includesAny(normalizedText, ["status", "state", "dirty", "working tree"]) || !includesAny(normalizedText, ["diff", "log", "show", "history", "commit"])) {
2242
- pushUniqueTool(situational, "git_status");
2243
- }
2244
- if (includesAny(normalizedText, ["diff", "changes", "patch"])) {
2245
- pushUniqueTool(situational, "git_diff");
2246
- }
2247
- if (includesAny(normalizedText, ["log", "history", "commits"])) {
2248
- pushUniqueTool(situational, "git_log");
2249
- }
2250
- if (includesAny(normalizedText, ["show", "revision", "commit"])) {
2251
- pushUniqueTool(situational, "git_show");
2252
- }
2253
- }
2254
- if (mode === "build" && includesAny(normalizedText, ["todo", "tasks", "checklist", "steps", "epic", "ticket"])) {
2255
- pushUniqueTool(situational, "todo_write");
2256
- }
2257
- if (includesAny(normalizedText, ["tool_search", "tool search", "find tool", "available tool"])) {
2258
- pushUniqueTool(situational, "tool_search");
2259
- }
2260
- if (includesAny(normalizedText, ["skill", "skills", "load skill"])) {
2261
- pushUniqueTool(situational, "skill");
2262
- }
2263
- if (includesAny(normalizedText, ["mcp", "resource", "resources", "mcp tool", "mcp server"])) {
2264
- pushUniqueTool(situational, "list_mcp_resources");
2265
- if (includesAny(normalizedText, ["read", "resource", "resources"])) {
2266
- pushUniqueTool(situational, "read_mcp_resource");
2267
- }
2268
- if (mode === "build" && includesAny(normalizedText, ["call", "execute", "run"])) {
2269
- pushUniqueTool(situational, "call_mcp_tool");
2270
- }
2271
- }
2272
- if (includesAny(normalizedText, [
2273
- "http://",
2274
- "https://",
2275
- "url",
2276
- "fetch",
2277
- "website",
2278
- "web",
2279
- "online",
2280
- "search internet",
2281
- "latest"
2282
- ])) {
2283
- pushUniqueTool(situational, "web_fetch");
2284
- pushUniqueTool(situational, "web_search");
2285
- }
2286
- if (mode === "plan" && includesAny(normalizedText, ["question", "ask", "clarify"])) {
2287
- pushUniqueTool(situational, "request_user_input");
2288
- }
2289
- const ordered = [...baseReadTools, ...situational];
2290
- if (mode === "build") {
2291
- ordered.push("write_file", "edit_file", "bash");
2292
- }
2293
- const selected = [];
2294
- for (const toolName of ordered) {
2295
- pushUniqueTool(selected, toolName);
2296
- }
2297
- return selected.slice(0, maxProviderActiveTools);
2348
+ return [...getCodingAgentModeDefinition(mode).activeTools];
2298
2349
  }
2299
2350
  function limitProviderActiveTools(tools, maxTools = maxProviderActiveTools) {
2300
2351
  if (tools.length <= maxTools) {
@@ -2303,15 +2354,10 @@ function limitProviderActiveTools(tools, maxTools = maxProviderActiveTools) {
2303
2354
  const toolSet = new Set(tools);
2304
2355
  return providerToolPriority.filter((toolName) => toolSet.has(toolName)).slice(0, maxTools);
2305
2356
  }
2306
- var casualPromptPattern, baseReadTools, maxProviderActiveTools = 7, providerToolPriority;
2357
+ var casualPromptPattern, maxProviderActiveTools = 24, providerToolPriority;
2307
2358
  var init_intent_tool_selection = __esm(() => {
2359
+ init_coding_agent_modes();
2308
2360
  casualPromptPattern = /^(hi|hello|hey|how are you|how r you|what'?s up|whats up|sup|thanks|thank you|ok|okay|yes|no|nice|cool|great)[\s?!.,]*$/i;
2309
- baseReadTools = [
2310
- "list_files",
2311
- "glob_search",
2312
- "read_file",
2313
- "grep"
2314
- ];
2315
2361
  providerToolPriority = [
2316
2362
  "list_files",
2317
2363
  "glob_search",
@@ -2984,6 +3030,16 @@ function getPartState(part) {
2984
3030
  const state = isRecord3(part) ? Reflect.get(part, "state") : undefined;
2985
3031
  return typeof state === "string" ? state : null;
2986
3032
  }
3033
+ function getPlainTextPartContent(part) {
3034
+ if (!isRecord3(part) || Reflect.get(part, "type") !== "text") {
3035
+ return null;
3036
+ }
3037
+ const text = Reflect.get(part, "text");
3038
+ return typeof text === "string" ? text : null;
3039
+ }
3040
+ function messageHasToolPart(message) {
3041
+ return message.parts.some((part) => getToolNameFromPart(part) !== null);
3042
+ }
2987
3043
  function computePruneCutoffIndex(messages, preserveRecentMessages, quantizeUserTurns = DEFAULT_PRUNE_QUANTIZE_USER_TURNS) {
2988
3044
  const candidate = messages.length - preserveRecentMessages;
2989
3045
  if (candidate <= 0) {
@@ -3033,15 +3089,29 @@ function pruneToolOutputs(messages, options) {
3033
3089
  });
3034
3090
  let elidedToolOutputs = 0;
3035
3091
  let dedupedFileReads = 0;
3092
+ let elidedProgressNotes = 0;
3036
3093
  let savedChars = 0;
3037
3094
  const prunedMessages = messages.map((message, messageIndex) => {
3038
3095
  if (messageIndex >= cutoff) {
3039
3096
  return message;
3040
3097
  }
3098
+ const noteCandidate = message.role === "assistant" && messageHasToolPart(message);
3041
3099
  let changed = false;
3042
3100
  const parts = message.parts.map((part, partIndex) => {
3043
3101
  const toolName = getToolNameFromPart(part);
3044
3102
  if (!toolName || getPartState(part) !== "output-available") {
3103
+ if (noteCandidate && toolName === null) {
3104
+ const text = getPlainTextPartContent(part);
3105
+ if (text !== null && text !== PROGRESS_NOTE_ELISION_STUB && text.length > PROGRESS_NOTE_PRUNE_MIN_CHARS) {
3106
+ elidedProgressNotes += 1;
3107
+ savedChars += Math.max(0, text.length - PROGRESS_NOTE_ELISION_STUB.length);
3108
+ changed = true;
3109
+ return {
3110
+ ...part,
3111
+ text: PROGRESS_NOTE_ELISION_STUB
3112
+ };
3113
+ }
3114
+ }
3045
3115
  return part;
3046
3116
  }
3047
3117
  const output = isRecord3(part) ? Reflect.get(part, "output") : undefined;
@@ -3058,7 +3128,7 @@ function pruneToolOutputs(messages, options) {
3058
3128
  }
3059
3129
  const stub = {
3060
3130
  lightcodeElided: true,
3061
- note: isStaleRead ? "Earlier read of this file elided; a later read in this conversation has the current content. Re-run read_file if needed." : "Large tool output elided to preserve context budget. Re-run the tool if this output is needed again.",
3131
+ note: isStaleRead ? "Earlier read of this file elided; its current content is already shown later in this conversation \u2014 do not read it again." : "Large tool output elided to preserve context budget. Re-run the tool only if this output is needed again.",
3062
3132
  originalChars: serializedOutput.length
3063
3133
  };
3064
3134
  if (isStaleRead) {
@@ -3076,10 +3146,11 @@ function pruneToolOutputs(messages, options) {
3076
3146
  messages: prunedMessages,
3077
3147
  elidedToolOutputs,
3078
3148
  dedupedFileReads,
3149
+ elidedProgressNotes,
3079
3150
  savedChars
3080
3151
  };
3081
3152
  }
3082
- var DEFAULT_PRUNE_MIN_OUTPUT_CHARS = 2000, DEFAULT_PRUNE_QUANTIZE_USER_TURNS = 4;
3153
+ var DEFAULT_PRUNE_MIN_OUTPUT_CHARS = 2000, PROGRESS_NOTE_ELISION_STUB = "[progress note elided]", DEFAULT_PRUNE_QUANTIZE_USER_TURNS = 4;
3083
3154
  var init_tier1_prune = __esm(() => {
3084
3155
  init_estimate();
3085
3156
  init_message_parts();
@@ -6104,6 +6175,22 @@ function mergeFinishedMessagesIntoFullHistory({
6104
6175
  return [...fullMessages, ...appendedMessages];
6105
6176
  }
6106
6177
 
6178
+ // packages/ai/src/tool-call-repeat-guard.ts
6179
+ var guardedTools, lastCallByTurn;
6180
+ var init_tool_call_repeat_guard = __esm(() => {
6181
+ guardedTools = new Set([
6182
+ "read_file",
6183
+ "list_files",
6184
+ "glob_search",
6185
+ "grep",
6186
+ "git_status",
6187
+ "git_diff",
6188
+ "git_log",
6189
+ "git_show"
6190
+ ]);
6191
+ lastCallByTurn = new Map;
6192
+ });
6193
+
6107
6194
  // packages/ai/src/common/resolve-within-workspace.ts
6108
6195
  import path8 from "path";
6109
6196
  import {
@@ -6483,6 +6570,8 @@ var init_runtime16 = __esm(() => {
6483
6570
  // packages/ai/src/runtime-registry.ts
6484
6571
  var init_runtime_registry = __esm(() => {
6485
6572
  init_agent_tools();
6573
+ init_coerce_tool_input();
6574
+ init_tool_call_repeat_guard();
6486
6575
  init_runtime4();
6487
6576
  init_runtime6();
6488
6577
  init_runtime7();
@@ -7418,6 +7507,26 @@ var init_session_auto_title = __esm(() => {
7418
7507
  import { readFile as readFile2, readdir } from "fs/promises";
7419
7508
  import { platform as platform3 } from "os";
7420
7509
  import path11 from "path";
7510
+ function formatAvailableSkills(skills) {
7511
+ if (skills.length === 0) {
7512
+ return "";
7513
+ }
7514
+ const shown = skills.slice(0, maxListedSkills);
7515
+ const lines = shown.map((skill) => `- ${skill.name}${skill.description ? `: ${skill.description}` : ""}`);
7516
+ const more = skills.length > shown.length ? `
7517
+ \u2026 and ${skills.length - shown.length} more` : "";
7518
+ return `Available skills (load by name with the skill tool):
7519
+ ${lines.join(`
7520
+ `)}${more}`;
7521
+ }
7522
+ function buildAvailableSkills(cwd) {
7523
+ try {
7524
+ return formatAvailableSkills(listSkills({ cwd }));
7525
+ } catch (error) {
7526
+ logger7.warn("workspace_skills_failed", { cwd, error: getErrorMessage(error) });
7527
+ return "";
7528
+ }
7529
+ }
7421
7530
  function describePlatform() {
7422
7531
  const map = {
7423
7532
  darwin: "macOS",
@@ -7493,6 +7602,7 @@ async function buildWorkspaceContext({
7493
7602
  buildGitSummary(cwd),
7494
7603
  buildProjectDocs(cwd)
7495
7604
  ]);
7605
+ const skills = buildAvailableSkills(cwd);
7496
7606
  const today = new Date().toISOString().slice(0, 10);
7497
7607
  const parts = [
7498
7608
  "<environment>",
@@ -7508,6 +7618,8 @@ async function buildWorkspaceContext({
7508
7618
  docs ? `
7509
7619
  Project instructions:
7510
7620
  ${docs}` : "",
7621
+ skills ? `
7622
+ ${skills}` : "",
7511
7623
  "</environment>"
7512
7624
  ];
7513
7625
  const block = parts.join(`
@@ -7516,7 +7628,7 @@ ${docs}` : "",
7516
7628
  \u2026 (environment truncated)
7517
7629
  </environment>` : block;
7518
7630
  }
7519
- var logger7, projectDocNames, maxListedEntries = 40, maxDocChars = 1500, maxBlockChars = 8000;
7631
+ var logger7, projectDocNames, maxListedEntries = 40, maxListedSkills = 20, maxDocChars = 1500, maxBlockChars = 8000;
7520
7632
  var init_workspace_context = __esm(() => {
7521
7633
  init_runtime17();
7522
7634
  init_src();
@@ -7555,7 +7667,8 @@ function messagesHaveToolActivity(messages) {
7555
7667
  function shouldUseFastChatPath({
7556
7668
  messages,
7557
7669
  mode,
7558
- allowedTools
7670
+ allowedTools,
7671
+ availableSkillNames = []
7559
7672
  }) {
7560
7673
  if (allowedTools && allowedTools.length > 0) {
7561
7674
  return false;
@@ -7566,7 +7679,8 @@ function shouldUseFastChatPath({
7566
7679
  return selectCodingAgentIntentTools({
7567
7680
  mode,
7568
7681
  prompt: "",
7569
- messages
7682
+ messages,
7683
+ availableSkillNames
7570
7684
  }).length === 0;
7571
7685
  }
7572
7686
  async function countPendingChatInteractions(sessionId) {
@@ -7884,11 +7998,13 @@ async function streamSessionChat(c, sessionIdentifier, messagesPayload, cwd, mod
7884
7998
  };
7885
7999
  };
7886
8000
  const environmentContext = await buildWorkspaceContext({ cwd });
8001
+ const availableSkillNames = listSkills({ cwd }).map((skill) => skill.name);
7887
8002
  try {
7888
8003
  if (shouldUseFastChatPath({
7889
8004
  messages: providerMessages,
7890
8005
  mode,
7891
- allowedTools
8006
+ allowedTools,
8007
+ availableSkillNames
7892
8008
  })) {
7893
8009
  logger8.info("chat_fast_path", {
7894
8010
  sessionId,