pretticlaw 0.1.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.
- package/CONTRIBUTING.md +123 -0
- package/README.md +150 -0
- package/assets/logo.png +0 -0
- package/dist/agent/context.d.ts +22 -0
- package/dist/agent/context.js +85 -0
- package/dist/agent/loop.d.ts +63 -0
- package/dist/agent/loop.js +244 -0
- package/dist/agent/memory.d.ts +16 -0
- package/dist/agent/memory.js +98 -0
- package/dist/agent/skills.d.ts +18 -0
- package/dist/agent/skills.js +121 -0
- package/dist/agent/subagent.d.ts +30 -0
- package/dist/agent/subagent.js +92 -0
- package/dist/agent/tools/base.d.ts +10 -0
- package/dist/agent/tools/base.js +58 -0
- package/dist/agent/tools/cron.d.ts +43 -0
- package/dist/agent/tools/cron.js +83 -0
- package/dist/agent/tools/filesystem.d.ts +79 -0
- package/dist/agent/tools/filesystem.js +125 -0
- package/dist/agent/tools/message.d.ts +41 -0
- package/dist/agent/tools/message.js +55 -0
- package/dist/agent/tools/registry.d.ts +9 -0
- package/dist/agent/tools/registry.js +33 -0
- package/dist/agent/tools/shell.d.ts +26 -0
- package/dist/agent/tools/shell.js +78 -0
- package/dist/agent/tools/spawn.d.ts +27 -0
- package/dist/agent/tools/spawn.js +35 -0
- package/dist/agent/tools/web.d.ts +50 -0
- package/dist/agent/tools/web.js +119 -0
- package/dist/bus/async-queue.d.ts +7 -0
- package/dist/bus/async-queue.js +20 -0
- package/dist/bus/events.d.ts +19 -0
- package/dist/bus/events.js +3 -0
- package/dist/bus/queue.d.ts +12 -0
- package/dist/bus/queue.js +23 -0
- package/dist/channels/base.d.ts +22 -0
- package/dist/channels/base.js +35 -0
- package/dist/channels/discord.d.ts +24 -0
- package/dist/channels/discord.js +133 -0
- package/dist/channels/manager.d.ts +17 -0
- package/dist/channels/manager.js +67 -0
- package/dist/channels/stub.d.ts +10 -0
- package/dist/channels/stub.js +18 -0
- package/dist/channels/telegram.d.ts +20 -0
- package/dist/channels/telegram.js +93 -0
- package/dist/cli/commands.d.ts +2 -0
- package/dist/cli/commands.js +552 -0
- package/dist/config/loader.d.ts +5 -0
- package/dist/config/loader.js +55 -0
- package/dist/config/schema.d.ts +246 -0
- package/dist/config/schema.js +94 -0
- package/dist/cron/service.d.ts +33 -0
- package/dist/cron/service.js +195 -0
- package/dist/cron/types.d.ts +47 -0
- package/dist/cron/types.js +1 -0
- package/dist/dashboard/index.html +1567 -0
- package/dist/heartbeat/service.d.ts +21 -0
- package/dist/heartbeat/service.js +101 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/providers/base.d.ts +23 -0
- package/dist/providers/base.js +21 -0
- package/dist/providers/custom-provider.d.ts +16 -0
- package/dist/providers/custom-provider.js +49 -0
- package/dist/providers/litellm-provider.d.ts +19 -0
- package/dist/providers/litellm-provider.js +128 -0
- package/dist/providers/registry.d.ts +5 -0
- package/dist/providers/registry.js +45 -0
- package/dist/session/manager.d.ts +31 -0
- package/dist/session/manager.js +116 -0
- package/dist/skills/README.md +25 -0
- package/dist/skills/clawhub/SKILL.md +53 -0
- package/dist/skills/cron/SKILL.md +57 -0
- package/dist/skills/github/SKILL.md +48 -0
- package/dist/skills/memory/SKILL.md +31 -0
- package/dist/skills/skill-creator/SKILL.md +371 -0
- package/dist/skills/summarize/SKILL.md +67 -0
- package/dist/skills/tmux/SKILL.md +121 -0
- package/dist/skills/tmux/scripts/find-sessions.sh +112 -0
- package/dist/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/dist/skills/weather/SKILL.md +49 -0
- package/dist/templates/AGENTS.md +23 -0
- package/dist/templates/HEARTBEAT.md +16 -0
- package/dist/templates/SOUL.md +21 -0
- package/dist/templates/TOOLS.md +15 -0
- package/dist/templates/USER.md +49 -0
- package/dist/templates/memory/MEMORY.md +23 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +3 -0
- package/dist/utils/helpers.d.ts +5 -0
- package/dist/utils/helpers.js +53 -0
- package/dist/web/server.d.ts +15 -0
- package/dist/web/server.js +169 -0
- package/package.json +37 -0
- package/scripts/copy-assets.mjs +21 -0
- package/src/agent/context.ts +90 -0
- package/src/agent/loop.ts +291 -0
- package/src/agent/memory.ts +104 -0
- package/src/agent/skills.ts +121 -0
- package/src/agent/subagent.ts +96 -0
- package/src/agent/tools/base.ts +59 -0
- package/src/agent/tools/cron.ts +79 -0
- package/src/agent/tools/filesystem.ts +93 -0
- package/src/agent/tools/message.ts +57 -0
- package/src/agent/tools/registry.ts +36 -0
- package/src/agent/tools/shell.ts +69 -0
- package/src/agent/tools/spawn.ts +37 -0
- package/src/agent/tools/web.ts +108 -0
- package/src/bus/async-queue.ts +20 -0
- package/src/bus/events.ts +23 -0
- package/src/bus/queue.ts +31 -0
- package/src/channels/base.ts +36 -0
- package/src/channels/discord.ts +156 -0
- package/src/channels/manager.ts +70 -0
- package/src/channels/stub.ts +20 -0
- package/src/channels/telegram.ts +120 -0
- package/src/cli/commands.ts +581 -0
- package/src/config/loader.ts +58 -0
- package/src/config/schema.ts +144 -0
- package/src/cron/service.ts +190 -0
- package/src/cron/types.ts +36 -0
- package/src/dashboard/index.html +1567 -0
- package/src/heartbeat/service.ts +95 -0
- package/src/index.ts +6 -0
- package/src/providers/base.ts +43 -0
- package/src/providers/custom-provider.ts +46 -0
- package/src/providers/litellm-provider.ts +131 -0
- package/src/providers/registry.ts +48 -0
- package/src/session/manager.ts +129 -0
- package/src/skills/README.md +25 -0
- package/src/skills/clawhub/SKILL.md +53 -0
- package/src/skills/cron/SKILL.md +57 -0
- package/src/skills/github/SKILL.md +48 -0
- package/src/skills/memory/SKILL.md +31 -0
- package/src/skills/skill-creator/SKILL.md +371 -0
- package/src/skills/summarize/SKILL.md +67 -0
- package/src/skills/tmux/SKILL.md +121 -0
- package/src/skills/tmux/scripts/find-sessions.sh +112 -0
- package/src/skills/tmux/scripts/wait-for-text.sh +83 -0
- package/src/skills/weather/SKILL.md +49 -0
- package/src/templates/AGENTS.md +23 -0
- package/src/templates/HEARTBEAT.md +16 -0
- package/src/templates/SOUL.md +21 -0
- package/src/templates/TOOLS.md +15 -0
- package/src/templates/USER.md +49 -0
- package/src/templates/memory/MEMORY.md +23 -0
- package/src/types/prompts.d.ts +14 -0
- package/src/types/ws.d.ts +15 -0
- package/src/types.ts +5 -0
- package/src/utils/helpers.ts +55 -0
- package/src/web/server.ts +198 -0
- package/test/context.test.ts +27 -0
- package/test/cron-service.test.ts +31 -0
- package/test/message-tool.test.ts +10 -0
- package/test/providers.test.ts +43 -0
- package/test/tool-validation.test.ts +61 -0
- package/tsconfig.json +16 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
5
|
+
import { ContextBuilder } from "../src/agent/context.js";
|
|
6
|
+
|
|
7
|
+
describe("context runtime separation", () => {
|
|
8
|
+
test("runtime context is separate untrusted user message", () => {
|
|
9
|
+
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), "pretticlaw-ctx-"));
|
|
10
|
+
fs.writeFileSync(path.join(workspace, "AGENTS.md"), "agent", "utf8");
|
|
11
|
+
const ctx = new ContextBuilder(workspace);
|
|
12
|
+
const messages = ctx.buildMessages({ history: [], currentMessage: "Return exactly: OK", channel: "cli", chatId: "direct" });
|
|
13
|
+
|
|
14
|
+
expect(messages[0].role).toBe("system");
|
|
15
|
+
expect(String(messages[0].content)).not.toContain("## Current Session");
|
|
16
|
+
|
|
17
|
+
const runtimeContent = String(messages[messages.length - 2].content);
|
|
18
|
+
expect(messages[messages.length - 2].role).toBe("user");
|
|
19
|
+
expect(runtimeContent).toContain(ContextBuilder.RUNTIME_CONTEXT_TAG);
|
|
20
|
+
expect(runtimeContent).toContain("Current Time:");
|
|
21
|
+
expect(runtimeContent).toContain("Channel: cli");
|
|
22
|
+
expect(runtimeContent).toContain("Chat ID: direct");
|
|
23
|
+
|
|
24
|
+
expect(messages[messages.length - 1].role).toBe("user");
|
|
25
|
+
expect(messages[messages.length - 1].content).toBe("Return exactly: OK");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
5
|
+
import { CronService } from "../src/cron/service.js";
|
|
6
|
+
|
|
7
|
+
describe("cron timezone", () => {
|
|
8
|
+
test("rejects unknown timezone", () => {
|
|
9
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pretticlaw-cron-"));
|
|
10
|
+
const service = new CronService(path.join(dir, "jobs.json"));
|
|
11
|
+
expect(() => {
|
|
12
|
+
service.addJob({
|
|
13
|
+
name: "bad",
|
|
14
|
+
schedule: { kind: "cron", expr: "0 9 * * *", tz: "America/Vancovuer" },
|
|
15
|
+
message: "hello",
|
|
16
|
+
});
|
|
17
|
+
}).toThrow("unknown timezone 'America/Vancovuer'");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("accepts valid timezone", () => {
|
|
21
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pretticlaw-cron-"));
|
|
22
|
+
const service = new CronService(path.join(dir, "jobs.json"));
|
|
23
|
+
const job = service.addJob({
|
|
24
|
+
name: "good",
|
|
25
|
+
schedule: { kind: "cron", expr: "0 9 * * *", tz: "America/Vancouver" },
|
|
26
|
+
message: "hello",
|
|
27
|
+
});
|
|
28
|
+
expect((job.schedule as any).tz).toBe("America/Vancouver");
|
|
29
|
+
expect(job.state.nextRunAtMs).not.toBeNull();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { MessageTool } from "../src/agent/tools/message.js";
|
|
3
|
+
|
|
4
|
+
describe("message tool", () => {
|
|
5
|
+
test("returns error when no target context", async () => {
|
|
6
|
+
const tool = new MessageTool();
|
|
7
|
+
const result = await tool.execute({ content: "test" });
|
|
8
|
+
expect(result).toBe("Error: No target channel/chat specified");
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { DEFAULT_CONFIG, getProviderName } from "../src/config/schema.js";
|
|
3
|
+
import { findByModel } from "../src/providers/registry.js";
|
|
4
|
+
import { LiteLLMProvider } from "../src/providers/litellm-provider.js";
|
|
5
|
+
import { stripModelPrefix } from "../src/providers/registry.js";
|
|
6
|
+
|
|
7
|
+
describe("provider matching", () => {
|
|
8
|
+
test("matches github copilot with hyphen prefix", () => {
|
|
9
|
+
const config = structuredClone(DEFAULT_CONFIG);
|
|
10
|
+
config.agents.defaults.model = "github-copilot/gpt-5.3-codex";
|
|
11
|
+
config.providers.github_copilot.apiKey = "oauth";
|
|
12
|
+
expect(getProviderName(config)).toBe("github_copilot");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("matches openai codex with hyphen prefix", () => {
|
|
16
|
+
const config = structuredClone(DEFAULT_CONFIG);
|
|
17
|
+
config.agents.defaults.model = "openai-codex/gpt-5.1-codex";
|
|
18
|
+
config.providers.openai_codex.apiKey = "oauth";
|
|
19
|
+
expect(getProviderName(config)).toBe("openai_codex");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("find by model prefers explicit prefix", () => {
|
|
23
|
+
const spec = findByModel("github-copilot/gpt-5.3-codex");
|
|
24
|
+
expect(spec?.name).toBe("github_copilot");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("canonicalizes github copilot hyphen prefix", () => {
|
|
28
|
+
const provider = new LiteLLMProvider(null, null, "github-copilot/gpt-5.3-codex", null);
|
|
29
|
+
expect(provider.resolveModel("github-copilot/gpt-5.3-codex")).toBe("github_copilot/gpt-5.3-codex");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("strips groq/ prefix for groq models", () => {
|
|
33
|
+
const provider = new LiteLLMProvider("key", "https://api.groq.com/openai/v1", "llama-3.3-70b-versatile", "groq");
|
|
34
|
+
expect(provider.resolveModel("groq/llama-3.3-70b-versatile")).toBe("llama-3.3-70b-versatile");
|
|
35
|
+
expect(provider.resolveModel("openai/gpt-oss-120b")).toBe("openai/gpt-oss-120b");
|
|
36
|
+
expect(provider.resolveModel("groq/compound")).toBe("groq/compound");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("openai codex strip prefix supports hyphen and underscore", () => {
|
|
40
|
+
expect(stripModelPrefix("openai-codex/gpt-5.1-codex")).toBe("gpt-5.1-codex");
|
|
41
|
+
expect(stripModelPrefix("openai_codex/gpt-5.1-codex")).toBe("gpt-5.1-codex");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { Tool } from "../src/agent/tools/base.js";
|
|
3
|
+
import { ToolRegistry } from "../src/agent/tools/registry.js";
|
|
4
|
+
|
|
5
|
+
class SampleTool extends Tool {
|
|
6
|
+
readonly name = "sample";
|
|
7
|
+
readonly description = "sample tool";
|
|
8
|
+
readonly parameters = {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
query: { type: "string", minLength: 2 },
|
|
12
|
+
count: { type: "integer", minimum: 1, maximum: 10 },
|
|
13
|
+
mode: { type: "string", enum: ["fast", "full"] },
|
|
14
|
+
meta: {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
tag: { type: "string" },
|
|
18
|
+
flags: { type: "array", items: { type: "string" } },
|
|
19
|
+
},
|
|
20
|
+
required: ["tag"],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["query", "count"],
|
|
24
|
+
};
|
|
25
|
+
async execute(): Promise<string> { return "ok"; }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("tool validation", () => {
|
|
29
|
+
test("missing required", () => {
|
|
30
|
+
const tool = new SampleTool();
|
|
31
|
+
const errors = tool.validateParams({ query: "hi" });
|
|
32
|
+
expect(errors.join("; ")).toContain("missing required count");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("type and range", () => {
|
|
36
|
+
const tool = new SampleTool();
|
|
37
|
+
expect(tool.validateParams({ query: "hi", count: 0 }).some((e) => e.includes("count must be >= 1"))).toBe(true);
|
|
38
|
+
expect(tool.validateParams({ query: "hi", count: "2" as any }).some((e) => e.includes("count should be integer"))).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("enum and min length", () => {
|
|
42
|
+
const tool = new SampleTool();
|
|
43
|
+
const errors = tool.validateParams({ query: "h", count: 2, mode: "slow" });
|
|
44
|
+
expect(errors.some((e) => e.includes("query must be at least 2 chars"))).toBe(true);
|
|
45
|
+
expect(errors.some((e) => e.includes("mode must be one of"))).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("nested object and array", () => {
|
|
49
|
+
const tool = new SampleTool();
|
|
50
|
+
const errors = tool.validateParams({ query: "hi", count: 2, meta: { flags: [1, "ok"] } });
|
|
51
|
+
expect(errors.some((e) => e.includes("missing required meta.tag"))).toBe(true);
|
|
52
|
+
expect(errors.some((e) => e.includes("meta.flags[0] should be string"))).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("registry returns validation error", async () => {
|
|
56
|
+
const reg = new ToolRegistry();
|
|
57
|
+
reg.register(new SampleTool());
|
|
58
|
+
const result = await reg.execute("sample", { query: "hi" });
|
|
59
|
+
expect(result).toContain("Invalid parameters");
|
|
60
|
+
});
|
|
61
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"outDir": "dist",
|
|
9
|
+
"rootDir": "src",
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"resolveJsonModule": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*.ts", "src/**/*.d.ts"]
|
|
16
|
+
}
|