cueloop 0.1.0-alpha.7 → 0.1.0-alpha.71

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/package.json CHANGED
@@ -1,37 +1,42 @@
1
1
  {
2
2
  "name": "cueloop",
3
- "version": "0.1.0-alpha.7",
3
+ "version": "0.1.0-alpha.71",
4
+ "description": "Terminal review surface for coding agents: annotate plans and diffs, return a verdict, send structured feedback back",
5
+ "homepage": "https://github.com/mmurakaru/cueloop#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/mmurakaru/cueloop/issues"
8
+ },
4
9
  "license": "Apache-2.0",
5
- "type": "module",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/mmurakaru/cueloop.git",
13
+ "directory": "packages/cli"
14
+ },
6
15
  "bin": {
7
16
  "cueloop": "./src/main.ts"
8
17
  },
18
+ "files": [
19
+ "src",
20
+ "README.md"
21
+ ],
22
+ "type": "module",
9
23
  "exports": {
10
24
  ".": "./src/main.ts"
11
25
  },
12
- "dependencies": {
13
- "@cueloop/schema": "0.1.0-alpha.7",
14
- "@cueloop/daemon": "0.1.0-alpha.7",
15
- "@cueloop/client": "0.1.0-alpha.7"
16
- },
17
26
  "publishConfig": {
18
27
  "access": "public"
19
28
  },
20
- "files": [
21
- "src",
22
- "README.md"
23
- ],
24
- "engines": {
25
- "bun": ">=1.3.0"
29
+ "scripts": {
30
+ "build:binary": "bun build --compile --external cpu-features ./src/main.ts --outfile dist/cueloop"
26
31
  },
27
- "repository": {
28
- "type": "git",
29
- "url": "git+https://github.com/mmurakaru/cueloop.git",
30
- "directory": "packages/cli"
31
- },
32
- "homepage": "https://github.com/mmurakaru/cueloop#readme",
33
- "bugs": {
34
- "url": "https://github.com/mmurakaru/cueloop/issues"
32
+ "dependencies": {
33
+ "@cueloop/adapters": "0.1.0-alpha.71",
34
+ "@cueloop/client": "0.1.0-alpha.71",
35
+ "@cueloop/daemon": "0.1.0-alpha.71",
36
+ "@cueloop/schema": "0.1.0-alpha.71",
37
+ "valibot": "^1.4.2"
35
38
  },
36
- "description": "Terminal review surface for coding agents: annotate plans and diffs, return a verdict, send structured feedback back"
37
- }
39
+ "engines": {
40
+ "bun": ">=1.3.0"
41
+ }
42
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `cueloop actions list [--session <id>]` - print the quick-action vocabulary
3
+ * (the same presets a human picks in the marker popover) so a review-side agent
4
+ * can reference one by name or index via `session annotate --action`. With
5
+ * `--session`, the vocabulary comes from that session's repo (matching what
6
+ * `--action` will resolve); otherwise from the caller's cwd. JSON on stdout.
7
+ */
8
+
9
+ import { loadConfig } from "@cueloop/client/config";
10
+ import { DaemonClient } from "@cueloop/daemon/client";
11
+ import { parseArgs, stringFlag } from "./args";
12
+
13
+ export async function actionsCommand(argv: string[]): Promise<number> {
14
+ const { positional, flags } = parseArgs(argv);
15
+ const primitive = positional[0] ?? "list";
16
+
17
+ if (primitive !== "list") {
18
+ console.error("usage: cueloop actions list [--session <id>]");
19
+
20
+ return 2;
21
+ }
22
+ const role = stringFlag(flags, "role") === "agent" ? "agent" : undefined;
23
+ const repoRoot = await repoRootForSession(stringFlag(flags, "session"), role);
24
+ const actions = loadConfig({ repoRoot }).actions;
25
+ const numbered = actions.map((action, index) => ({
26
+ index: index + 1,
27
+ prompt: action.prompt,
28
+ metadata: action.metadata,
29
+ }));
30
+
31
+ console.log(JSON.stringify(numbered, null, 2));
32
+
33
+ return 0;
34
+ }
35
+
36
+ /** The repo the given session lives in, or the caller's cwd when no session is named. */
37
+ async function repoRootForSession(
38
+ sessionId: string | undefined,
39
+ role: "agent" | undefined,
40
+ ): Promise<string> {
41
+ if (sessionId === undefined) return process.cwd();
42
+ const client = await DaemonClient.connect({ autostart: true, role });
43
+
44
+ try {
45
+ return (await client.sessionGet(sessionId)).workspace.repoRoot;
46
+ } finally {
47
+ client.close();
48
+ }
49
+ }
package/src/args.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  /** Tiny flag parser: --key value, --key=value, boolean --flag, positionals. */
2
2
 
3
+ import * as v from "valibot";
4
+
3
5
  export interface ParsedArgs {
4
6
  positional: string[];
5
7
  flags: Record<string, string | boolean>;
@@ -8,18 +10,22 @@ export interface ParsedArgs {
8
10
  export function parseArgs(argv: string[]): ParsedArgs {
9
11
  const positional: string[] = [];
10
12
  const flags: Record<string, string | boolean> = {};
13
+
11
14
  for (let i = 0; i < argv.length; i++) {
12
- const a = argv[i]!;
13
- if (!a.startsWith("--")) {
14
- positional.push(a);
15
+ const argument = argv[i]!;
16
+
17
+ if (!argument.startsWith("--")) {
18
+ positional.push(argument);
15
19
  continue;
16
20
  }
17
- const eq = a.indexOf("=");
18
- if (eq !== -1) {
19
- flags[a.slice(2, eq)] = a.slice(eq + 1);
21
+ const equalsIndex = argument.indexOf("=");
22
+
23
+ if (equalsIndex !== -1) {
24
+ flags[argument.slice(2, equalsIndex)] = argument.slice(equalsIndex + 1);
20
25
  } else {
21
- const key = a.slice(2);
26
+ const key = argument.slice(2);
22
27
  const next = argv[i + 1];
28
+
23
29
  if (next !== undefined && !next.startsWith("--")) {
24
30
  flags[key] = next;
25
31
  i++;
@@ -28,10 +34,15 @@ export function parseArgs(argv: string[]): ParsedArgs {
28
34
  }
29
35
  }
30
36
  }
37
+
31
38
  return { positional, flags };
32
39
  }
33
40
 
34
- export function flagStr(flags: Record<string, string | boolean>, key: string): string | undefined {
35
- const v = flags[key];
36
- return typeof v === "string" ? v : undefined;
41
+ export function stringFlag(
42
+ flags: Record<string, string | boolean>,
43
+ key: string,
44
+ ): string | undefined {
45
+ const result = v.safeParse(v.string(), flags[key]);
46
+
47
+ return result.success ? result.output : undefined;
37
48
  }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `cueloop dev`: open the TUI against an isolated dev home seeded once with a
3
+ * plan and a diff under this repo's project plus a standalone reply thread, so
4
+ * the Projects and Threads sidebar and the thread view are populated without a
5
+ * live agent. It never touches the real ~/.cueloop home.
6
+ */
7
+
8
+ import { homedir, tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { DaemonClient } from "@cueloop/daemon/client";
11
+ import { resolveWorkspace } from "@cueloop/daemon/review";
12
+
13
+ const SEED_PLAN = `# Read the repository
14
+
15
+ A seeded plan so \`cueloop dev\` always has a thread to open.
16
+
17
+ ## Steps
18
+ - open the thread view
19
+ - move the cursor into the agent's text
20
+ - type to leave a comment
21
+ `;
22
+
23
+ const SEED_REPLY = `A standalone thought, not tied to any repository.
24
+
25
+ It lands under Threads in the sidebar rather than a project.
26
+ `;
27
+
28
+ const SEED_DIFF_PATH = "packages/client/src/theme.ts";
29
+ const SEED_DIFF_OLD = ' accent: "#f5a3a3",\n';
30
+ const SEED_DIFF_NEW = ' accent: "#cba6f7",\n';
31
+ const SEED_DIFF = `diff --git a/${SEED_DIFF_PATH} b/${SEED_DIFF_PATH}
32
+ --- a/${SEED_DIFF_PATH}
33
+ +++ b/${SEED_DIFF_PATH}
34
+ @@ -1 +1 @@
35
+ -${SEED_DIFF_OLD.trimEnd()}
36
+ +${SEED_DIFF_NEW.trimEnd()}
37
+ `;
38
+
39
+ async function seedDevSessions(client: DaemonClient): Promise<void> {
40
+ // the current repo carries a root commit, so its threads gather under a project
41
+ const project = await resolveWorkspace(process.cwd());
42
+ // a non-repo directory has no root commit, so its thread stays standalone
43
+ const standalone = await resolveWorkspace(tmpdir());
44
+
45
+ await client.sessionCreate(project, {
46
+ type: "plan",
47
+ content: SEED_PLAN,
48
+ meta: { title: "Read the repository" },
49
+ });
50
+ await client.sessionCreate(project, {
51
+ type: "diff",
52
+ content: SEED_DIFF,
53
+ meta: { title: "Review the accent change" },
54
+ files: [
55
+ {
56
+ path: SEED_DIFF_PATH,
57
+ oldContents: SEED_DIFF_OLD,
58
+ newContents: SEED_DIFF_NEW,
59
+ status: "modified",
60
+ },
61
+ ],
62
+ });
63
+ await client.sessionCreate(standalone, {
64
+ type: "reply",
65
+ content: SEED_REPLY,
66
+ meta: { title: "A standalone thought" },
67
+ });
68
+ }
69
+
70
+ /** `cueloop dev`: seed an isolated dev home once, then open the TUI on it. */
71
+ export async function devCommand(): Promise<number> {
72
+ process.env.CUELOOP_HOME ??= join(homedir(), ".cueloop-dev");
73
+
74
+ const client = await DaemonClient.connect({ autostart: true });
75
+ try {
76
+ const existing = await client.sessionList();
77
+ if (existing.length === 0) await seedDevSessions(client);
78
+ } finally {
79
+ client.close();
80
+ }
81
+
82
+ const { runClient } = await import("@cueloop/client");
83
+
84
+ return runClient({});
85
+ }