@archon-claw/cli 0.0.4 → 0.2.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.
@@ -3,10 +3,11 @@ const liquid = new Liquid({ strictVariables: false, strictFilters: true });
3
3
  export const systemPromptPlugin = {
4
4
  name: "system-prompt",
5
5
  pattern: "system-prompt.md",
6
+ required: true,
6
7
  validate(content, _fileName) {
7
8
  const errors = [];
8
9
  if (!content.trim()) {
9
- errors.push("System prompt content is empty");
10
+ errors.push({ message: "System prompt content is empty", severity: "error" });
10
11
  return { valid: false, errors };
11
12
  }
12
13
  // Validate Liquid template syntax if template tags are present
@@ -17,7 +18,7 @@ export const systemPromptPlugin = {
17
18
  }
18
19
  catch (e) {
19
20
  const msg = e instanceof Error ? e.message : String(e);
20
- errors.push(`Liquid template syntax error: ${msg}`);
21
+ errors.push({ message: `Liquid template syntax error: ${msg}`, severity: "error" });
21
22
  }
22
23
  }
23
24
  return { valid: errors.length === 0, errors };
@@ -9,11 +9,11 @@ export const toolPlugin = {
9
9
  data = JSON.parse(content);
10
10
  }
11
11
  catch {
12
- return { valid: false, errors: ["Invalid JSON"] };
12
+ return { valid: false, errors: [{ message: "Invalid JSON", severity: "error" }] };
13
13
  }
14
14
  const result = toolSchemaSchema.safeParse(data);
15
15
  if (!result.success) {
16
- return { valid: false, errors: formatZodErrors(result.error) };
16
+ return { valid: false, errors: formatZodErrors(result.error, data, content) };
17
17
  }
18
18
  return { valid: true, errors: [] };
19
19
  },
@@ -1,3 +1,10 @@
1
1
  import type { ZodError } from "zod";
2
- /** Format Zod errors to match the AJV-style output: `/{path} {message}` */
3
- export declare function formatZodErrors(error: ZodError): string[];
2
+ import type { ValidationError } from "./plugin.js";
3
+ /**
4
+ * Given a raw JSON string, find the 1-based line number where a key at
5
+ * the given path (e.g. ["provider"] or ["input_schema", "type"]) appears.
6
+ * Returns undefined if the key cannot be located.
7
+ */
8
+ export declare function getLineForPath(jsonString: string, pathSegments: (string | number)[]): number | undefined;
9
+ /** Format Zod errors into structured ValidationError objects */
10
+ export declare function formatZodErrors(error: ZodError, data?: unknown, rawJson?: string): ValidationError[];
@@ -1,7 +1,96 @@
1
- /** Format Zod errors to match the AJV-style output: `/{path} {message}` */
2
- export function formatZodErrors(error) {
1
+ /** Traverse an object using a path array to get the value at that path */
2
+ function getValueAtPath(data, path) {
3
+ let current = data;
4
+ for (const key of path) {
5
+ if (current === null || current === undefined)
6
+ return undefined;
7
+ current = current[key];
8
+ }
9
+ return current;
10
+ }
11
+ /** Extract expected/received from a Zod issue based on its code */
12
+ function extractDetails(issue, data) {
13
+ const result = {};
14
+ switch (issue.code) {
15
+ case "invalid_enum_value":
16
+ result.expected = issue.options?.join(" | ");
17
+ result.received = String(issue.received);
18
+ break;
19
+ case "too_big":
20
+ result.expected = `<= ${issue.maximum}`;
21
+ result.received = String(issue.value ?? "");
22
+ break;
23
+ case "too_small":
24
+ result.expected = `>= ${issue.minimum}`;
25
+ result.received = String(issue.value ?? "");
26
+ break;
27
+ case "invalid_string": {
28
+ const validation = issue.validation;
29
+ if (typeof validation === "object" && validation !== null && "regex" in validation) {
30
+ result.expected = `regex: ${validation.regex}`;
31
+ }
32
+ else {
33
+ result.expected = `${validation}`;
34
+ }
35
+ // Zod doesn't include received for invalid_string — look up from original data
36
+ const issueReceived = issue.received;
37
+ if (issueReceived !== undefined) {
38
+ result.received = String(issueReceived);
39
+ }
40
+ else if (data !== undefined) {
41
+ const val = getValueAtPath(data, issue.path);
42
+ if (val !== undefined) {
43
+ result.received = String(val);
44
+ }
45
+ }
46
+ break;
47
+ }
48
+ case "invalid_type":
49
+ result.expected = String(issue.expected);
50
+ result.received = String(issue.received);
51
+ break;
52
+ case "unrecognized_keys":
53
+ result.received = issue.keys?.join(", ");
54
+ break;
55
+ }
56
+ return result;
57
+ }
58
+ /**
59
+ * Given a raw JSON string, find the 1-based line number where a key at
60
+ * the given path (e.g. ["provider"] or ["input_schema", "type"]) appears.
61
+ * Returns undefined if the key cannot be located.
62
+ */
63
+ export function getLineForPath(jsonString, pathSegments) {
64
+ if (pathSegments.length === 0)
65
+ return undefined;
66
+ const lastKey = pathSegments[pathSegments.length - 1];
67
+ if (typeof lastKey === "number")
68
+ return undefined;
69
+ // Build a regex that matches `"key":` — we search for the last segment
70
+ const escaped = lastKey.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
71
+ const pattern = new RegExp(`"${escaped}"\\s*:`);
72
+ const lines = jsonString.split("\n");
73
+ // For simple (flat) paths, find the first occurrence
74
+ // For nested paths, we'd need smarter logic, but for typical JSON configs this works
75
+ for (let i = 0; i < lines.length; i++) {
76
+ if (pattern.test(lines[i])) {
77
+ return i + 1; // 1-based
78
+ }
79
+ }
80
+ return undefined;
81
+ }
82
+ /** Format Zod errors into structured ValidationError objects */
83
+ export function formatZodErrors(error, data, rawJson) {
3
84
  return error.issues.map((issue) => {
4
85
  const path = issue.path.length > 0 ? `/${issue.path.join("/")}` : "/";
5
- return `${path} ${issue.message}`;
86
+ const details = extractDetails(issue, data);
87
+ const line = rawJson ? getLineForPath(rawJson, issue.path) : undefined;
88
+ return {
89
+ message: `${path} ${issue.message}`,
90
+ severity: "error",
91
+ path,
92
+ ...details,
93
+ ...(line !== undefined ? { line } : {}),
94
+ };
6
95
  });
7
96
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archon-claw/cli",
3
- "version": "0.0.4",
3
+ "version": "0.2.0",
4
4
  "description": "AI Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,10 +8,12 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc -p tsconfig.build.json && cp -r templates dist/ && rm -rf dist/public && cp -r ../web/dist dist/public",
11
- "dev": "tsx src/cli.ts start ../../agents/my-agent",
11
+ "dev": "tsx src/cli.ts start examples/my-agent",
12
12
  "start": "node dist/cli.js",
13
- "test": "vitest run --exclude dist",
14
- "test:watch": "vitest --exclude dist"
13
+ "test": "vitest run --exclude dist --exclude examples",
14
+ "test:watch": "vitest --exclude dist --exclude examples",
15
+ "e2e": "playwright test",
16
+ "e2e:ui": "playwright test --ui"
15
17
  },
16
18
  "files": [
17
19
  "dist"
@@ -21,6 +23,8 @@
21
23
  },
22
24
  "license": "MIT",
23
25
  "devDependencies": {
26
+ "@archon-claw/web": "workspace:*",
27
+ "@playwright/test": "^1.58.2",
24
28
  "@types/node": "^25.3.3",
25
29
  "@types/picomatch": "^4.0.2",
26
30
  "@vitest/coverage-v8": "^4.0.18",
@@ -29,14 +33,17 @@
29
33
  "vitest": "^4.0.18"
30
34
  },
31
35
  "dependencies": {
32
- "@archon-claw/web": "workspace:*",
36
+ "@archon-claw/skills": "workspace:*",
37
+ "@clack/prompts": "^1.1.0",
33
38
  "@modelcontextprotocol/sdk": "^1.27.1",
39
+ "chokidar": "^5.0.0",
34
40
  "commander": "^14.0.3",
35
41
  "dotenv": "^17.3.1",
36
42
  "gray-matter": "^4.0.3",
37
43
  "liquidjs": "^10.24.0",
44
+ "open": "^10.2.0",
38
45
  "openai": "^6.25.0",
39
46
  "picomatch": "^4.0.3",
40
- "zod": "^3.24.0"
47
+ "zod": "^3.25.76"
41
48
  }
42
49
  }
@@ -1 +0,0 @@
1
- import{u as r,r as t,i as a,j as e,b as c,C as i,X as d,d as l}from"./chat-input-Cx9bd-IU.js";function x(){return e.jsxs("div",{className:"flex h-10 shrink-0 items-center justify-between border-b px-3",children:[e.jsx("span",{className:"text-sm font-semibold",children:"Archon Claw"}),e.jsx("button",{type:"button",className:"text-muted-foreground hover:bg-accent rounded p-1 transition-colors",onClick:()=>window.parent.postMessage({type:"archon-claw:request-close"},"*"),children:e.jsx(d,{className:"h-4 w-4"})})]})}function u(){const n=r(s=>s.createSession),o=r(s=>s.activeSession());return t.useEffect(()=>{o||n()},[o,n]),t.useEffect(()=>{a()},[]),e.jsxs("div",{className:"flex h-dvh flex-col bg-background text-foreground",children:[e.jsx(x,{}),e.jsx(c,{}),e.jsx(i,{})]})}l.createRoot(document.getElementById("embed-root")).render(e.jsx(t.StrictMode,{children:e.jsx(u,{})}));
@@ -1,16 +0,0 @@
1
- import{c as x,r as h,j as e,a as d,u as t,B as i,X as f,M as p,b,C as j,d as y}from"./chat-input-Cx9bd-IU.js";/**
2
- * @license lucide-react v0.469.0 - ISC
3
- *
4
- * This source code is licensed under the ISC license.
5
- * See the LICENSE file in the root directory of this source tree.
6
- */const v=x("Menu",[["line",{x1:"4",x2:"20",y1:"12",y2:"12",key:"1e0a9i"}],["line",{x1:"4",x2:"20",y1:"6",y2:"6",key:"1owob3"}],["line",{x1:"4",x2:"20",y1:"18",y2:"18",key:"yk5zj1"}]]);/**
7
- * @license lucide-react v0.469.0 - ISC
8
- *
9
- * This source code is licensed under the ISC license.
10
- * See the LICENSE file in the root directory of this source tree.
11
- */const g=x("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);/**
12
- * @license lucide-react v0.469.0 - ISC
13
- *
14
- * This source code is licensed under the ISC license.
15
- * See the LICENSE file in the root directory of this source tree.
16
- */const N=x("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);function w({content:a,children:o,side:n="top"}){const[r,l]=h.useState(!1),c={top:"bottom-full left-1/2 -translate-x-1/2 mb-2",bottom:"top-full left-1/2 -translate-x-1/2 mt-2",left:"right-full top-1/2 -translate-y-1/2 mr-2",right:"left-full top-1/2 -translate-y-1/2 ml-2"};return e.jsxs("div",{className:"relative inline-flex",onMouseEnter:()=>l(!0),onMouseLeave:()=>l(!1),children:[o,r&&e.jsx("div",{className:d("absolute z-50 whitespace-nowrap rounded-md bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md border border-border animate-in fade-in-0",c[n]),children:a})]})}function k(){const a=t(s=>s.sessions),o=t(s=>s.activeLocalId),n=t(s=>s.sidebarOpen),r=t(s=>s.setSidebarOpen),l=t(s=>s.createSession),c=t(s=>s.switchSession),m=t(s=>s.deleteSession);return e.jsxs(e.Fragment,{children:[n&&e.jsx("div",{className:"fixed inset-0 z-40 bg-black/60 md:hidden",onClick:()=>r(!1)}),e.jsxs("aside",{className:d("fixed inset-y-0 left-0 z-50 flex w-60 flex-col border-r border-border bg-secondary transition-transform duration-200 md:relative md:translate-x-0",n?"translate-x-0":"-translate-x-full"),children:[e.jsxs("div",{className:"flex h-14 items-center justify-between border-b border-border px-4",children:[e.jsx("h1",{className:"text-sm font-semibold text-foreground",children:"Archon Claw"}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(w,{content:"New chat",side:"bottom",children:e.jsx(i,{variant:"ghost",size:"icon",className:"h-8 w-8",onClick:l,children:e.jsx(g,{className:"h-4 w-4"})})}),e.jsx(i,{variant:"ghost",size:"icon",className:"h-8 w-8 md:hidden",onClick:()=>r(!1),children:e.jsx(f,{className:"h-4 w-4"})})]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto p-2",children:[a.length===0&&e.jsx("p",{className:"px-2 py-4 text-center text-xs text-muted-foreground",children:"No conversations yet"}),a.map(s=>e.jsxs("div",{className:d("group flex items-center gap-2 rounded-lg px-3 py-2 text-sm cursor-pointer transition-colors",s.localId===o?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:bg-accent"),onClick:()=>c(s.localId),children:[e.jsx(p,{className:"h-4 w-4 shrink-0"}),e.jsx("span",{className:"flex-1 truncate",children:s.title}),e.jsx(i,{variant:"ghost",size:"icon",className:"h-6 w-6 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity",onClick:u=>{u.stopPropagation(),m(s.localId)},children:e.jsx(N,{className:"h-3.5 w-3.5 text-muted-foreground hover:text-destructive"})})]},s.localId))]})]})]})}function S(){const a=t(n=>n.setSidebarOpen),o=t(n=>n.activeSession());return e.jsxs("div",{className:"flex h-full flex-col",children:[e.jsxs("div",{className:"flex h-14 items-center gap-3 border-b border-border px-4",children:[e.jsx(i,{variant:"ghost",size:"icon",className:"md:hidden",onClick:()=>a(!0),children:e.jsx(v,{className:"h-5 w-5"})}),e.jsx("h2",{className:"truncate text-sm font-medium text-foreground",children:(o==null?void 0:o.title)??""})]}),e.jsx(b,{}),e.jsx(j,{})]})}function M(){const a=t(o=>o.loadSessions);return h.useEffect(()=>{a()},[a]),e.jsxs("div",{className:"flex h-dvh overflow-hidden bg-background text-foreground",children:[e.jsx(k,{}),e.jsx("main",{className:"flex-1 overflow-hidden",children:e.jsx(S,{})})]})}y.createRoot(document.getElementById("root")).render(e.jsx(h.StrictMode,{children:e.jsx(M,{})}));