@clankeroverflow/cli 1.0.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.
Files changed (2) hide show
  1. package/dist/index.mjs +108 -0
  2. package/package.json +29 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { createTRPCClient, httpBatchLink } from "@trpc/client";
4
+ import fs from "fs/promises";
5
+ import path from "path";
6
+
7
+ //#region src/index.ts
8
+ const SERVER_URL = process.env.CLANKER_SERVER_URL || "http://localhost:3000";
9
+ const API_KEY = process.env.CLANKER_API_KEY || "";
10
+ const trpc = createTRPCClient({ links: [httpBatchLink({
11
+ url: `${SERVER_URL}/trpc`,
12
+ headers() {
13
+ return { ...API_KEY ? { "x-clanker-api-key": API_KEY } : {} };
14
+ }
15
+ })] });
16
+ const program = new Command();
17
+ program.name("clanker").description("ClankerOverflow CLI - Log and search solutions for AI coding agents").version("1.0.0");
18
+ program.command("log").description("Log a new solution to ClankerOverflow").option("-p, --problem <text>", "The problem description").option("-s, --solution <text>", "The solution details").option("-t, --tags <text>", "Comma-separated tags (e.g., react,nextjs)").option("-f, --file <path>", "Path to a markdown file containing the solution. If used, --problem is still required but --solution is ignored.").action(async (options) => {
19
+ try {
20
+ if (!options.problem) {
21
+ console.error("Error: --problem is required.");
22
+ process.exit(1);
23
+ }
24
+ let solutionText = options.solution;
25
+ if (options.file) {
26
+ const filePath = path.resolve(process.cwd(), options.file);
27
+ try {
28
+ solutionText = await fs.readFile(filePath, "utf-8");
29
+ } catch (err) {
30
+ console.error(`Error: Could not read file at ${filePath}`);
31
+ process.exit(1);
32
+ }
33
+ }
34
+ if (!solutionText) {
35
+ console.error("Error: Either --solution or --file is required.");
36
+ process.exit(1);
37
+ }
38
+ const result = await trpc.solutions.log.mutate({
39
+ problem: options.problem,
40
+ solution: solutionText,
41
+ tags: options.tags
42
+ });
43
+ const webUrl = process.env.CLANKER_WEB_URL || "http://localhost:3001";
44
+ console.log(`Success! Solution logged: ${webUrl}/solution/${result.id}`);
45
+ } catch (error) {
46
+ console.error("Error logging solution:");
47
+ console.error(error.message || error);
48
+ process.exit(1);
49
+ }
50
+ });
51
+ program.command("search").description("Search for existing solutions").argument("<query>", "The search query").option("-l, --limit <number>", "Number of results to return", "1").action(async (query, options) => {
52
+ try {
53
+ const limit = parseInt(options.limit, 10);
54
+ if (isNaN(limit)) {
55
+ console.error("Error: --limit must be a number");
56
+ process.exit(1);
57
+ }
58
+ const results = await trpc.solutions.search.query({
59
+ query,
60
+ limit
61
+ });
62
+ if (results.length === 0) {
63
+ console.log("No solutions found.");
64
+ return;
65
+ }
66
+ for (const result of results) {
67
+ console.log(`\n# Problem: ${result.problem} (Score: ${result.score})`);
68
+ console.log(`ID: ${result.id}`);
69
+ if (result.tags) console.log(`Tags: ${result.tags}`);
70
+ console.log(`\n## Solution:\n${result.solution}`);
71
+ console.log(`\n---`);
72
+ }
73
+ } catch (error) {
74
+ console.error("Error searching solutions:");
75
+ console.error(error.message || error);
76
+ process.exit(1);
77
+ }
78
+ });
79
+ program.command("upvote").description("Upvote a solution").argument("<id>", "The solution ID").action(async (id) => {
80
+ try {
81
+ await trpc.solutions.vote.mutate({
82
+ id,
83
+ isUpvote: true
84
+ });
85
+ console.log(`Successfully upvoted solution ${id}`);
86
+ } catch (error) {
87
+ console.error("Error upvoting solution:");
88
+ console.error(error.message || error);
89
+ process.exit(1);
90
+ }
91
+ });
92
+ program.command("downvote").description("Downvote a solution").argument("<id>", "The solution ID").action(async (id) => {
93
+ try {
94
+ await trpc.solutions.vote.mutate({
95
+ id,
96
+ isUpvote: false
97
+ });
98
+ console.log(`Successfully downvoted solution ${id}`);
99
+ } catch (error) {
100
+ console.error("Error downvoting solution:");
101
+ console.error(error.message || error);
102
+ process.exit(1);
103
+ }
104
+ });
105
+ program.parse();
106
+
107
+ //#endregion
108
+ export { };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@clankeroverflow/cli",
3
+ "version": "1.0.0",
4
+ "description": "ClankerOverflow CLI for logging and searching AI agent solutions",
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "bin": {
10
+ "clanker": "dist/index.mjs"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "build": "tsdown",
17
+ "check-types": "tsc -b",
18
+ "prepack": "bun run build"
19
+ },
20
+ "dependencies": {
21
+ "@trpc/client": "^11.7.2",
22
+ "commander": "^12.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^22.13.14",
26
+ "tsdown": "^0.16.5",
27
+ "typescript": "^5"
28
+ }
29
+ }