Commit Graph
11 Commits
Author SHA1 Message Date
kimandClaude Sonnet 5 c649fdf6ed feat: task persistence, memoized UI components, hook/delete-failure safety
locode-authored (dogfooded) changes:
- TaskStore.toJSON/fromJSON: task_create/list state now survives session
  resume instead of resetting to empty (session.ts, task.ts, sessionStore.ts).
- StatusBar/ChatInput/ThinkingIndicator wrapped in memo() to skip re-renders
  during the ~30fps streaming-text update loop.
- HistoryItemView's todos list keys off t.content instead of array index
  (TodoItem has no id field; content is the best stable key available).
- sessionStore.ts read paths now log (console.warn) on corrupt/unreadable
  files instead of silently swallowing the error.

Fixes on top, found in review:
- safeRunHooksForEvent() wraps the 4 remaining unprotected hook call sites
  (PreToolUse, PermissionRequest, PostToolUse, SubagentStart) — these fire on
  every tool call/sub-agent, far more often than the 3 sites already wrapped
  (Stop/SessionStart/UserPromptSubmit), so a broken hook script was still able
  to abort an in-flight turn.
- deleteSession() no longer reports success when unlinkSync fails for a
  reason other than ENOENT (e.g. EPERM/EBUSY from a Windows file lock) — it
  used to drop the index entry and return true anyway, orphaning the file on
  disk with no way to reference it again. `sessions rm` in cli.ts now
  distinguishes "not found" from "found but couldn't delete" in its message.
- ChatInput's memo() was a no-op: onSubmit={handleSubmit} passed a fresh
  closure every render since handleSubmit isn't wrapped in useCallback.
  Rather than force a ~300-line function (which reads isThinking/streamingText
  — both changing every streaming frame) into a dependency array, added a
  handleSubmitRef + stable wrapper, the same ref-indirection this file
  already uses for phaseRef.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LxiAaGhSD4DRVYYQZ5GJjm
2026-08-24 15:02:34 +09:00
kim f2ca1543d2 feat(lsp): configurable language servers + merge C/C++ into one clangd
The built-in LANGUAGE_SPECS were hardcoded — users couldn't add Java/Ruby/Lua
servers or override a built-in's command/args, and the file's own comment
flagged this as a future extension. Also, c and cpp were separate specs both
spawning clangd, so a mixed C/C++ project ran two indexing the same headers.

- lspManager.ts: LANGUAGE_SPECS is now mutable; configureLanguageSpecs(overrides)
  merges user entries (keyed by languageId) into the built-ins. A built-in id
  override replaces command/args and, if extensions is given, rewrites routing.
  A new id adds a mapping but REQUIRES extensions (ignored otherwise — can't
  route files to it). C and C++ collapse into one 'c' clangd spec (all
  .c/.h/.cpp/.cc/.cxx/.hpp/.hh/.hxx route to a single clangd).
- config.ts: resolveLspServers() reads stored.lspServers.
- store.ts: StoredConfig.lspServers field.
- cli.ts: 'locode config set lspServers <json>' (JSON object value, validated).
- ui/ink/index.tsx: configureLanguageSpecs(resolveLspServers()) at startup.
- lspManager.test.ts: 6 tests for the merge (add, override, rewrite exts,
  ignore-without-extensions, c/cpp collapse) via _specsForTests/_resetSpecsForTests
  — no servers spawned.

Verified: typecheck clean, build 256.60 KB, 256 tests pass (+6).
2026-08-21 13:43:05 +09:00
kim cb950891d5 feat: LSP code intelligence + parallel sub-agent mutation gate
Code intelligence (LSP):
- src/codeintel/lspManager.ts: lazy per-language LSP server lifecycle
  (tsserver/pyright/gopls/clangd/rust-analyzer), didOpen/didChange sync,
  definition/references/diagnostics, notifyFileChanged, shutdownAll.
  Fix stream typing (StreamMessageReader/Writer) + MarkupContent→string.
- src/tools/codeIntel.ts: definition/references/diagnostics read-only tools.
- tools/index.ts: register the three LSP tools.
- agent/loop.ts: wire notifyFileChanged into the FileChanged hook path
  (fire-and-forget, best-effort) so live servers stay in sync with disk.
- ui/ink/index.tsx: shutdownAll on exit so spawned servers aren't orphaned.

Parallel sub-agent orchestration:
- agent/session.ts: session.mutationGate promise chain serializes every
  mutating tool call across the session (incl. parallel sub-agents that
  share the parent session) so they can't race on the single permission
  slot or interleave filesystem writes. Read-only tools stay concurrent.
- agent/loop.ts: runUnderMutationGate wraps mutating tool execution;
  sub-agents inherit the parent's gate.
- tools/agentTool.ts: `tasks` array runs N sub-agents in parallel; one
  failure surfaces as that task's error, not a rejected batch.
- tools/types.ts: SubAgentResult type.

Other:
- cli.ts + mcp/client.ts: read version from package.json instead of
  hardcoding "0.3.1".
- agent/parallelAgents.test.ts: parallel batch + mutation-gate tests.
- Remove scratch guardtest2.mjs.

Verified: typecheck clean, build 245.85 KB, 219 tests pass.
2026-08-21 13:22:34 +09:00
kim 6fe98887d5 v0.6.0: complete all 12 local-model upgrades + raise maxIterations to 100
Upgrade candidates (all 12 done):
- #1 parallel read-only tool execution (runToolBatch, 4 loop sites)
- #2 configurable retry policy (maxRetries + exponential backoff via SDK)
- #3 script-aware token estimation (CJK/symbol/structure-aware heuristic)
- #4 head+tail output capping (truncate.ts), auto-applies to bash/git/etc
- #5 partial-history compaction (preserve recent tail, summarize older prefix)
- #6 dynamic max_tokens (resolveMaxTokens)
- #7 richer tool descriptions with "use when" guidance
- #8 MCP reconnect retry + /mcp reconnect command + session toolset refresh
- #9 context-window cache TTL (cachedAt timestamp, default 7 days)
- #10 edit_file similar-match suggestion on old_string miss (bounded Levenshtein)
- #11 git_status output head+tail (resolved via #4)
- #12 auto-accept now approves all mutating tools; auto-edit stays edit-only

Other:
- DEFAULT_MAX_ITERATIONS 50 -> 100 (local models issue one tool call per step)
- MaxIterationsError message guides resume + config override
- plus prior known-issues work (grep -e/--, session id sanitization, MCP content
  types, 12 hook events, plugin collisions, skill references, git ops expansion,
  configurable autoCompactThreshold, bashGuard, pathGuard, FilePanel, replay)
2026-08-20 16:52:03 +09:00
kim eb03d0bd14 feat: todo lists, permission modes, project instructions, and agent robustness
- Add todo_write tool with live checklist rendering in the UI
- Add permission modes (default/plan/auto-edit/auto-accept) cycled via Shift+Tab or /perm
- Load CLAUDE.md/AGENTS.md as project instructions into the system prompt
- Forward sub-agent tool calls/results to parent UI panel
- Auto-compact context mid-turn and keep model moving with a synthetic user turn
- Prune older image_url parts to cap vision token cost
- Idle-abort guard for stalled streaming backends
- Improve background bash jobs, kill whole process trees on timeout/exit
- Expand hooks: command/http hooks, blocking, JSON outputSchema
- MCP: JSON schema → Zod conversion, parallel connection, duplicate server detection
- Add tests for loop, confirmFn, projectInstructions, readFile, bash, todoWrite, contextWindowCache
2026-07-13 15:47:48 +09:00
kimandClaude Sonnet 5 f01898a9c3 Bump to 0.3.1: fix backend hangs, UI layout, and dead-code type error
A day of dogfooding locode on itself surfaced several real bugs, most centered on
the backend request lifecycle silently hanging with no error surfaced to the user:

- bash/hooks: execa's shell:true defaulted to cmd.exe on Windows, which lacks
  Unix tools (cat, sed, grep, ...) and fails a whole pipe with an unhelpful exit
  255 if any stage isn't found. Added src/utils/shell.ts to resolve real Git Bash
  when available.
- agent/loop: the backend client's own `timeout` only bounds time-to-first-byte —
  once a streaming response starts, a backend that goes silent mid-stream (or one
  that sends periodic content-less "heartbeat" chunks to keep the connection alive
  through a proxy) hung the request forever with zero output. Added an idle-abort
  guard that pokes only on chunks carrying real progress (usage/text/tool-calls/
  finish_reason), covering the main stream, the non-streaming malformed-JSON
  retry, and compactSession.
- agent/loop: a single tool-call-heavy turn (e.g. reading dozens of files) had no
  auto-compaction safety net at all — shouldAutoCompact was only ever checked
  between turns, never during one, so a long turn could blow past the context
  window with no mid-turn correction. Now checked every iteration; re-anchors
  mutationCommitLength afterward since compaction replaces session.messages
  wholesale.
- agent/loop: hitting the iteration cap surfaced as an opaque "Request failed"
  even when every prior tool call (including file edits) had actually succeeded.
  Added MaxIterationsError, rendered as a calm "paused, send another message to
  continue" notice instead, and raised the default cap 25 -> 50.
- agent/loop: removed a dead-code branch (fallback-malformed retry handling
  placed inside a native-mode-only code path, so session.mode === "fallback"
  could never be true there) that TypeScript correctly flagged as a type error.
- ui/App: text_done fires with an empty fullText for pure tool-call turns (no
  preceding prose); the assistant-message push had no guard, rendering a blank
  line every time. Now skipped when fullText is empty.
- ui/App+index: alternate-screen-buffer mode and StatusBar-above-ChatInput
  layout, fixing the reported "prompt starts below the dashboard, jumps up on
  space" glitch. Added input history (up/down recall).
- utils/writeFileAtomic: random temp-file suffix instead of a fixed `<file>.tmp`,
  so concurrent writes to the same directory can't collide.

Also fixes two hardcoded version strings (cli.ts --version, mcp/client.ts's
self-reported MCP identity) that were left stale at 0.2.0 through the 0.3.0
bump, and a stale maxIterations default noted in the README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 18:06:52 +09:00
kimandClaude Sonnet 5 4652ecb87b Add bash_kill, background job eviction, and configurable request timeout
Bump to 0.3.0.

- bash_kill tool lets the model terminate a still-running backgrounded
  job (Ctrl+B); all running jobs are killed on process exit so they
  don't outlive locode as orphans.
- Finished background jobs are now evicted from the registry after a
  10-minute TTL instead of accumulating for the life of the session.
- The chat completion request timeout is now configurable
  (requestTimeoutMs / LOCODE_REQUEST_TIMEOUT_MS, default 180000) so
  backends that queue requests behind a concurrency limit (e.g.
  Ollama's OLLAMA_NUM_PARALLEL) under multi-session load aren't forced
  into a hard failure at a fixed 3 minutes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 12:48:32 +09:00
kimandClaude Sonnet 5 2a35f40c73 Add plugin/hook/skill support, images, @mentions, dashboard, and stability fixes
Builds out locode's Claude Code plugin parity: MCP servers, slash commands,
sub-agents, hooks (12 lifecycle events), and skills, all loadable from a
local path or git URL. Also adds image support (read_file, /import), @-mention
file autocomplete, a /dashboard stats view, /export with an editable filename
prompt, an expanded git tool (reset/stash/merge/rebase/delete_branch), and a
redesigned status bar.

On top of that, a review pass found and fixed 10 correctness/stability bugs:
argument-injection in git reset/merge/rebase (a ref like "--hard" was parsed
as a flag), tool-call image results interleaving with native tool_call_id
messages and breaking OpenAI-compatible message ordering, backgrounded bash
jobs still being silently killed by their original timeout with the kill
masked as a clean exit, a SubagentStart hook's block being ignored, the
sub-agent timeout clock starting before the hook it should exclude, a
template-expansion bug that could re-substitute $1..$9 placeholders,
unbounded background-job output buffers, a missing directory-target fallback
in /export, and image size caps checked after reading the whole file instead
of before.

Session saves and exports now go through a shared atomic
write-then-rename helper so a crash can't leave a truncated file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 18:17:45 +09:00
kimandClaude Sonnet 5 4e6c0bb216 Bump to 0.2.0 and make max tool-call iterations configurable
The 8-iteration-per-turn cap was hardcoded from before this session's feature work and
too tight for genuine multi-file tasks, causing "Max tool-call iterations reached" on
legitimately multi-step work. Raised the default to 25 and made it configurable via
`locode config set maxIterations <n>` / $LOCODE_MAX_ITERATIONS, mirroring the existing
contextWindow config pattern.

Also bumps the version to 0.2.0 (package.json, --version, and the MCP client's
self-reported identity) to reflect the substantial feature additions since 0.1.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 18:08:49 +09:00
kimandClaude Sonnet 5 3f1527375c Add web search/fetch, sub-agents, MCP support, git tools, session persistence, and context compaction
Substantially expands locode's tool loop toward feature parity with Claude Code:

- web_search / web_fetch: DuckDuckGo-backed search and URL fetching (HTML stripped to text)
- git_status / git_commit: structured git tools with real diff/status/commit-list previews
  before any mutating operation is confirmed
- agent tool: sub-agents run an isolated, headless tool loop sharing the parent's permissions;
  bounded by an explicit depth guard (no nested sub-agents) and a 120s hard timeout, independent
  of the toolset already excluding `agent` for sub-agent sessions
- MCP client support (stdio + streamable HTTP transports) via the official SDK: locode connects
  to servers configured in `.mcp.json` or via `locode mcp add`, and exposes their tools alongside
  the built-in ones, namespaced as `mcp__<server>__<tool>`
- Session persistence: conversations auto-save after every turn; `--continue`/`--resume`,
  `locode sessions list/rm`, and an interactive resume picker
- Context compaction: tracks real token usage via `stream_options.include_usage` (falling back to
  a char-based estimate), auto-detects the model's context window (Ollama/LM Studio native APIs,
  cached, else a configurable default), and auto-summarizes the conversation at 85% usage or on
  demand via /compact
- Status bar and welcome banner: added a context-usage indicator and fixed several dim-by-default
  colors that were hard to read

Session/loop architecture changed to support all of this: Session now carries its own toolset
(tools + registry + OpenAI schemas) built from local + MCP tools, so runTurn no longer depends on
a fixed global tool list — the same mechanism that lets sub-agents inherit MCP tools while
excluding `agent` itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 17:53:27 +09:00
kim 68f2242984 first commit 2026-07-06 12:41:15 +09:00