mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 06:04:40 +00:00
remove
This commit is contained in:
parent
0973c1cbc5
commit
88e7883051
188 changed files with 64 additions and 27581 deletions
|
|
@ -298,8 +298,46 @@ describe("extensions discovery", () => {
|
|||
});
|
||||
|
||||
it("resolves dependencies from extension's own node_modules", async () => {
|
||||
// Load extension that has its own package.json and node_modules with 'ms' package
|
||||
const extPath = path.resolve(__dirname, "../examples/extensions/with-deps");
|
||||
const extPath = path.join(tempDir, "custom-location", "with-deps");
|
||||
const nodeModulesDir = path.join(extPath, "node_modules", "ms");
|
||||
fs.mkdirSync(nodeModulesDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(extPath, "index.ts"),
|
||||
`
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import ms from "ms";
|
||||
export default function(pi) {
|
||||
pi.registerTool({
|
||||
name: "parse_duration",
|
||||
label: "parse_duration",
|
||||
description: "Parse a duration string",
|
||||
parameters: Type.Object({ value: Type.String() }),
|
||||
execute: async (_toolCallId, params) => ({
|
||||
content: [{ type: "text", text: String(ms(params.value)) }],
|
||||
}),
|
||||
});
|
||||
}
|
||||
`,
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(extPath, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "with-deps",
|
||||
type: "module",
|
||||
}),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(nodeModulesDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "ms",
|
||||
type: "module",
|
||||
exports: "./index.js",
|
||||
}),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(nodeModulesDir, "index.js"),
|
||||
`export default function ms(value) { return value === "1m" ? 60000 : 0; }`,
|
||||
);
|
||||
|
||||
const result = await discoverAndLoadExtensions([extPath], tempDir, tempDir);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,261 +0,0 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
cleanStepText,
|
||||
extractDoneSteps,
|
||||
extractTodoItems,
|
||||
isSafeCommand,
|
||||
markCompletedSteps,
|
||||
type TodoItem,
|
||||
} from "../examples/extensions/plan-mode/utils.js";
|
||||
|
||||
describe("isSafeCommand", () => {
|
||||
describe("safe commands", () => {
|
||||
it("allows basic read commands", () => {
|
||||
expect(isSafeCommand("ls -la")).toBe(true);
|
||||
expect(isSafeCommand("cat file.txt")).toBe(true);
|
||||
expect(isSafeCommand("head -n 10 file.txt")).toBe(true);
|
||||
expect(isSafeCommand("tail -f log.txt")).toBe(true);
|
||||
expect(isSafeCommand("grep pattern file")).toBe(true);
|
||||
expect(isSafeCommand("find . -name '*.ts'")).toBe(true);
|
||||
});
|
||||
|
||||
it("allows git read commands", () => {
|
||||
expect(isSafeCommand("git status")).toBe(true);
|
||||
expect(isSafeCommand("git log --oneline")).toBe(true);
|
||||
expect(isSafeCommand("git diff")).toBe(true);
|
||||
expect(isSafeCommand("git branch")).toBe(true);
|
||||
});
|
||||
|
||||
it("allows npm/yarn read commands", () => {
|
||||
expect(isSafeCommand("npm list")).toBe(true);
|
||||
expect(isSafeCommand("npm outdated")).toBe(true);
|
||||
expect(isSafeCommand("yarn info react")).toBe(true);
|
||||
});
|
||||
|
||||
it("allows other safe commands", () => {
|
||||
expect(isSafeCommand("pwd")).toBe(true);
|
||||
expect(isSafeCommand("echo hello")).toBe(true);
|
||||
expect(isSafeCommand("wc -l file.txt")).toBe(true);
|
||||
expect(isSafeCommand("du -sh .")).toBe(true);
|
||||
expect(isSafeCommand("df -h")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("destructive commands", () => {
|
||||
it("blocks file modification commands", () => {
|
||||
expect(isSafeCommand("rm file.txt")).toBe(false);
|
||||
expect(isSafeCommand("rm -rf dir")).toBe(false);
|
||||
expect(isSafeCommand("mv old new")).toBe(false);
|
||||
expect(isSafeCommand("cp src dst")).toBe(false);
|
||||
expect(isSafeCommand("mkdir newdir")).toBe(false);
|
||||
expect(isSafeCommand("touch newfile")).toBe(false);
|
||||
});
|
||||
|
||||
it("blocks git write commands", () => {
|
||||
expect(isSafeCommand("git add .")).toBe(false);
|
||||
expect(isSafeCommand("git commit -m 'msg'")).toBe(false);
|
||||
expect(isSafeCommand("git push")).toBe(false);
|
||||
expect(isSafeCommand("git checkout main")).toBe(false);
|
||||
expect(isSafeCommand("git reset --hard")).toBe(false);
|
||||
});
|
||||
|
||||
it("blocks package manager installs", () => {
|
||||
expect(isSafeCommand("npm install lodash")).toBe(false);
|
||||
expect(isSafeCommand("yarn add react")).toBe(false);
|
||||
expect(isSafeCommand("pip install requests")).toBe(false);
|
||||
expect(isSafeCommand("brew install node")).toBe(false);
|
||||
});
|
||||
|
||||
it("blocks redirects", () => {
|
||||
expect(isSafeCommand("echo hello > file.txt")).toBe(false);
|
||||
expect(isSafeCommand("cat foo >> bar")).toBe(false);
|
||||
expect(isSafeCommand(">file.txt")).toBe(false);
|
||||
});
|
||||
|
||||
it("blocks dangerous commands", () => {
|
||||
expect(isSafeCommand("sudo rm -rf /")).toBe(false);
|
||||
expect(isSafeCommand("kill -9 1234")).toBe(false);
|
||||
expect(isSafeCommand("reboot")).toBe(false);
|
||||
});
|
||||
|
||||
it("blocks editors", () => {
|
||||
expect(isSafeCommand("vim file.txt")).toBe(false);
|
||||
expect(isSafeCommand("nano file.txt")).toBe(false);
|
||||
expect(isSafeCommand("code .")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("requires command to be in safe list (not just non-destructive)", () => {
|
||||
expect(isSafeCommand("unknown-command")).toBe(false);
|
||||
expect(isSafeCommand("my-script.sh")).toBe(false);
|
||||
});
|
||||
|
||||
it("handles commands with leading whitespace", () => {
|
||||
expect(isSafeCommand(" ls -la")).toBe(true);
|
||||
expect(isSafeCommand(" rm file")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("cleanStepText", () => {
|
||||
it("removes markdown bold/italic", () => {
|
||||
expect(cleanStepText("**bold text**")).toBe("Bold text");
|
||||
expect(cleanStepText("*italic text*")).toBe("Italic text");
|
||||
});
|
||||
|
||||
it("removes markdown code", () => {
|
||||
expect(cleanStepText("run `npm install`")).toBe("Npm install"); // "run" is stripped as action word
|
||||
expect(cleanStepText("check the `config.json` file")).toBe("Config.json file");
|
||||
});
|
||||
|
||||
it("removes leading action words", () => {
|
||||
expect(cleanStepText("Create the new file")).toBe("New file");
|
||||
expect(cleanStepText("Run the tests")).toBe("Tests");
|
||||
expect(cleanStepText("Check the status")).toBe("Status");
|
||||
});
|
||||
|
||||
it("capitalizes first letter", () => {
|
||||
expect(cleanStepText("update config")).toBe("Config");
|
||||
});
|
||||
|
||||
it("truncates long text", () => {
|
||||
const longText = "This is a very long step description that exceeds the maximum allowed length for display";
|
||||
const result = cleanStepText(longText);
|
||||
expect(result.length).toBe(50);
|
||||
expect(result.endsWith("...")).toBe(true);
|
||||
});
|
||||
|
||||
it("normalizes whitespace", () => {
|
||||
expect(cleanStepText("multiple spaces here")).toBe("Multiple spaces here");
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractTodoItems", () => {
|
||||
it("extracts numbered items after Plan: header", () => {
|
||||
const message = `Here's what we'll do:
|
||||
|
||||
Plan:
|
||||
1. First step here
|
||||
2. Second step here
|
||||
3. Third step here`;
|
||||
|
||||
const items = extractTodoItems(message);
|
||||
expect(items).toHaveLength(3);
|
||||
expect(items[0].step).toBe(1);
|
||||
expect(items[0].text).toBe("First step here");
|
||||
expect(items[0].completed).toBe(false);
|
||||
});
|
||||
|
||||
it("handles bold Plan header", () => {
|
||||
const message = `**Plan:**
|
||||
1. Do something`;
|
||||
|
||||
const items = extractTodoItems(message);
|
||||
expect(items).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("handles parenthesis-style numbering", () => {
|
||||
const message = `Plan:
|
||||
1) First item
|
||||
2) Second item`;
|
||||
|
||||
const items = extractTodoItems(message);
|
||||
expect(items).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("returns empty array without Plan header", () => {
|
||||
const message = `Here are some steps:
|
||||
1. First step
|
||||
2. Second step`;
|
||||
|
||||
const items = extractTodoItems(message);
|
||||
expect(items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("filters out short items", () => {
|
||||
const message = `Plan:
|
||||
1. OK
|
||||
2. This is a proper step`;
|
||||
|
||||
const items = extractTodoItems(message);
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0].text).toContain("proper");
|
||||
});
|
||||
|
||||
it("filters out code-like items", () => {
|
||||
const message = `Plan:
|
||||
1. \`npm install\`
|
||||
2. Run the build process`;
|
||||
|
||||
const items = extractTodoItems(message);
|
||||
expect(items).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractDoneSteps", () => {
|
||||
it("extracts single DONE marker", () => {
|
||||
const message = "I've completed the first step [DONE:1]";
|
||||
expect(extractDoneSteps(message)).toEqual([1]);
|
||||
});
|
||||
|
||||
it("extracts multiple DONE markers", () => {
|
||||
const message = "Did steps [DONE:1] and [DONE:2] and [DONE:3]";
|
||||
expect(extractDoneSteps(message)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("handles case insensitivity", () => {
|
||||
const message = "[done:1] [DONE:2] [Done:3]";
|
||||
expect(extractDoneSteps(message)).toEqual([1, 2, 3]);
|
||||
});
|
||||
|
||||
it("returns empty array with no markers", () => {
|
||||
const message = "No markers here";
|
||||
expect(extractDoneSteps(message)).toEqual([]);
|
||||
});
|
||||
|
||||
it("ignores malformed markers", () => {
|
||||
const message = "[DONE:abc] [DONE:] [DONE:1]";
|
||||
expect(extractDoneSteps(message)).toEqual([1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("markCompletedSteps", () => {
|
||||
it("marks matching items as completed", () => {
|
||||
const items: TodoItem[] = [
|
||||
{ step: 1, text: "First", completed: false },
|
||||
{ step: 2, text: "Second", completed: false },
|
||||
{ step: 3, text: "Third", completed: false },
|
||||
];
|
||||
|
||||
const count = markCompletedSteps("[DONE:1] [DONE:3]", items);
|
||||
|
||||
expect(count).toBe(2);
|
||||
expect(items[0].completed).toBe(true);
|
||||
expect(items[1].completed).toBe(false);
|
||||
expect(items[2].completed).toBe(true);
|
||||
});
|
||||
|
||||
it("returns count of completed items", () => {
|
||||
const items: TodoItem[] = [{ step: 1, text: "First", completed: false }];
|
||||
|
||||
expect(markCompletedSteps("[DONE:1]", items)).toBe(1);
|
||||
expect(markCompletedSteps("no markers", items)).toBe(0);
|
||||
});
|
||||
|
||||
it("ignores markers for non-existent steps", () => {
|
||||
const items: TodoItem[] = [{ step: 1, text: "First", completed: false }];
|
||||
|
||||
const count = markCompletedSteps("[DONE:99]", items);
|
||||
|
||||
expect(count).toBe(1); // Still counts the marker found
|
||||
expect(items[0].completed).toBe(false); // But doesn't mark anything
|
||||
});
|
||||
|
||||
it("doesn't double-complete already completed items", () => {
|
||||
const items: TodoItem[] = [{ step: 1, text: "First", completed: true }];
|
||||
|
||||
markCompletedSteps("[DONE:1]", items);
|
||||
expect(items[0].completed).toBe(true);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue