mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 14:01:06 +00:00
Add bash mode for executing shell commands
- Add ! prefix in TUI editor to execute shell commands directly
- Output streams in real-time and is added to LLM context
- Supports multiline commands, cancellation (Escape), truncation
- Preview mode shows last 20 lines, Ctrl+O expands full output
- Commands persist in session history as bashExecution messages
- Add bash command to RPC mode via {type:'bash',command:'...'}
- Add RPC tests for bash command execution and context inclusion
- Update docs: rpc.md, session.md, README.md, CHANGELOG.md
Closes #112
Co-authored-by: Markus Ylisiurunen <markus.ylisiurunen@gmail.com>
This commit is contained in:
parent
1608da8770
commit
bd0d0676d4
13 changed files with 917 additions and 126 deletions
92
packages/coding-agent/test/fuzzy.test.ts
Normal file
92
packages/coding-agent/test/fuzzy.test.ts
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import { describe, expect, test } from "vitest";
|
||||
import { fuzzyFilter, fuzzyMatch } from "../src/fuzzy.js";
|
||||
|
||||
describe("fuzzyMatch", () => {
|
||||
test("empty query matches everything with score 0", () => {
|
||||
const result = fuzzyMatch("", "anything");
|
||||
expect(result.matches).toBe(true);
|
||||
expect(result.score).toBe(0);
|
||||
});
|
||||
|
||||
test("query longer than text does not match", () => {
|
||||
const result = fuzzyMatch("longquery", "short");
|
||||
expect(result.matches).toBe(false);
|
||||
});
|
||||
|
||||
test("exact match has good score", () => {
|
||||
const result = fuzzyMatch("test", "test");
|
||||
expect(result.matches).toBe(true);
|
||||
expect(result.score).toBeLessThan(0); // Should be negative due to consecutive bonuses
|
||||
});
|
||||
|
||||
test("characters must appear in order", () => {
|
||||
const matchInOrder = fuzzyMatch("abc", "aXbXc");
|
||||
expect(matchInOrder.matches).toBe(true);
|
||||
|
||||
const matchOutOfOrder = fuzzyMatch("abc", "cba");
|
||||
expect(matchOutOfOrder.matches).toBe(false);
|
||||
});
|
||||
|
||||
test("case insensitive matching", () => {
|
||||
const result = fuzzyMatch("ABC", "abc");
|
||||
expect(result.matches).toBe(true);
|
||||
|
||||
const result2 = fuzzyMatch("abc", "ABC");
|
||||
expect(result2.matches).toBe(true);
|
||||
});
|
||||
|
||||
test("consecutive matches score better than scattered matches", () => {
|
||||
const consecutive = fuzzyMatch("foo", "foobar");
|
||||
const scattered = fuzzyMatch("foo", "f_o_o_bar");
|
||||
|
||||
expect(consecutive.matches).toBe(true);
|
||||
expect(scattered.matches).toBe(true);
|
||||
expect(consecutive.score).toBeLessThan(scattered.score);
|
||||
});
|
||||
|
||||
test("word boundary matches score better", () => {
|
||||
const atBoundary = fuzzyMatch("fb", "foo-bar");
|
||||
const notAtBoundary = fuzzyMatch("fb", "afbx");
|
||||
|
||||
expect(atBoundary.matches).toBe(true);
|
||||
expect(notAtBoundary.matches).toBe(true);
|
||||
expect(atBoundary.score).toBeLessThan(notAtBoundary.score);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fuzzyFilter", () => {
|
||||
test("empty query returns all items unchanged", () => {
|
||||
const items = ["apple", "banana", "cherry"];
|
||||
const result = fuzzyFilter(items, "", (x) => x);
|
||||
expect(result).toEqual(items);
|
||||
});
|
||||
|
||||
test("filters out non-matching items", () => {
|
||||
const items = ["apple", "banana", "cherry"];
|
||||
const result = fuzzyFilter(items, "an", (x) => x);
|
||||
expect(result).toContain("banana");
|
||||
expect(result).not.toContain("apple");
|
||||
expect(result).not.toContain("cherry");
|
||||
});
|
||||
|
||||
test("sorts results by match quality", () => {
|
||||
const items = ["a_p_p", "app", "application"];
|
||||
const result = fuzzyFilter(items, "app", (x) => x);
|
||||
|
||||
// "app" should be first (exact consecutive match at start)
|
||||
expect(result[0]).toBe("app");
|
||||
});
|
||||
|
||||
test("works with custom getText function", () => {
|
||||
const items = [
|
||||
{ name: "foo", id: 1 },
|
||||
{ name: "bar", id: 2 },
|
||||
{ name: "foobar", id: 3 },
|
||||
];
|
||||
const result = fuzzyFilter(items, "foo", (item) => item.name);
|
||||
|
||||
expect(result.length).toBe(2);
|
||||
expect(result.map((r) => r.name)).toContain("foo");
|
||||
expect(result.map((r) => r.name)).toContain("foobar");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue