Files
locode/.claude/plans/resolve-known-issues.md
T
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

9.7 KiB
Raw Blame History

Plan: Resolve locode's 9 known issues

Goals

Fix all nine documented issues/limitations in one coherent pass, keep typecheck green, and add enough tests so npm test passes.

Issues and implementation details

1. Grep bug: patterns starting with - are parsed as ripgrep flags

File: src/tools/grep.ts Approach: Pass the pattern with ripgrep's -e option instead of as a positional argument, and put -- before the path argument. This prevents any pattern (including -foo, --foo, +foo) from being interpreted as a flag. Test: Add src/tools/grep.test.ts that mocks execa and verifies the generated args for a leading-dash pattern.

2. Security: session id is not sanitized before building the file path

File: src/persistence/sessionStore.ts Approach:

  • Derive a safe filename from the user-supplied id by replacing path separators and other unsafe characters with _.
  • Keep the original id inside the record (it already is), but use the sanitized id for the filesystem lookup.
  • Add a safeId helper and use it in filePath, loadSession, deleteSession, and listSessions so --resume ../../etc/passwd cannot escape the sessions directory. Test: Add src/persistence/sessionStore.test.ts verifying that malicious ids are contained, normal ids still work, and listSessions ignores non-.json files.

3. Tests: npm test fails because there are no test files

Approach: Create the first test suite covering the fixes above. Add vitest.config.ts with globals: true and a test//src/**/*.test.ts include. The tests will be pure unit tests that don't require a backend. Files to add:

  • vitest.config.ts
  • src/tools/grep.test.ts
  • src/persistence/sessionStore.test.ts
  • src/plugins/expandTemplate.test.ts (small sanity test for existing behavior)

4. MCP content types: non-text results are placeholder-only

Files: src/mcp/toolAdapter.ts, src/mcp/client.ts (type update) Approach:

  • Update McpToolInfo and the call-tool handler to support image, audio, and resource content blocks from the MCP spec.
  • For image: include mimeType and a truncated base64 note; if the model path is vision-capable, return the actual data: URI so the model can see it (same pattern as read_file).
  • For audio: include mimeType and a note.
  • For resource: if it's a text resource, inline the text; if binary, note the URI and mime type.
  • Keep text blocks unchanged. Test: Add src/mcp/toolAdapter.test.ts with mocked MCP content blocks of each type.

5. Hooks: only 6 events; no structured JSON output; no HTTP/prompt/agent hook types

Files: src/hooks/types.ts, src/hooks/runner.ts, src/hooks/config.ts Approach (scoped but complete within reason):

  • Expand the hook event set to the most useful missing events: PermissionRequest, SubagentStart, SubagentStop, CwdChanged, FileChanged, ConfigChange, Stop is already present. Final set: all 12 events from Claude Code's common set: SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PermissionRequest, SubagentStart, SubagentStop, CwdChanged, FileChanged, ConfigChange, Stop, SessionEnd.
  • Add typed payload shapes for each event in src/hooks/types.ts.
  • Add a structured output schema option: hooks can specify outputSchema: "json" in their config; when set, stdout is parsed as JSON and validated against a simple zod schema. If parsing fails, treat as a warning.
  • Add an HTTPHook type (simple GET/POST with optional headers/body) and a PromptHook type (shows a yes/no prompt to the user). Wire HTTPHook in the runner; PromptHook will be added to types but marked as not yet implemented in the runner to avoid UI-blocking complexity in this pass.
  • Fire PermissionRequest right before the user confirm gate in agent/loop.ts gateAndRun.
  • Fire SubagentStart/SubagentStop around runSubAgentTurn.
  • Fire CwdChanged when the session cwd would change (currently static; hook is informational for future use).
  • Fire FileChanged after a mutating write_file/edit_file/bash succeeds.
  • Fire ConfigChange when /model, /backend, /mode, or locode config set changes config. Test: Add src/hooks/runner.test.ts testing exit-code semantics (0/2/other), JSON output parsing, and tool-scoped matching.

6. Plugin collisions: MCP and skill name collisions are not resolved cleanly

Files: src/mcp/config.ts, src/plugins/skillTool.ts, src/plugins/registry.ts Approach:

  • MCP servers: when a plugin provides a server whose name collides with a user or project server, log/record the collision and keep the more-specific tier (project > user > plugin). Add a collisions field to the returned merge result and surface it in /mcp.
  • Skills: namespace skills internally as <pluginName>/<skillName> in the skill tool, but still accept the bare skill name for the /name shortcut. If two skills share a bare name, prefer the first loaded plugin and emit a warning in /skills listing duplicates.
  • Plugin agent names are already namespaced (agent__<plugin>__<agent>); commands are already namespaced by the fact that they share a single command namespace. Add a warning list for duplicate command names too.
  • Add a PluginCollisionWarning type and expose it via getLoadedPlugins() metadata. Test: Add src/plugins/skillTool.test.ts and src/mcp/config.test.ts for collision behavior.

7. Skill references: cannot load sibling reference files for a skill

Files: src/plugins/loader.ts, src/plugins/types.ts, src/plugins/skillTool.ts Approach:

  • When loading a skill, also read any references/*.md files in the same skill directory.
  • Store them as references: { name, content }[] on PluginSkill.
  • When the skill is invoked (via the skill tool or /name), concatenate the references after the main SKILL.md body under a clear header so the model sees them. Test: Add src/plugins/loader.test.ts with a temporary in-memory plugin layout.

8. Git operations: git_commit covers a subset

File: src/tools/git.ts Approach: Extend git_commit with these additional operations:

  • reset — git reset (mixed by default) with optional ref and mode (soft/mixed/hard). Requires confirmation; preview shows affected commits/files.
  • stash — git stash push (with optional message and paths) and git stash pop (with optional stash ref). Preview shows what will be stashed/popped.
  • merge — git merge <branchName> with optional --no-ff/--ff-only. Preview shows branches and commits.
  • rebase — git rebase <branchName> with optional --onto. Preview shows commits.
  • delete_branch — git branch -d/-D <branchName>. Preview shows the branch and whether it has unmerged commits.
  • Add operation union entries and the necessary parameters (ref, mode, stashRef, branchName, strategy, message, paths, force).
  • Keep the existing preview/handler pattern. Test: Add src/tools/git.test.ts that validates argument generation for each operation (no real git exec).

9. Context window: 85% auto-compact threshold is hardcoded

Files: src/agent/loop.ts, src/config/config.ts, src/config/store.ts, src/config/defaults.ts, src/cli.ts Approach:

  • Add autoCompactThreshold to StoredConfig and env var LOCODE_AUTO_COMPACT_THRESHOLD.
  • Default remains 0.85; allow values 0.1–0.95.
  • Add locode config set autoCompactThreshold <0.0-1.0> and locode config get autoCompactThreshold.
  • Read it in resolveAutoCompactThreshold() and use it in shouldAutoCompact in agent/loop.ts.
  • Pass the threshold into the Session object so sub-agents inherit it. Test: Add src/config/config.test.ts for threshold resolution and src/agent/loop.test.ts for the compact check.

Files to modify

  1. src/tools/grep.ts
  2. src/persistence/sessionStore.ts
  3. src/mcp/toolAdapter.ts
  4. src/mcp/client.ts
  5. src/mcp/types.ts (add status/collision type)
  6. src/mcp/config.ts
  7. src/mcp/manager.ts (surface collision warnings)
  8. src/hooks/types.ts
  9. src/hooks/runner.ts
  10. src/hooks/config.ts
  11. src/agent/loop.ts (fire new hooks, use threshold)
  12. src/agent/session.ts (store threshold)
  13. src/plugins/loader.ts
  14. src/plugins/types.ts
  15. src/plugins/skillTool.ts
  16. src/plugins/registry.ts (collision tracking)
  17. src/plugins/agentTool.ts (add command dup tracking if needed)
  18. src/tools/git.ts
  19. src/config/config.ts
  20. src/config/store.ts
  21. src/config/defaults.ts
  22. src/config/types.ts
  23. src/cli.ts (add config key)
  24. src/ui/ink/App.tsx (pass threshold, fire ConfigChange)
  25. src/ui/ink/HistoryItemView.tsx (show MCP collision warnings)
  26. README.md (update limitations)

Files to add

  1. vitest.config.ts
  2. src/tools/grep.test.ts
  3. src/persistence/sessionStore.test.ts
  4. src/plugins/expandTemplate.test.ts
  5. src/mcp/toolAdapter.test.ts
  6. src/hooks/runner.test.ts
  7. src/plugins/skillTool.test.ts
  8. src/mcp/config.test.ts
  9. src/plugins/loader.test.ts
  10. src/tools/git.test.ts
  11. src/config/config.test.ts
  12. src/agent/loop.test.ts

Validation

  • Run npm run typecheck — must pass.
  • Run npm test — must pass.
  • Run npm run build — must succeed.

Risks / trade-offs

  • Expanding hooks to 12 events touches agent/loop.ts in several places; need to keep event payloads consistent.
  • MCP content type support is best-effort; real vision models may still ignore audio/resource blocks.
  • Git operation expansion increases the chance of destructive commands (reset --hard, rebase, delete_branch); previews must be clear and the tool remains mutating/confirm-gated.
  • Making the auto-compact threshold configurable requires threading a new field through Session creation/resumption.